Class List Method List File List

# Class: Concurrent::IVar

Inherits:
  Synchronization::LockableObject
  - Object
  - Synchronization::LockableObject
  - Concurrent::IVar
  show all
Includes:
  Concern::Obligation, Concern::Observable
Defined in:
  lib/concurrent/ivar.rb

## Overview

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 and 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.

## Examples

Create, set and get an `IVar`

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

## See Also

1. For the theory: Arvind, R. Nikhil, and K. Pingali. I-Structures: Data structures for parallel computing. In Proceedings of Workshop on Graph Reduction, 1986.
2. For recent application: DataDrivenFuture in Habanero Java from Rice.

## Direct Known Subclasses

Future, Promise, ScheduledTask

## Instance Method Summary (collapse)

- - (undocumented) add_observer(observer = nil, func = :update, &block)

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

- - (Boolean) complete? included from Concern::Obligation

  Has the obligation completed processing?.

- - (Integer) count_observers included from Concern::Observable

  Return the number of observers associated with this object.

- - (Object) delete_observer(observer) included from Concern::Observable

  Remove `observer` as an observer on this object so that it will no longer receive notifications.

- - (Observable) delete_observers included from Concern::Observable

  Remove all observers associated with this object.

- - (undocumented) exception(*args) included from Concern::Obligation
- - (IVar) fail(reason = StandardError.new)

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

