class Garcon::IVar

An ‘IVar` is like a future that you can assign. As a future is a value that is being computed that you can wait on, an `IVar` is a value that is waiting to be assigned, that you can wait on. `IVars` are single assignment and deterministic.

Then, express futures as an asynchronous computation that assigns an ‘IVar`. The `IVar` becomes the primitive on which [futures](Future) and [dataflow](Dataflow) are built.

An ‘IVar` is a single-element container that is normally created empty, and can only be set once. The I in `IVar` stands for immutable. Reading an `IVar` normally blocks until it is set. It is safe to set and read an `IVar` from different threads.

If you want to have some parallel task set the value in an ‘IVar`, you want a `Future`. If you want to create a graph of parallel tasks all executed when the values they depend on are ready you want `dataflow`. `IVar` is generally a low-level primitive.

@example Create, set and get an ‘IVar`

ivar = Garcon::IVar.new
ivar.set 14
ivar.get #=> 14
ivar.set 2 # would now be an error

Constants

NO_VALUE

@!visibility private

Public Class Methods

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

Create a new ‘IVar` in the `:pending` state with the (optional) initial value.

@param [Object] value

The initial value.

@param [Hash] opts the options to create a message with.

@option opts [String] :dup_on_deref (false)

Call `#dup` before returning the data.

@option opts [String] :freeze_on_deref (false)

Call `#freeze` before returning the data.

@option opts [String] :copy_on_deref (nil)

Cll the given `Proc` passing the internal value and returning the value
returned from the proc.
# File lib/garcon/task/ivar.rb, line 76
def initialize(value = NO_VALUE, opts = {})
  init_obligation
  self.observers = CopyOnWriteObserverSet.new
  set_deref_options(opts)

  if value == NO_VALUE
    @state = :pending
  else
    set(value)
  end
end

Public Instance Methods

add_observer(bsrver = nil, func = :update, &block) click to toggle source

Add an observer on this object that will receive notification on update.

Upon completion the ‘IVar` will notify all observers in a thread-safe way. The `func` method of the observer will be called with three arguments: the `Time` at which the `Future` completed the asynchronous operation, the final `value` (or `nil` on rejection), and the final `reason` (or `nil` on fulfillment).

@param [Object] observer

The object that will be notified of changes.

@param [Symbol] func

Symbol naming the method to call when the `Observable` has changes`
# File lib/garcon/task/ivar.rb, line 102
def add_observer(bsrver = nil, func = :update, &block)
  if observer && block
    raise ArgumentError, 'cannot provide both an observer and a block'
  end
  direct_notification = false

  if block
    observer = block
    func     = :call
  end

  mutex.synchronize do
    if event.set?
      direct_notification = true
    else
      observers.add_observer(observer, func)
    end
  end

  observer.send(func, Time.now, self.value, reason) if direct_notification
  observer
end
complete(success, value, reason) click to toggle source

@!visibility private

# File lib/garcon/task/ivar.rb, line 155
def complete(success, value, reason)
  mutex.synchronize do
    if [:fulfilled, :rejected].include? @state
      raise MultipleAssignmentError, 'multiple assignment'
    end
    set_state(success, value, reason)
    event.set
  end

  time = Time.now
  observers.notify_and_delete_observers{ [time, self.value, reason] }
  self
end
fail(reason = StandardError.new) click to toggle source

Set the ‘IVar` to failed due to some error and wake or notify all threads waiting on it.

@param [Object] reason

For the failure.

@raise [Garcon::MultipleAssignmentError]

If the `IVar` has already been set or otherwise completed.

@return [IVar] self

# File lib/garcon/task/ivar.rb, line 150
def fail(reason = StandardError.new)
  complete(false, nil, reason)
end
set(value) click to toggle source

Set the ‘IVar` to a value and wake or notify all threads waiting on it.

@param [Object] value

The value to store in the `IVar`.

@raise [Garcon::MultipleAssignmentError]

If the `IVar` has already been set or otherwise completed.

@return [IVar] self

# File lib/garcon/task/ivar.rb, line 135
def set(value)
  complete(true, value, nil)
end