Class: Concurrent::Delay

Inherits:
Object
  • Object
show all
Includes:
Obligation
Defined in:
lib/concurrent/delay.rb

Overview

Lazy evaluation of a block yielding an immutable result. Useful for expensive operations that may never be needed.

A ‘Delay` is similar to `Future` but solves a different problem. Where a `Future` schedules an operation for immediate execution and performs the operation asynchronously, a `Delay` (as the name implies) delays execution of the operation until the result is actually needed.

When a ‘Delay` is created its state is set to `pending`. The value and reason are both `nil`. The first time the `#value` method is called the enclosed opration will be run and the calling thread will block. Other threads attempting to call `#value` will block as well. Once the operation is complete the value will be set to the result of the operation or the reason will be set to the raised exception, as appropriate. All threads blocked on `#value` will return. Subsequent calls to `#value` will immediately return the cached value. The operation will only be run once. This means that any side effects created by the operation will only happen once as well.

‘Delay` includes the `Concurrent::Dereferenceable` mixin to support thread safety of the reference returned by `#value`.

Instance Method Summary collapse

Methods included from Obligation

#completed?, #exception, #fulfilled?, #incomplete?, #no_error!, #pending?, #reason, #rejected?, #state, #unscheduled?, #value!, #wait

Constructor Details

#initialize(opts = {}) { ... } ⇒ Delay

Create a new ‘Delay` in the `:pending` state.

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

Yields:

  • the delayed operation to perform

Raises:

  • (ArgumentError)

    if no block is given

Since:

  • 0.6.0



47
48
49
50
51
52
53
54
# File 'lib/concurrent/delay.rb', line 47

def initialize(opts = {}, &block)
  raise ArgumentError.new('no block given') unless block_given?

  init_obligation
  @state = :pending
  @task  = block
  set_deref_options(opts)
end

Instance Method Details

#reconfigure { ... } ⇒ true, false

reconfigures the block returning the value if still #incomplete?

Yields:

  • the delayed operation to perform

Since:

  • 0.6.0



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/concurrent/delay.rb', line 85

def reconfigure(&block)
  mutex.lock
  raise ArgumentError.new('no block given') unless block_given?
  if @state == :pending
    @task = block
    true
  else
    false
  end
ensure
  mutex.unlock
end

#valueObject

Return the (possibly memoized) value of the delayed operation.

If the state is ‘:pending` then the calling thread will block while the operation is performed. All other threads simultaneously calling `#value` will block as well. Once the operation is complete (either `:fulfilled` or `:rejected`) all waiting threads will unblock and the new value will be returned.

If the state is not ‘:pending` when `#value` is called the (possibly memoized) value will be returned without blocking and without performing the operation again.

Regardless of the final disposition all ‘Dereferenceable` options set during object construction will be honored.

See Also:

Since:

  • 0.6.0



74
75
76
77
78
79
80
# File 'lib/concurrent/delay.rb', line 74

def value
  mutex.lock
  execute_task_once
  apply_deref_options(@value)
ensure
  mutex.unlock
end