- - (Boolean) fulfilled? (also: #realized?) included from Concern::Obligation

  Has the obligation been fulfilled?.

- - (Boolean) incomplete? included from Concern::Obligation

  Is the obligation still awaiting completion of processing?.

- - (IVar) initialize(value = NULL, opts = {}, &block) constructor

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

- - (Boolean) pending? included from Concern::Obligation

  Is obligation completion still pending?.

- - (Exception) reason included from Concern::Obligation

  If an exception was raised during processing this will return the exception object.

- - (Boolean) rejected? included from Concern::Obligation

  Has the obligation been rejected?.

- - (IVar) set(value = NULL) { ... }

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

- - (Symbol) state included from Concern::Obligation

  The current state of the obligation.

- - (Boolean) try_set(value = NULL) { ... }

  Attempt to set the `IVar` with the given value or block.

- - (Boolean) unscheduled? included from Concern::Obligation

  Is the obligation still unscheduled?.

- - (Object) value(timeout = nil) included from Concern::Obligation

  The current value of the obligation.

- - (Object) value!(timeout = nil) included from Concern::Obligation

  The current value of the obligation.

- - (Obligation) wait(timeout = nil) included from Concern::Obligation

  Wait until obligation is complete or the timeout has been reached.

- - (Obligation) wait!(timeout = nil) (also: #no_error!) included from Concern::Obligation

  Wait until obligation is complete or the timeout is reached.

- - (Observable) with_observer(observer = nil, func = :update, &block) included from Concern::Observable

  As `#add_observer` but can be used for chaining.

## Constructor Details

### - (`IVar`) initialize(value = NULL, opts = {}, &block)

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

Parameters:

- value (`Object`) (defaults to: `NULL`) —

  the initial value

- opts (`Hash`) (defaults to: `{}`) —

  the options to create a message with

Options Hash (`opts`):

- :dup_on_deref (`String`) — default: `false` —

  call `#dup` before returning the data

- :freeze_on_deref (`String`) — default: `false` —

  call `#freeze` before returning the data

- :copy_on_deref (`String`) — default: `nil` —

  call the given `Proc` passing the internal value and returning the value returned from the proc

    61
    62
    63
    64
    65
    66
    67

    # File 'lib/concurrent/ivar.rb', line 61

    def initialize(value = NULL, opts = {}, &block)
      if value != NULL && block_given?
        raise ArgumentError.new('provide only a value or a block')
      end
      super(&nil)
      synchronize { ns_initialize(value, opts, &block) }
    end

## Instance Method Details

### - (`undocumented`) add_observer(observer = nil, func = :update, &block)

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).

Parameters:

- observer (`Object`) (defaults to: `nil`) —

  the object that will be notified of changes

- func (`Symbol`) (defaults to: `:update`) —

  symbol naming the method to call when this `Observable` has changes`

Raises:

- (`ArgumentError`)

    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99

    # File 'lib/concurrent/ivar.rb', line 80

    def add_observer(observer = nil, func = :update, &block)
      raise ArgumentError.new('cannot provide both an observer and a block') if observer && block
      direct_notification = false

      if block
        observer = block
        func = :call
      end

      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

### - (`Boolean`) complete? Originally defined in module Concern::Obligation

Has the obligation completed processing?

Returns:

- (`Boolean`)

### - (`Integer`) count_observers Originally defined in module Concern::Observable

Return the number of observers associated with this object.

Returns:

- (`Integer`) —

  the observers count

### - (`Object`) delete_observer(observer) Originally defined in module Concern::Observable

Remove `observer` as an observer on this object so that it will no longer receive notifications.

Parameters:

- observer (`Object`) —

  the observer to remove

Returns:

- (`Object`) —

  the deleted observer

### - (`Observable`) delete_observers Originally defined in module Concern::Observable

Remove all observers associated with this object.

Returns:

- (`Observable`) —

  self

### - (`undocumented`) exception(*args) Originally defined in module Concern::Obligation

Examples:

allows Obligation to be risen

    rejected_ivar = Ivar.new.fail
    raise rejected_ivar

### - (`IVar`) fail(reason = StandardError.new)

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

Parameters:

- reason (`Object`) (defaults to: `StandardError.new`) —

  for the failure

Returns:

- (`IVar`) —

  self

Raises:

- (`Concurrent::MultipleAssignmentError`) —

  if the `IVar` has already been set or otherwise completed

    134
    135
    136

    # File 'lib/concurrent/ivar.rb', line 134

    def fail(reason = StandardError.new)
      complete(false, nil, reason)
    end

### - (`Boolean`) fulfilled? Also known as: realized? Originally defined in module Concern::Obligation

Has the obligation been fulfilled?

Returns:

- (`Boolean`)

### - (`Boolean`) incomplete? Originally defined in module Concern::Obligation

Is the obligation still awaiting completion of processing?

Returns:

- (`Boolean`)

### - (`Boolean`) pending? Originally defined in module Concern::Obligation

Is obligation completion still pending?

Returns:

- (`Boolean`)

### - (`Exception`) reason Originally defined in module Concern::Obligation

If an exception was raised during processing this will return the exception object. Will return `nil` when the state is pending or if the obligation has been successfully fulfilled.

Returns:

- (`Exception`) —

  the exception raised during processing or `nil`

### - (`Boolean`) rejected? Originally defined in module Concern::Obligation

Has the obligation been rejected?

Returns:

- (`Boolean`)

### - (`IVar`) set(value = NULL) { ... }

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

Parameters:

- value (`Object`) (defaults to: `NULL`) —

  the value to store in the `IVar`

Yields:

- A block operation to use for setting the value

Returns:

- (`IVar`) —

  self

Raises:

- (`ArgumentError`) —

  if both a value and a block are given

- (`Concurrent::MultipleAssignmentError`) —

  if the `IVar` has already been set or otherwise completed

    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125

    # File 'lib/concurrent/ivar.rb', line 112

    def set(value = NULL)
      check_for_block_or_value!(block_given?, value)
      raise MultipleAssignmentError unless compare_and_set_state(:processing, :pending)

      begin
        value = yield if block_given?
        complete_without_notification(true, value, nil)
      rescue => ex
        complete_without_notification(false, nil, ex)
      end

      notify_observers(self.value, reason)
      self
    end

### - (`Symbol`) state Originally defined in module Concern::Obligation

The current state of the obligation.

Returns:

- (`Symbol`) —

  the current state

### - (`Boolean`) try_set(value = NULL) { ... }

Attempt to set the `IVar` with the given value or block. Return a boolean indicating the success or failure of the set operation.

Parameters:

- value (`Object`) (defaults to: `NULL`) —

  the value to store in the `IVar`

Yields:

- A block operation to use for setting the value

Returns:

- (`Boolean`) —

  true if the value was set else false

Raises:

- (`ArgumentError`) —

  if both a value and a block are given

- (`Concurrent::MultipleAssignmentError`) —

  if the `IVar` has already been set or otherwise completed

    144
    145
    146
    147
    148
    149

    # File 'lib/concurrent/ivar.rb', line 144

    def try_set(value = NULL, &block)
      set(value, &block)
      true
    rescue MultipleAssignmentError
      false
    end

### - (`Boolean`) unscheduled? Originally defined in module Concern::Obligation

Is the obligation still unscheduled?

Returns:

- (`Boolean`)

### - (`Object`) value(timeout = nil) Originally defined in module Concern::Obligation

The current value of the obligation. Will be `nil` while the state is pending or the operation has been rejected.

Parameters:

- timeout (`Numeric`) (defaults to: `nil`) —

  the maximum time in seconds to wait.

Returns:

- (`Object`) —

  see Dereferenceable#deref

### - (`Object`) value!(timeout = nil) Originally defined in module Concern::Obligation

The current value of the obligation. Will be `nil` while the state is pending or the operation has been rejected. Will re-raise any exceptions raised during processing (but will not raise an exception on timeout).

Parameters:

- timeout (`Numeric`) (defaults to: `nil`) —

  the maximum time in seconds to wait.

Returns:

- (`Object`) —

  see Dereferenceable#deref

Raises:

- (`Exception`) —

  raises the reason when rejected

### - (`Obligation`) wait(timeout = nil) Originally defined in module Concern::Obligation

Wait until obligation is complete or the timeout has been reached.

Parameters:

- timeout (`Numeric`) (defaults to: `nil`) —

  the maximum time in seconds to wait.

Returns:

- (`Obligation`) —

  self

### - (`Obligation`) wait!(timeout = nil) Also known as: no_error! Originally defined in module Concern::Obligation

Wait until obligation is complete or the timeout is reached. Will re-raise any exceptions raised during processing (but will not raise an exception on timeout).

Parameters:

- timeout (`Numeric`) (defaults to: `nil`) —

  the maximum time in seconds to wait.

Returns:

- (`Obligation`) —

  self

Raises:

- (`Exception`) —

  raises the reason when rejected

### - (`Observable`) with_observer(observer = nil, func = :update, &block) Originally defined in module Concern::Observable

As `#add_observer` but can be used for chaining.

Parameters:

- observer (`Object`) (defaults to: `nil`) —

  the observer to add

- func (`Symbol`) (defaults to: `:update`) —

  the function to call on the observer during notification.

Returns:

- (`Observable`) —

  self