Class: Garcon::Delay

Inherits:
Object show all
Includes:
ExecutorOptions, Obligation
Defined in:
lib/garcon/task/delay.rb

Overview

Note:

The default behavior of ‘Delay` is to block indefinitely when calling either `value` or `wait`, executing the delayed operation on the current thread. This makes the `timeout` value completely irrelevant. To enable non-blocking behavior, use the `executor` constructor option. This will cause the delayed operation to be execute on the given executor, allowing the call to timeout.

Lazy evaluation of a block yielding an immutable result. Useful for expensive operations that may never be needed. It may be non-blocking, supports the ‘Obligation` interface, and accepts the injection of custom executor upon which to execute the block. Processing of block will be deferred until the first time `#value` is called. At that time the caller can choose to return immediately and let the block execute asynchronously, block indefinitely, or block with a timeout.

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 `Garcon::Dereferenceable` mixin to support thread safety of the reference returned by `#value`.

See Also:

Instance Method Summary collapse

Methods included from Obligation

#complete?, #exception, #fulfilled?, #incomplete?, #pending?, #reason, #rejected?, #state, #unscheduled?, #wait!

Constructor Details

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

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

Yields:

  • the delayed operation to perform

Raises:

  • (ArgumentError)

    if no block is given



68
69
70
71
72
73
74
75
76
# File 'lib/garcon/task/delay.rb', line 68

def initialize(opts = {}, &block)
  raise ArgumentError, 'no block given' unless block_given?
  init_obligation
  set_deref_options(opts)
  @task_executor = get_executor_from(opts)
  @task          = block
  @state         = :pending
  @computing     = false
end

Instance Method Details

#reconfigure { ... } ⇒ true, false

Reconfigures the block returning the value if still ‘#incomplete?`

Yields:

  • the delayed operation to perform

Returns:

  • (true, false)

    if success



158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/garcon/task/delay.rb', line 158

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

#value(timeout = nil) ⇒ Object

Return the value this object represents after applying the options specified by the ‘#set_deref_options` method. If the delayed operation raised an exception this method will return nil. The execption object can be accessed via the `#reason` method.

Parameters:

  • Timeout (Numeric)

    the maximum number of seconds to wait.

Returns:

  • (Object)

    the current value of the object.



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/garcon/task/delay.rb', line 88

def value(timeout = nil)
  if @task_executor
    super
  else
    mutex.synchronize do
      execute = @computing = true unless @computing
      if execute
        begin
          set_state(true, @task.call, nil)
        rescue => e
          set_state(false, nil, e)
        end
      end
    end
    if @do_nothing_on_deref
      @value
    else
      apply_deref_options(@value)
    end
  end
end

#value!(timeout = nil) ⇒ Object

Return the value this object represents after applying the options specified by the ‘#set_deref_options` method. If the delayed operation raised an exception, this method will raise that exception (even when) the operation has already been executed).

Parameters:

  • Timeout (Numeric)

    the maximum number of seconds to wait.

Returns:

  • (Object)

    The current value of the object.

Raises:

  • (Exception)

    when ‘#rejected?` raises `#reason`.



123
124
125
126
127
128
129
130
131
# File 'lib/garcon/task/delay.rb', line 123

def value!(timeout = nil)
  if @task_executor
    super
  else
    result = value
    raise @reason if @reason
    result
  end
end

#wait(timeout = nil) ⇒ Object

Return the value this object represents after applying the options specified by the ‘#set_deref_options` method.

Parameters:

  • Timeout (Integer)

    (nil) the maximum number of seconds to wait for the value to be computed. When ‘nil` the caller will block indefinitely.

Returns:



142
143
144
145
146
147
148
149
150
# File 'lib/garcon/task/delay.rb', line 142

def wait(timeout = nil)
  if @task_executor
    execute_task_once
    super(timeout)
  else
    value
  end
  self
end