class Concurrent::MVar

(PoPL), 1996.
In Proceedings of the 23rd Symposium on Principles of Programming Languages
2. S. Peyton Jones, A. Gordon, and S. Finne. [Concurrent Haskell](dl.acm.org/citation.cfm?id=237794).
ACM Conference on Functional Programming Languages and Computer Architecture (FPCA), 1991.
1. P. 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
## See Also
@!macro copy_options
Haskell and Scala do it today.
Note that unlike the original Haskell paper, our ‘#take` is blocking. This is how
`MVar` is related to M-structures in Id, `MVar` in Haskell and `SyncVar` in Scala.
`MVar` is a [Dereferenceable](Dereferenceable).
You shouldn’t use these operations in the first instance.
‘MVar::EMPTY` if the `MVar` is empty and can be used to set `MVar::EMPTY`.
without removing it or returns `MVar::EMPTY`, and a `#modify!` that yields
`#set!` that ignores existing values, a `#value` that returns the value
We also support non-blocking operations `#try_put!` and `#try_take!`, a
These operations all support timeouts.
`#mutate` that is atomic with respect to operations on the same instance.
On top of the fundamental `#put` and `#take` operations, we also provide a
queue of length one, or a special kind of mutable variable.
putting a value into a full one. You can either think of them as blocking
contain one item. Taking a value from an empty `MVar` blocks, as does
An `MVar` is a synchronized single element container. They are empty or

def borrow(timeout = nil)

Returns:
  • (Object) - the value returned by the block, or `TIMEOUT`
def borrow(timeout = nil)
  @mutex.synchronize do
    wait_for_full(timeout)
    # if we timeoud out we'll still be empty
    if unlocked_full?
      yield @value
    else
      TIMEOUT
    end
  end
end

def empty?

Returns if the `MVar` is currently empty.
def empty?
  @mutex.synchronize { @value == EMPTY }
end

def full?

Returns if the `MVar` currently contains a value.
def full?
  !empty?
end

def initialize(value = EMPTY, opts = {})

Parameters:
  • opts (Hash) -- the options controlling how the future will be processed
def initialize(value = EMPTY, opts = {})
  @value = value
  @mutex = Mutex.new
  @empty_condition = ConditionVariable.new
  @full_condition = ConditionVariable.new
  set_deref_options(opts)
end

def modify(timeout = nil)

Returns:
  • (Object) - the transformed value, or `TIMEOUT`
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

def modify!

Non-blocking version of `modify` that will yield with `EMPTY` if there is no value yet.
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

def put(value, timeout = nil)

Returns:
  • (Object) - the value that was put, or `TIMEOUT`
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

def set!(value)

Non-blocking version of `put` that will overwrite an existing value.
def set!(value)
  @mutex.synchronize do
    old_value = @value
    @value = value
    @full_condition.signal
    apply_deref_options(old_value)
  end
end

def synchronize(&block)

def synchronize(&block)
  @mutex.synchronize(&block)
end

def take(timeout = nil)

Returns:
  • (Object) - the value that was taken, or `TIMEOUT`
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

def try_put!(value)

Non-blocking version of `put`, that returns whether or not it was successful.
def try_put!(value)
  @mutex.synchronize do
    if unlocked_empty?
      @value = value
      @full_condition.signal
      true
    else
      false
    end
  end
end

def try_take!

Non-blocking version of `take`, that returns `EMPTY` instead of blocking.
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

def unlocked_empty?

def unlocked_empty?
  @value == EMPTY
end

def unlocked_full?

def unlocked_full?
  ! unlocked_empty?
end

def wait_for_empty(timeout)

def wait_for_empty(timeout)
  wait_while(@empty_condition, timeout) { unlocked_full? }
end

def wait_for_full(timeout)

def wait_for_full(timeout)
  wait_while(@full_condition, timeout) { unlocked_empty? }
end

def wait_while(condition, timeout)

def wait_while(condition, timeout)
  if timeout.nil?
    while yield
      condition.wait(@mutex)
    end
  else
    stop = Concurrent.monotonic_time + timeout
    while yield && timeout > 0.0
      condition.wait(@mutex, timeout)
      timeout = stop - Concurrent.monotonic_time
    end
  end
end