concurrent-ruby 0.8.0.pre2-java → 0.9.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +114 -3
- data/README.md +111 -55
- data/lib/concurrent.rb +90 -14
- data/lib/concurrent/async.rb +143 -51
- data/lib/concurrent/atom.rb +131 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +27 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +234 -109
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +19 -7
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +71 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +229 -136
- data/lib/concurrent/scheduled_task.rb +341 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +34 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +26 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +92 -103
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +44 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +46 -66
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -48
- data/lib/concurrent/utility/processor_count.rb +0 -152
- data/lib/extension_helper.rb +0 -37
data/lib/concurrent/executors.rb
CHANGED
@@ -2,8 +2,8 @@ require 'concurrent/executor/cached_thread_pool'
|
|
2
2
|
require 'concurrent/executor/fixed_thread_pool'
|
3
3
|
require 'concurrent/executor/immediate_executor'
|
4
4
|
require 'concurrent/executor/indirect_immediate_executor'
|
5
|
-
require 'concurrent/executor/per_thread_executor'
|
6
5
|
require 'concurrent/executor/safe_task_executor'
|
6
|
+
require 'concurrent/executor/simple_executor_service'
|
7
7
|
require 'concurrent/executor/single_thread_executor'
|
8
8
|
require 'concurrent/executor/thread_pool_executor'
|
9
9
|
require 'concurrent/executor/timer_set'
|
data/lib/concurrent/future.rb
CHANGED
@@ -1,13 +1,15 @@
|
|
1
1
|
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/options_parser'
|
2
|
+
require 'concurrent/errors'
|
4
3
|
require 'concurrent/ivar'
|
4
|
+
require 'concurrent/executor/executor'
|
5
5
|
require 'concurrent/executor/safe_task_executor'
|
6
6
|
|
7
7
|
module Concurrent
|
8
8
|
|
9
9
|
# {include:file:doc/future.md}
|
10
10
|
#
|
11
|
+
# @!macro copy_options
|
12
|
+
#
|
11
13
|
# @see http://ruby-doc.org/stdlib-2.1.1/libdoc/observer/rdoc/Observable.html Ruby Observable module
|
12
14
|
# @see http://clojuredocs.org/clojure_core/clojure.core/future Clojure's future function
|
13
15
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html java.util.concurrent.Future
|
@@ -17,24 +19,15 @@ module Concurrent
|
|
17
19
|
#
|
18
20
|
# @yield the asynchronous operation to perform
|
19
21
|
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# @option opts [object] :executor when provided will run all operations on
|
25
|
-
# this executor rather than the global thread pool (overrides :operation)
|
26
|
-
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
27
|
-
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
28
|
-
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
29
|
-
# returning the value returned from the proc
|
22
|
+
# @!macro executor_and_deref_options
|
23
|
+
#
|
24
|
+
# @option opts [object, Array] :args zero or more arguments to be passed the task
|
25
|
+
# block on execution
|
30
26
|
#
|
31
27
|
# @raise [ArgumentError] if no block is given
|
32
28
|
def initialize(opts = {}, &block)
|
33
29
|
raise ArgumentError.new('no block given') unless block_given?
|
34
|
-
super(IVar::NO_VALUE, opts)
|
35
|
-
@state = :unscheduled
|
36
|
-
@task = block
|
37
|
-
@executor = OptionsParser::get_executor_from(opts) || Concurrent.configuration.global_operation_pool
|
30
|
+
super(IVar::NO_VALUE, opts.merge(__task_from_block__: block), &nil)
|
38
31
|
end
|
39
32
|
|
40
33
|
# Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and
|
@@ -52,11 +45,9 @@ module Concurrent
|
|
52
45
|
# @example Instance and execute in one line
|
53
46
|
# future = Concurrent::Future.new{ sleep(1); 42 }.execute
|
54
47
|
# future.state #=> :pending
|
55
|
-
#
|
56
|
-
# @since 0.5.0
|
57
48
|
def execute
|
58
49
|
if compare_and_set_state(:pending, :unscheduled)
|
59
|
-
@executor.post{
|
50
|
+
@executor.post{ safe_execute(@task, @args) }
|
60
51
|
self
|
61
52
|
end
|
62
53
|
end
|
@@ -66,38 +57,80 @@ module Concurrent
|
|
66
57
|
#
|
67
58
|
# @yield the asynchronous operation to perform
|
68
59
|
#
|
69
|
-
#
|
70
|
-
# @option opts [Boolean] :operation (false) when `true` will execute the future on the global
|
71
|
-
# operation pool (for long-running operations), when `false` will execute the future on the
|
72
|
-
# global task pool (for short-running tasks)
|
73
|
-
# @option opts [object] :executor when provided will run all operations on
|
74
|
-
# this executor rather than the global thread pool (overrides :operation)
|
75
|
-
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
76
|
-
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
77
|
-
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
78
|
-
# returning the value returned from the proc
|
60
|
+
# @!macro executor_and_deref_options
|
79
61
|
#
|
80
|
-
# @
|
62
|
+
# @option opts [object, Array] :args zero or more arguments to be passed the task
|
63
|
+
# block on execution
|
81
64
|
#
|
82
65
|
# @raise [ArgumentError] if no block is given
|
83
66
|
#
|
67
|
+
# @return [Future] the newly created `Future` in the `:pending` state
|
68
|
+
#
|
84
69
|
# @example
|
85
70
|
# future = Concurrent::Future.execute{ sleep(1); 42 }
|
86
71
|
# future.state #=> :pending
|
87
|
-
#
|
88
|
-
# @since 0.5.0
|
89
72
|
def self.execute(opts = {}, &block)
|
90
73
|
Future.new(opts, &block).execute
|
91
74
|
end
|
92
75
|
|
93
|
-
|
76
|
+
# @!macro ivar_set_method
|
77
|
+
def set(value = IVar::NO_VALUE, &block)
|
78
|
+
check_for_block_or_value!(block_given?, value)
|
79
|
+
synchronize do
|
80
|
+
if @state != :unscheduled
|
81
|
+
raise MultipleAssignmentError
|
82
|
+
else
|
83
|
+
@task = block || Proc.new { value }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
execute
|
87
|
+
end
|
94
88
|
|
95
|
-
|
89
|
+
# Attempt to cancel the operation if it has not already processed.
|
90
|
+
# The operation can only be cancelled while still `pending`. It cannot
|
91
|
+
# be cancelled once it has begun processing or has completed.
|
92
|
+
#
|
93
|
+
# @return [Boolean] was the operation successfully cancelled.
|
94
|
+
def cancel
|
95
|
+
if compare_and_set_state(:cancelled, :pending)
|
96
|
+
complete(false, nil, CancelledOperationError.new)
|
97
|
+
true
|
98
|
+
else
|
99
|
+
false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Has the operation been successfully cancelled?
|
104
|
+
#
|
105
|
+
# @return [Boolean]
|
106
|
+
def cancelled?
|
107
|
+
state == :cancelled
|
108
|
+
end
|
96
109
|
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
|
110
|
+
# Wait the given number of seconds for the operation to complete.
|
111
|
+
# On timeout attempt to cancel the operation.
|
112
|
+
#
|
113
|
+
# @param [Numeric] timeout the maximum time in seconds to wait.
|
114
|
+
# @return [Boolean] true if the operation completed before the timeout
|
115
|
+
# else false
|
116
|
+
def wait_or_cancel(timeout)
|
117
|
+
wait(timeout)
|
118
|
+
if complete?
|
119
|
+
true
|
120
|
+
else
|
121
|
+
cancel
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
128
|
+
def ns_initialize(value, opts)
|
129
|
+
super
|
130
|
+
@state = :unscheduled
|
131
|
+
@task = opts[:__task_from_block__]
|
132
|
+
@executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
|
133
|
+
@args = get_arguments_from(opts)
|
101
134
|
end
|
102
135
|
end
|
103
136
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'concurrent/synchronization/abstract_struct'
|
2
|
+
require 'concurrent/synchronization'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# A thread-safe, immutable variation of Ruby's standard `Struct`.
|
7
|
+
#
|
8
|
+
# @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
|
9
|
+
module ImmutableStruct
|
10
|
+
include Synchronization::AbstractStruct
|
11
|
+
|
12
|
+
# @!macro struct_values
|
13
|
+
def values
|
14
|
+
ns_values
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :to_a, :values
|
18
|
+
|
19
|
+
# @!macro struct_values_at
|
20
|
+
def values_at(*indexes)
|
21
|
+
ns_values_at(indexes)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!macro struct_inspect
|
25
|
+
def inspect
|
26
|
+
ns_inspect
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :to_s, :inspect
|
30
|
+
|
31
|
+
# @!macro struct_merge
|
32
|
+
def merge(other, &block)
|
33
|
+
ns_merge(other, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @!macro struct_to_h
|
37
|
+
def to_h
|
38
|
+
ns_to_h
|
39
|
+
end
|
40
|
+
|
41
|
+
# @!macro struct_get
|
42
|
+
def [](member)
|
43
|
+
ns_get(member)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @!macro struct_equality
|
47
|
+
def ==(other)
|
48
|
+
ns_equality(other)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!macro struct_each
|
52
|
+
def each(&block)
|
53
|
+
return enum_for(:each) unless block_given?
|
54
|
+
ns_each(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @!macro struct_each_pair
|
58
|
+
def each_pair(&block)
|
59
|
+
return enum_for(:each_pair) unless block_given?
|
60
|
+
ns_each_pair(&block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @!macro struct_select
|
64
|
+
def select(&block)
|
65
|
+
return enum_for(:select) unless block_given?
|
66
|
+
ns_select(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @!macro struct_new
|
70
|
+
def self.new(*args, &block)
|
71
|
+
clazz_name = nil
|
72
|
+
if args.length == 0
|
73
|
+
raise ArgumentError.new('wrong number of arguments (0 for 1+)')
|
74
|
+
elsif args.length > 0 && args.first.is_a?(String)
|
75
|
+
clazz_name = args.shift
|
76
|
+
end
|
77
|
+
FACTORY.define_struct(clazz_name, args, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
FACTORY = Class.new(Synchronization::Object) do
|
81
|
+
def define_struct(name, members, &block)
|
82
|
+
synchronize do
|
83
|
+
Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end.new
|
87
|
+
private_constant :FACTORY
|
88
|
+
end
|
89
|
+
end
|
data/lib/concurrent/ivar.rb
CHANGED
@@ -1,39 +1,53 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
3
|
require 'concurrent/errors'
|
4
|
-
require 'concurrent/
|
5
|
-
require 'concurrent/
|
4
|
+
require 'concurrent/collection/copy_on_write_observer_set'
|
5
|
+
require 'concurrent/concern/obligation'
|
6
|
+
require 'concurrent/concern/observable'
|
7
|
+
require 'concurrent/synchronization'
|
6
8
|
|
7
9
|
module Concurrent
|
8
10
|
|
9
|
-
# An `IVar` is like a future that you can assign. As a future is a value that
|
10
|
-
#
|
11
|
-
#
|
11
|
+
# An `IVar` is like a future that you can assign. As a future is a value that
|
12
|
+
# is being computed that you can wait on, an `IVar` is a value that is waiting
|
13
|
+
# to be assigned, that you can wait on. `IVars` are single assignment and
|
14
|
+
# deterministic.
|
15
|
+
#
|
16
|
+
# Then, express futures as an asynchronous computation that assigns an `IVar`.
|
17
|
+
# The `IVar` becomes the primitive on which [futures](Future) and
|
18
|
+
# [dataflow](Dataflow) are built.
|
12
19
|
#
|
13
20
|
# An `IVar` is a single-element container that is normally created empty, and
|
14
|
-
# can only be set once. The I in `IVar` stands for immutable. Reading an
|
15
|
-
# normally blocks until it is set. It is safe to set and read an `IVar`
|
16
|
-
# different threads.
|
21
|
+
# can only be set once. The I in `IVar` stands for immutable. Reading an
|
22
|
+
# `IVar` normally blocks until it is set. It is safe to set and read an `IVar`
|
23
|
+
# from different threads.
|
17
24
|
#
|
18
25
|
# If you want to have some parallel task set the value in an `IVar`, you want
|
19
|
-
# a `Future`. If you want to create a graph of parallel tasks all executed
|
20
|
-
# the values they depend on are ready you want `dataflow`. `IVar` is
|
21
|
-
# a low-level primitive.
|
22
|
-
#
|
23
|
-
# **See Also:**
|
26
|
+
# a `Future`. If you want to create a graph of parallel tasks all executed
|
27
|
+
# when the values they depend on are ready you want `dataflow`. `IVar` is
|
28
|
+
# generally a low-level primitive.
|
24
29
|
#
|
25
|
-
#
|
26
|
-
# * For recent application: [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html).
|
30
|
+
# ## Examples
|
27
31
|
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
# Create, set and get an `IVar`
|
33
|
+
#
|
34
|
+
# ```ruby
|
35
|
+
# ivar = Concurrent::IVar.new
|
36
|
+
# ivar.set 14
|
37
|
+
# ivar.value #=> 14
|
38
|
+
# ivar.set 2 # would now be an error
|
39
|
+
# ```
|
40
|
+
#
|
41
|
+
# ## See Also
|
42
|
+
#
|
43
|
+
# 1. For the theory: Arvind, R. Nikhil, and K. Pingali.
|
44
|
+
# [I-Structures: Data structures for parallel computing](http://dl.acm.org/citation.cfm?id=69562).
|
45
|
+
# In Proceedings of Workshop on Graph Reduction, 1986.
|
46
|
+
# 2. For recent application:
|
47
|
+
# [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html).
|
48
|
+
class IVar < Synchronization::Object
|
49
|
+
include Concern::Obligation
|
50
|
+
include Concern::Observable
|
37
51
|
|
38
52
|
# @!visibility private
|
39
53
|
NO_VALUE = Object.new # :nodoc:
|
@@ -42,31 +56,31 @@ module Concurrent
|
|
42
56
|
#
|
43
57
|
# @param [Object] value the initial value
|
44
58
|
# @param [Hash] opts the options to create a message with
|
45
|
-
# @option opts [String] :dup_on_deref (false) call `#dup` before returning
|
46
|
-
#
|
47
|
-
# @option opts [String] :
|
48
|
-
# returning the
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
if value == NO_VALUE
|
55
|
-
@state = :pending
|
56
|
-
else
|
57
|
-
set(value)
|
59
|
+
# @option opts [String] :dup_on_deref (false) call `#dup` before returning
|
60
|
+
# the data
|
61
|
+
# @option opts [String] :freeze_on_deref (false) call `#freeze` before
|
62
|
+
# returning the data
|
63
|
+
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing
|
64
|
+
# the internal value and returning the value returned from the proc
|
65
|
+
def initialize(value = NO_VALUE, opts = {}, &block)
|
66
|
+
if value != NO_VALUE && block_given?
|
67
|
+
raise ArgumentError.new('provide only a value or a block')
|
58
68
|
end
|
69
|
+
super(&nil)
|
70
|
+
synchronize { ns_initialize(value, opts, &block) }
|
59
71
|
end
|
60
72
|
|
61
73
|
# Add an observer on this object that will receive notification on update.
|
62
74
|
#
|
63
|
-
# Upon completion the `IVar` will notify all observers in a thread-
|
64
|
-
# method of the observer will be called with three arguments: the
|
65
|
-
# `Future` completed the asynchronous operation, the
|
66
|
-
# and the final `reason` (or `nil` on
|
75
|
+
# Upon completion the `IVar` will notify all observers in a thread-safe way.
|
76
|
+
# The `func` method of the observer will be called with three arguments: the
|
77
|
+
# `Time` at which the `Future` completed the asynchronous operation, the
|
78
|
+
# final `value` (or `nil` on rejection), and the final `reason` (or `nil` on
|
79
|
+
# fulfillment).
|
67
80
|
#
|
68
81
|
# @param [Object] observer the object that will be notified of changes
|
69
|
-
# @param [Symbol] func symbol naming the method to call when this
|
82
|
+
# @param [Symbol] func symbol naming the method to call when this
|
83
|
+
# `Observable` has changes`
|
70
84
|
def add_observer(observer = nil, func = :update, &block)
|
71
85
|
raise ArgumentError.new('cannot provide both an observer and a block') if observer && block
|
72
86
|
direct_notification = false
|
@@ -76,7 +90,7 @@ module Concurrent
|
|
76
90
|
func = :call
|
77
91
|
end
|
78
92
|
|
79
|
-
|
93
|
+
synchronize do
|
80
94
|
if event.set?
|
81
95
|
direct_notification = true
|
82
96
|
else
|
@@ -88,33 +102,111 @@ module Concurrent
|
|
88
102
|
observer
|
89
103
|
end
|
90
104
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
|
96
|
-
|
105
|
+
# @!macro [attach] ivar_set_method
|
106
|
+
# Set the `IVar` to a value and wake or notify all threads waiting on it.
|
107
|
+
#
|
108
|
+
# @!macro [attach] ivar_set_parameters_and_exceptions
|
109
|
+
# @param [Object] value the value to store in the `IVar`
|
110
|
+
# @yield A block operation to use for setting the value
|
111
|
+
# @raise [ArgumentError] if both a value and a block are given
|
112
|
+
# @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already
|
113
|
+
# been set or otherwise completed
|
114
|
+
#
|
115
|
+
# @return [IVar] self
|
116
|
+
def set(value = NO_VALUE)
|
117
|
+
check_for_block_or_value!(block_given?, value)
|
118
|
+
raise MultipleAssignmentError unless compare_and_set_state(:processing, :pending)
|
119
|
+
|
120
|
+
begin
|
121
|
+
value = yield if block_given?
|
122
|
+
complete_without_notification(true, value, nil)
|
123
|
+
rescue => ex
|
124
|
+
complete_without_notification(false, nil, ex)
|
125
|
+
end
|
126
|
+
|
127
|
+
notify_observers(self.value, reason)
|
128
|
+
self
|
97
129
|
end
|
98
130
|
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
131
|
+
# @!macro [attach] ivar_fail_method
|
132
|
+
# Set the `IVar` to failed due to some error and wake or notify all threads waiting on it.
|
133
|
+
#
|
134
|
+
# @param [Object] reason for the failure
|
135
|
+
# @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already
|
136
|
+
# been set or otherwise completed
|
137
|
+
# @return [IVar] self
|
103
138
|
def fail(reason = StandardError.new)
|
104
139
|
complete(false, nil, reason)
|
105
140
|
end
|
106
141
|
|
142
|
+
# Attempt to set the `IVar` with the given value or block. Return a
|
143
|
+
# boolean indicating the success or failure of the set operation.
|
144
|
+
#
|
145
|
+
# @!macro ivar_set_parameters_and_exceptions
|
146
|
+
#
|
147
|
+
# @return [Boolean] true if the value was set else false
|
148
|
+
def try_set(value = NO_VALUE, &block)
|
149
|
+
set(value, &block)
|
150
|
+
true
|
151
|
+
rescue MultipleAssignmentError
|
152
|
+
false
|
153
|
+
end
|
154
|
+
|
155
|
+
protected
|
156
|
+
|
157
|
+
# @!visibility private
|
158
|
+
def ns_initialize(value, opts)
|
159
|
+
value = yield if block_given?
|
160
|
+
init_obligation(self)
|
161
|
+
self.observers = Collection::CopyOnWriteObserverSet.new
|
162
|
+
set_deref_options(opts)
|
163
|
+
|
164
|
+
if value == NO_VALUE
|
165
|
+
@state = :pending
|
166
|
+
else
|
167
|
+
ns_complete_without_notification(true, value, nil)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
107
171
|
# @!visibility private
|
108
|
-
def
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
172
|
+
def safe_execute(task, args = [])
|
173
|
+
if compare_and_set_state(:processing, :pending)
|
174
|
+
success, val, reason = SafeTaskExecutor.new(task, rescue_exception: true).execute(*@args)
|
175
|
+
complete(success, val, reason)
|
176
|
+
yield(success, val, reason) if block_given?
|
113
177
|
end
|
178
|
+
end
|
114
179
|
|
115
|
-
|
116
|
-
|
180
|
+
# @!visibility private
|
181
|
+
def complete(success, value, reason)
|
182
|
+
complete_without_notification(success, value, reason)
|
183
|
+
notify_observers(self.value, reason)
|
117
184
|
self
|
118
185
|
end
|
186
|
+
|
187
|
+
# @!visibility private
|
188
|
+
def complete_without_notification(success, value, reason)
|
189
|
+
synchronize { ns_complete_without_notification(success, value, reason) }
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
# @!visibility private
|
194
|
+
def notify_observers(value, reason)
|
195
|
+
observers.notify_and_delete_observers{ [Time.now, value, reason] }
|
196
|
+
end
|
197
|
+
|
198
|
+
# @!visibility private
|
199
|
+
def ns_complete_without_notification(success, value, reason)
|
200
|
+
raise MultipleAssignmentError if [:fulfilled, :rejected].include? @state
|
201
|
+
set_state(success, value, reason)
|
202
|
+
event.set
|
203
|
+
end
|
204
|
+
|
205
|
+
# @!visibility private
|
206
|
+
def check_for_block_or_value!(block_given, value) # :nodoc:
|
207
|
+
if (block_given && value != NO_VALUE) || (! block_given && value == NO_VALUE)
|
208
|
+
raise ArgumentError.new('must set with either a value or a block')
|
209
|
+
end
|
210
|
+
end
|
119
211
|
end
|
120
212
|
end
|