Parent

Files

Class/Module Index [+]

Quicksearch

Concurrent::MVar

An `MVar` is a synchronized single element container. They are empty or contain one item. Taking a value from an empty `MVar` blocks, as does putting a value into a full one. You can either think of them as blocking queue of length one, or a special kind of mutable variable.

On top of the fundamental `put` and `take` operations, we also provide a `mutate` that is atomic with respect to operations on the same instance. These operations all support timeouts.

We also support non-blocking operations `try_put!` and `try_take!`, a `set!` that ignores existing values, a `value` that returns the value without removing it or returns `MVar::EMPTY`, and a `modify!` that yields `MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`. You shouldn't use these operations in the first instance.

`MVar` is a [Dereferenceable](Dereferenceable).

`MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala.

Note that unlike the original Haskell paper, our `take` is blocking. This is how Haskell and Scala do it today.

@!macro copy_options

## See Also

    1. Barth, R. Nikhil, and Arvind. [M-Structures: Extending a parallel, non- strict, functional language with state](dl.acm.org/citation.cfm?id=652538). In Proceedings of the 5th

    ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991.

    1. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](dl.acm.org/citation.cfm?id=237794).

    In Proceedings of the 23rd Symposium on Principles of Programming Languages (PoPL), 1996.

Constants

EMPTY

Unique value that represents that an `MVar` was empty

TIMEOUT

Unique value that represents that an `MVar` timed out before it was able to produce a value.

Public Class Methods

new(value = EMPTY, opts = {}) click to toggle source

Create a new `MVar`, either empty or with an initial value.

@param [Hash] opts the options controlling how the future will be processed

@!macro deref_options

# File lib/concurrent/mvar.rb, line 53
def initialize(value = EMPTY, opts = {})
  @value = value
  @mutex = Mutex.new
  @empty_condition = ConditionVariable.new
  @full_condition = ConditionVariable.new
  set_deref_options(opts)
end

Public Instance Methods

empty?() click to toggle source

Returns if the `MVar` is currently empty.

# File lib/concurrent/mvar.rb, line 177
def empty?
  @mutex.synchronize { @value == EMPTY }
end
full?() click to toggle source

Returns if the `MVar` currently contains a value.

# File lib/concurrent/mvar.rb, line 182
def full?
  !empty?
end
modify(timeout = nil) click to toggle source

Atomically `take`, yield the value to a block for transformation, and then `put` the transformed value. Returns the transformed value. A timeout can be set to limit the time spent blocked, in which case it returns `TIMEOUT` if the time is exceeded. @return [Object] the transformed value, or `TIMEOUT`

# File lib/concurrent/mvar.rb, line 105
def modify(timeout = nil)
  raise ArgumentError.new('no block given') unless block_given?

  @mutex.synchronize do
    wait_for_full(timeout)

    # If we timed out we'll still be empty
    if unlocked_full?
      value = @value
      @value = yield value
      @full_condition.signal
      apply_deref_options(value)
    else
      TIMEOUT
    end
  end
end
modify!() click to toggle source

Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet.

# File lib/concurrent/mvar.rb, line 161
def modify!
  raise ArgumentError.new('no block given') unless block_given?

  @mutex.synchronize do
    value = @value
    @value = yield value
    if unlocked_empty?
      @empty_condition.signal
    else
      @full_condition.signal
    end
    apply_deref_options(value)
  end
end
put(value, timeout = nil) click to toggle source

Put a value into an `MVar`, blocking if there is already a value until it is empty. A timeout can be set to limit the time spent blocked, in which case it returns `TIMEOUT` if the time is exceeded. @return [Object] the value that was put, or `TIMEOUT`

# File lib/concurrent/mvar.rb, line 85
def put(value, timeout = nil)
  @mutex.synchronize do
    wait_for_empty(timeout)

    # If we timed out we won't be empty
    if unlocked_empty?
      @value = value
      @full_condition.signal
      apply_deref_options(value)
    else
      TIMEOUT
    end
  end
end
set!(value) click to toggle source

Non-blocking version of `put` that will overwrite an existing value.

# File lib/concurrent/mvar.rb, line 151
def set!(value)
  @mutex.synchronize do
    old_value = @value
    @value = value
    @full_condition.signal
    apply_deref_options(old_value)
  end
end
take(timeout = nil) click to toggle source

Remove the value from an `MVar`, leaving it empty, and blocking if there isn't a value. A timeout can be set to limit the time spent blocked, in which case it returns `TIMEOUT` if the time is exceeded. @return [Object] the value that was taken, or `TIMEOUT`

# File lib/concurrent/mvar.rb, line 65
def take(timeout = nil)
  @mutex.synchronize do
    wait_for_full(timeout)

    # If we timed out we'll still be empty
    if unlocked_full?
      value = @value
      @value = EMPTY
      @empty_condition.signal
      apply_deref_options(value)
    else
      TIMEOUT
    end
  end
end
try_put!(value) click to toggle source

Non-blocking version of `put`, that returns whether or not it was successful.

# File lib/concurrent/mvar.rb, line 138
def try_put!(value)
  @mutex.synchronize do
    if unlocked_empty?
      @value = value
      @full_condition.signal
      true
    else
      false
    end
  end
end
try_take!() click to toggle source

Non-blocking version of `take`, that returns `EMPTY` instead of blocking.

# File lib/concurrent/mvar.rb, line 124
def try_take!
  @mutex.synchronize do
    if unlocked_full?
      value = @value
      @value = EMPTY
      @empty_condition.signal
      apply_deref_options(value)
    else
      EMPTY
    end
  end
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.