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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +114 -3
  3. data/README.md +111 -55
  4. data/lib/concurrent.rb +90 -14
  5. data/lib/concurrent/async.rb +143 -51
  6. data/lib/concurrent/atom.rb +131 -0
  7. data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
  8. data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
  9. data/lib/concurrent/atomic/atomic_reference.rb +49 -0
  10. data/lib/concurrent/atomic/condition.rb +23 -12
  11. data/lib/concurrent/atomic/count_down_latch.rb +23 -21
  12. data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
  13. data/lib/concurrent/atomic/event.rb +33 -42
  14. data/lib/concurrent/atomic/read_write_lock.rb +252 -0
  15. data/lib/concurrent/atomic/semaphore.rb +64 -89
  16. data/lib/concurrent/atomic/thread_local_var.rb +130 -58
  17. data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
  18. data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
  19. data/lib/concurrent/atomic_reference/jruby.rb +6 -3
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
  21. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
  22. data/lib/concurrent/atomic_reference/rbx.rb +4 -1
  23. data/lib/concurrent/atomic_reference/ruby.rb +6 -3
  24. data/lib/concurrent/atomics.rb +74 -4
  25. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
  26. data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
  27. data/lib/concurrent/collection/priority_queue.rb +300 -245
  28. data/lib/concurrent/concern/deprecation.rb +34 -0
  29. data/lib/concurrent/concern/dereferenceable.rb +88 -0
  30. data/lib/concurrent/concern/logging.rb +27 -0
  31. data/lib/concurrent/concern/obligation.rb +228 -0
  32. data/lib/concurrent/concern/observable.rb +85 -0
  33. data/lib/concurrent/configuration.rb +234 -109
  34. data/lib/concurrent/dataflow.rb +2 -3
  35. data/lib/concurrent/delay.rb +141 -50
  36. data/lib/concurrent/edge.rb +30 -0
  37. data/lib/concurrent/errors.rb +19 -7
  38. data/lib/concurrent/exchanger.rb +25 -1
  39. data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
  40. data/lib/concurrent/executor/executor.rb +46 -299
  41. data/lib/concurrent/executor/executor_service.rb +521 -0
  42. data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
  43. data/lib/concurrent/executor/immediate_executor.rb +9 -9
  44. data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
  45. data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
  46. data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
  47. data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
  48. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
  49. data/lib/concurrent/executor/safe_task_executor.rb +5 -4
  50. data/lib/concurrent/executor/serialized_execution.rb +22 -18
  51. data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
  52. data/lib/concurrent/executor/single_thread_executor.rb +32 -21
  53. data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
  54. data/lib/concurrent/executor/timer_set.rb +96 -84
  55. data/lib/concurrent/executors.rb +1 -1
  56. data/lib/concurrent/future.rb +71 -38
  57. data/lib/concurrent/immutable_struct.rb +89 -0
  58. data/lib/concurrent/ivar.rb +152 -60
  59. data/lib/concurrent/lazy_register.rb +40 -20
  60. data/lib/concurrent/maybe.rb +226 -0
  61. data/lib/concurrent/mutable_struct.rb +227 -0
  62. data/lib/concurrent/mvar.rb +44 -43
  63. data/lib/concurrent/promise.rb +229 -136
  64. data/lib/concurrent/scheduled_task.rb +341 -43
  65. data/lib/concurrent/settable_struct.rb +127 -0
  66. data/lib/concurrent/synchronization.rb +17 -0
  67. data/lib/concurrent/synchronization/abstract_object.rb +163 -0
  68. data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
  69. data/lib/concurrent/synchronization/condition.rb +53 -0
  70. data/lib/concurrent/synchronization/java_object.rb +34 -0
  71. data/lib/concurrent/synchronization/lock.rb +32 -0
  72. data/lib/concurrent/synchronization/monitor_object.rb +26 -0
  73. data/lib/concurrent/synchronization/mutex_object.rb +43 -0
  74. data/lib/concurrent/synchronization/object.rb +78 -0
  75. data/lib/concurrent/synchronization/rbx_object.rb +75 -0
  76. data/lib/concurrent/timer_task.rb +92 -103
  77. data/lib/concurrent/tvar.rb +42 -38
  78. data/lib/concurrent/utilities.rb +3 -1
  79. data/lib/concurrent/utility/at_exit.rb +97 -0
  80. data/lib/concurrent/utility/engine.rb +44 -0
  81. data/lib/concurrent/utility/monotonic_time.rb +59 -0
  82. data/lib/concurrent/utility/native_extension_loader.rb +56 -0
  83. data/lib/concurrent/utility/processor_counter.rb +156 -0
  84. data/lib/concurrent/utility/timeout.rb +18 -14
  85. data/lib/concurrent/utility/timer.rb +11 -6
  86. data/lib/concurrent/version.rb +2 -1
  87. data/lib/concurrent_ruby.rb +1 -0
  88. data/lib/concurrent_ruby_ext.jar +0 -0
  89. metadata +46 -66
  90. data/lib/concurrent/actor.rb +0 -103
  91. data/lib/concurrent/actor/behaviour.rb +0 -70
  92. data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
  93. data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
  94. data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
  95. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
  96. data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
  97. data/lib/concurrent/actor/behaviour/linking.rb +0 -45
  98. data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
  99. data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
  100. data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
  101. data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
  102. data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
  103. data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
  104. data/lib/concurrent/actor/behaviour/termination.rb +0 -54
  105. data/lib/concurrent/actor/context.rb +0 -154
  106. data/lib/concurrent/actor/core.rb +0 -217
  107. data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
  108. data/lib/concurrent/actor/envelope.rb +0 -41
  109. data/lib/concurrent/actor/errors.rb +0 -27
  110. data/lib/concurrent/actor/internal_delegations.rb +0 -49
  111. data/lib/concurrent/actor/public_delegations.rb +0 -40
  112. data/lib/concurrent/actor/reference.rb +0 -81
  113. data/lib/concurrent/actor/root.rb +0 -37
  114. data/lib/concurrent/actor/type_check.rb +0 -48
  115. data/lib/concurrent/actor/utils.rb +0 -10
  116. data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
  117. data/lib/concurrent/actor/utils/balancer.rb +0 -42
  118. data/lib/concurrent/actor/utils/broadcast.rb +0 -52
  119. data/lib/concurrent/actor/utils/pool.rb +0 -59
  120. data/lib/concurrent/actress.rb +0 -3
  121. data/lib/concurrent/agent.rb +0 -209
  122. data/lib/concurrent/atomic.rb +0 -92
  123. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
  124. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
  125. data/lib/concurrent/atomic/synchronization.rb +0 -51
  126. data/lib/concurrent/channel/buffered_channel.rb +0 -85
  127. data/lib/concurrent/channel/channel.rb +0 -41
  128. data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
  129. data/lib/concurrent/channel/waitable_list.rb +0 -40
  130. data/lib/concurrent/channels.rb +0 -5
  131. data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
  132. data/lib/concurrent/collection/ring_buffer.rb +0 -59
  133. data/lib/concurrent/collections.rb +0 -3
  134. data/lib/concurrent/dereferenceable.rb +0 -108
  135. data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
  136. data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
  137. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
  138. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
  139. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
  140. data/lib/concurrent/logging.rb +0 -20
  141. data/lib/concurrent/obligation.rb +0 -171
  142. data/lib/concurrent/observable.rb +0 -73
  143. data/lib/concurrent/options_parser.rb +0 -48
  144. data/lib/concurrent/utility/processor_count.rb +0 -152
  145. data/lib/extension_helper.rb +0 -37
@@ -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'
@@ -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
- # @param [Hash] opts the options controlling how the future will be processed
21
- # @option opts [Boolean] :operation (false) when `true` will execute the future on the global
22
- # operation pool (for long-running operations), when `false` will execute the future on the
23
- # global task pool (for short-running tasks)
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{ work }
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
- # @param [Hash] opts the options controlling how the future will be processed
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
- # @return [Future] the newly created `Future` in the `:pending` state
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
- protected :set, :fail, :complete
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
- private
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
- # @!visibility private
98
- def work # :nodoc:
99
- success, val, reason = SafeTaskExecutor.new(@task).execute
100
- complete(success, val, reason)
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
@@ -1,39 +1,53 @@
1
1
  require 'thread'
2
2
 
3
3
  require 'concurrent/errors'
4
- require 'concurrent/obligation'
5
- require 'concurrent/observable'
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 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.
10
- #
11
- # 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.
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 `IVar`
15
- # normally blocks until it is set. It is safe to set and read an `IVar` from
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 when
20
- # the values they depend on are ready you want `dataflow`. `IVar` is generally
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
- # * For the theory: Arvind, R. Nikhil, and K. Pingali. [I-Structures: Data structures for parallel computing](http://dl.acm.org/citation.cfm?id=69562). In Proceedings of Workshop on Graph Reduction, 1986.
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
- # @example Create, set and get an `IVar`
29
- # ivar = Concurrent::IVar.new
30
- # ivar.set 14
31
- # ivar.get #=> 14
32
- # ivar.set 2 # would now be an error
33
- class IVar
34
-
35
- include Obligation
36
- include Observable
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 the data
46
- # @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
47
- # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
48
- # returning the value returned from the proc
49
- def initialize(value = NO_VALUE, opts = {})
50
- init_obligation
51
- self.observers = CopyOnWriteObserverSet.new
52
- set_deref_options(opts)
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-say way. The `func`
64
- # method of the observer will be called with three arguments: the `Time` at which the
65
- # `Future` completed the asynchronous operation, the final `value` (or `nil` on rejection),
66
- # and the final `reason` (or `nil` on fulfillment).
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 `Observable` has changes`
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
- mutex.synchronize do
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
- # Set the `IVar` to a value and wake or notify all threads waiting on it.
92
- #
93
- # @param [Object] value the value to store in the `IVar`
94
- # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already been set or otherwise completed
95
- def set(value)
96
- complete(true, value, nil)
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
- # Set the `IVar` to failed due to some error and wake or notify all threads waiting on it.
100
- #
101
- # @param [Object] reason for the failure
102
- # @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already been set or otherwise completed
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 complete(success, value, reason) # :nodoc:
109
- mutex.synchronize do
110
- raise MultipleAssignmentError.new('multiple assignment') if [:fulfilled, :rejected].include? @state
111
- set_state(success, value, reason)
112
- event.set
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
- time = Time.now
116
- observers.notify_and_delete_observers{ [time, self.value, reason] }
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