concurrent-ruby 1.0.0.pre1-java → 1.0.0.pre2-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +16 -18
  4. data/lib/concurrent.rb +3 -3
  5. data/lib/concurrent/agent.rb +583 -0
  6. data/lib/concurrent/array.rb +1 -0
  7. data/lib/concurrent/async.rb +236 -111
  8. data/lib/concurrent/atom.rb +101 -46
  9. data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
  10. data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
  11. data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
  12. data/lib/concurrent/atomic/event.rb +1 -1
  13. data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
  14. data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
  15. data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  16. data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
  17. data/lib/concurrent/atomic/read_write_lock.rb +5 -4
  18. data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
  19. data/lib/concurrent/atomic/thread_local_var.rb +2 -0
  20. data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
  21. data/lib/concurrent/atomics.rb +6 -4
  22. data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
  23. data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
  24. data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
  25. data/lib/concurrent/concern/observable.rb +38 -13
  26. data/lib/concurrent/configuration.rb +5 -4
  27. data/lib/concurrent/delay.rb +9 -8
  28. data/lib/concurrent/exchanger.rb +2 -0
  29. data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
  30. data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
  31. data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
  32. data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
  33. data/lib/concurrent/executor/safe_task_executor.rb +7 -8
  34. data/lib/concurrent/executor/serialized_execution.rb +4 -4
  35. data/lib/concurrent/executor/single_thread_executor.rb +20 -10
  36. data/lib/concurrent/executor/timer_set.rb +4 -2
  37. data/lib/concurrent/executors.rb +0 -1
  38. data/lib/concurrent/future.rb +3 -2
  39. data/lib/concurrent/hash.rb +1 -1
  40. data/lib/concurrent/immutable_struct.rb +5 -1
  41. data/lib/concurrent/ivar.rb +1 -1
  42. data/lib/concurrent/mutable_struct.rb +7 -6
  43. data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
  44. data/lib/concurrent/promise.rb +3 -2
  45. data/lib/concurrent/scheduled_task.rb +3 -2
  46. data/lib/concurrent/settable_struct.rb +5 -4
  47. data/lib/concurrent/synchronization.rb +11 -3
  48. data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
  49. data/lib/concurrent/synchronization/abstract_object.rb +16 -129
  50. data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
  51. data/lib/concurrent/synchronization/condition.rb +6 -4
  52. data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  53. data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
  54. data/lib/concurrent/synchronization/lock.rb +3 -2
  55. data/lib/concurrent/synchronization/lockable_object.rb +59 -0
  56. data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
  57. data/lib/concurrent/synchronization/mri_object.rb +35 -0
  58. data/lib/concurrent/synchronization/object.rb +111 -39
  59. data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
  60. data/lib/concurrent/synchronization/rbx_object.rb +17 -68
  61. data/lib/concurrent/thread_safe/util.rb +0 -9
  62. data/lib/concurrent/thread_safe/util/adder.rb +3 -0
  63. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
  64. data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
  65. data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
  66. data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
  67. data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
  68. data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
  69. data/lib/concurrent/tvar.rb +36 -0
  70. data/lib/concurrent/utility/at_exit.rb +1 -1
  71. data/lib/concurrent/utility/monotonic_time.rb +3 -4
  72. data/lib/concurrent/utility/native_extension_loader.rb +1 -1
  73. data/lib/concurrent/version.rb +2 -2
  74. data/lib/concurrent_ruby_ext.jar +0 -0
  75. metadata +11 -6
  76. data/lib/concurrent/synchronization/monitor_object.rb +0 -27
  77. data/lib/concurrent/synchronization/mutex_object.rb +0 -43
@@ -16,15 +16,22 @@ module Concurrent
16
16
 
17
17
  # @!macro [attach] single_thread_executor
18
18
  #
19
- # A thread pool with a set number of threads. The number of threads in the pool
20
- # is set on construction and remains constant. When all threads are busy new
21
- # tasks `#post` to the thread pool are enqueued until a thread becomes available.
22
- # Should a thread crash for any reason the thread will immediately be removed
23
- # from the pool and replaced.
19
+ # A thread pool with a single thread an unlimited queue. Should the thread
20
+ # die for any reason it will be removed and replaced, thus ensuring that
21
+ # the executor will always remain viable and available to process jobs.
24
22
  #
25
- # The API and behavior of this class are based on Java's `SingleThreadExecutor`
23
+ # A common pattern for background processing is to create a single thread
24
+ # on which an infinite loop is run. The thread's loop blocks on an input
25
+ # source (perhaps blocking I/O or a queue) and processes each input as it
26
+ # is received. This pattern has several issues. The thread itself is highly
27
+ # susceptible to errors during processing. Also, the thread itself must be
28
+ # constantly monitored and restarted should it die. `SingleThreadExecutor`
29
+ # encapsulates all these bahaviors. The task processor is highly resilient
30
+ # to errors from within tasks. Also, should the thread die it will
31
+ # automatically be restarted.
32
+ #
33
+ # The API and behavior of this class are based on Java's `SingleThreadExecutor`.
26
34
  #
27
- # @!macro thread_pool_options
28
35
  # @!macro abstract_executor_service_public_api
29
36
  class SingleThreadExecutor < SingleThreadExecutorImplementation
30
37
 
@@ -32,9 +39,12 @@ module Concurrent
32
39
  #
33
40
  # Create a new thread pool.
34
41
  #
35
- # @option opts [Symbol] :fallback_policy (:discard) the policy for
36
- # handling new tasks that are received when the queue size has
37
- # reached `max_queue` or after the executor has shut down
42
+ # @option opts [Symbol] :fallback_policy (:discard) the policy for handling new
43
+ # tasks that are received when the queue size has reached
44
+ # `max_queue` or the executor has shut down
45
+ #
46
+ # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
47
+ # in `FALLBACK_POLICIES`
38
48
  #
39
49
  # @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
40
50
  # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
@@ -6,6 +6,8 @@ require 'concurrent/executor/single_thread_executor'
6
6
 
7
7
  module Concurrent
8
8
 
9
+ autoload :Options, 'concurrent/options'
10
+
9
11
  # Executes a collection of tasks, each after a given delay. A master task
10
12
  # monitors the set and schedules each task for execution at the appropriate
11
13
  # time. Tasks are run on the global thread pool or on the supplied executor.
@@ -73,7 +75,7 @@ module Concurrent
73
75
  # @!visibility private
74
76
  def ns_initialize(opts)
75
77
  @queue = Collection::NonConcurrentPriorityQueue.new(order: :min)
76
- @task_executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
78
+ @task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
77
79
  @timer_executor = SingleThreadExecutor.new
78
80
  @condition = Event.new
79
81
  self.auto_terminate = opts.fetch(:auto_terminate, true)
@@ -115,7 +117,7 @@ module Concurrent
115
117
  synchronize{ @queue.delete(task) }
116
118
  end
117
119
 
118
- # `ExecutorServic` callback called during shutdown.
120
+ # `ExecutorService` callback called during shutdown.
119
121
  #
120
122
  # @!visibility private
121
123
  def ns_shutdown_execution
@@ -1,6 +1,5 @@
1
1
  require 'concurrent/executor/abstract_executor_service'
2
2
  require 'concurrent/executor/cached_thread_pool'
3
- require 'concurrent/executor/executor'
4
3
  require 'concurrent/executor/executor_service'
5
4
  require 'concurrent/executor/fixed_thread_pool'
6
5
  require 'concurrent/executor/immediate_executor'
@@ -1,11 +1,12 @@
1
1
  require 'thread'
2
2
  require 'concurrent/errors'
3
3
  require 'concurrent/ivar'
4
- require 'concurrent/executor/executor'
5
4
  require 'concurrent/executor/safe_task_executor'
6
5
 
7
6
  module Concurrent
8
7
 
8
+ autoload :Options, 'concurrent/options'
9
+
9
10
  # {include:file:doc/future.md}
10
11
  #
11
12
  # @!macro copy_options
@@ -129,7 +130,7 @@ module Concurrent
129
130
  super
130
131
  @state = :unscheduled
131
132
  @task = opts[:__task_from_block__]
132
- @executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
133
+ @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
133
134
  @args = get_arguments_from(opts)
134
135
  end
135
136
  end
@@ -24,12 +24,12 @@ module Concurrent
24
24
 
25
25
  elsif Concurrent.on_rbx?
26
26
  require 'monitor'
27
+ require 'concurrent/thread_safe/util/array_hash_rbx'
27
28
 
28
29
  # @!macro concurrent_hash
29
30
  class Hash < ::Hash
30
31
  end
31
32
 
32
33
  ThreadSafe::Util.make_synchronized_on_rbx Hash
33
-
34
34
  end
35
35
  end
@@ -9,6 +9,10 @@ module Concurrent
9
9
  module ImmutableStruct
10
10
  include Synchronization::AbstractStruct
11
11
 
12
+ def self.included(base)
13
+ base.safe_initialization!
14
+ end
15
+
12
16
  # @!macro struct_values
13
17
  def values
14
18
  ns_values
@@ -77,7 +81,7 @@ module Concurrent
77
81
  FACTORY.define_struct(clazz_name, args, &block)
78
82
  end
79
83
 
80
- FACTORY = Class.new(Synchronization::Object) do
84
+ FACTORY = Class.new(Synchronization::LockableObject) do
81
85
  def define_struct(name, members, &block)
82
86
  synchronize do
83
87
  Synchronization::AbstractStruct.define_struct_class(ImmutableStruct, Synchronization::Object, name, members, &block)
@@ -43,7 +43,7 @@ module Concurrent
43
43
  # In Proceedings of Workshop on Graph Reduction, 1986.
44
44
  # 2. For recent application:
45
45
  # [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html).
46
- class IVar < Synchronization::Object
46
+ class IVar < Synchronization::LockableObject
47
47
  include Concern::Obligation
48
48
  include Concern::Observable
49
49
 
@@ -75,7 +75,7 @@ module Concurrent
75
75
  alias_method :to_s, :inspect
76
76
 
77
77
  # @!macro [attach] struct_merge
78
- #
78
+ #
79
79
  # Returns a new struct containing the contents of `other` and the contents
80
80
  # of `self`. If no block is specified, the value for entries with duplicate
81
81
  # keys will be that of `other`. Otherwise the value for each duplicate key
@@ -98,7 +98,7 @@ module Concurrent
98
98
  # @!macro [attach] struct_to_h
99
99
  #
100
100
  # Returns a hash containing the names and values for the struct’s members.
101
- #
101
+ #
102
102
  # @return [Hash] the names and values for the struct’s members
103
103
  def to_h
104
104
  synchronize { ns_to_h }
@@ -184,8 +184,9 @@ module Concurrent
184
184
  # @raise [IndexError] if the index is out of range.
185
185
  def []=(member, value)
186
186
  if member.is_a? Integer
187
- if member >= @values.length
188
- raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
187
+ length = synchronize { @values.length }
188
+ if member >= length
189
+ raise IndexError.new("offset #{member} too large for struct(size:#{length})")
189
190
  end
190
191
  synchronize { @values[member] = value }
191
192
  else
@@ -206,10 +207,10 @@ module Concurrent
206
207
  FACTORY.define_struct(clazz_name, args, &block)
207
208
  end
208
209
 
209
- FACTORY = Class.new(Synchronization::Object) do
210
+ FACTORY = Class.new(Synchronization::LockableObject) do
210
211
  def define_struct(name, members, &block)
211
212
  synchronize do
212
- clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::Object, name, members, &block)
213
+ clazz = Synchronization::AbstractStruct.define_struct_class(MutableStruct, Synchronization::LockableObject, name, members, &block)
213
214
  members.each_with_index do |member, index|
214
215
  clazz.send(:define_method, member) do
215
216
  synchronize { @values[index] }
@@ -1,10 +1,11 @@
1
- # This file has circular require issues. For now we assume all
2
- # necessary dependencies have been required already.
1
+ # This file has circular require issues. It must be autoloaded.
2
+
3
+ require 'concurrent/configuration'
3
4
 
4
5
  module Concurrent
5
6
 
6
7
  # @!visibility private
7
- module Executor
8
+ module Options
8
9
 
9
10
  # Get the requested `Executor` based on the values set in the options hash.
10
11
  #
@@ -1,10 +1,11 @@
1
1
  require 'thread'
2
2
  require 'concurrent/errors'
3
3
  require 'concurrent/ivar'
4
- require 'concurrent/executor/executor'
5
4
 
6
5
  module Concurrent
7
6
 
7
+ autoload :Options, 'concurrent/options'
8
+
8
9
  PromiseExecutionError = Class.new(StandardError)
9
10
 
10
11
  # Promises are inspired by the JavaScript [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
@@ -441,7 +442,7 @@ module Concurrent
441
442
  def ns_initialize(value, opts)
442
443
  super
443
444
 
444
- @executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
445
+ @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
445
446
  @args = get_arguments_from(opts)
446
447
 
447
448
  @parent = opts.fetch(:parent) { nil }
@@ -2,11 +2,12 @@ require 'concurrent/errors'
2
2
  require 'concurrent/configuration'
3
3
  require 'concurrent/ivar'
4
4
  require 'concurrent/collection/copy_on_notify_observer_set'
5
- require 'concurrent/executor/executor'
6
5
  require 'concurrent/utility/monotonic_time'
7
6
 
8
7
  module Concurrent
9
8
 
9
+ autoload :Options, 'concurrent/options'
10
+
10
11
  # `ScheduledTask` is a close relative of `Concurrent::Future` but with one
11
12
  # important difference: A `Future` is set to execute as soon as possible
12
13
  # whereas a `ScheduledTask` is set to execute after a specified delay. This
@@ -173,7 +174,7 @@ module Concurrent
173
174
  @delay = delay.to_f
174
175
  @task = task
175
176
  @time = nil
176
- @executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
177
+ @executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
177
178
  self.observers = Collection::CopyOnNotifyObserverSet.new
178
179
  end
179
180
  end
@@ -74,8 +74,9 @@ module Concurrent
74
74
  # @raise [Concurrent::ImmutabilityError] if the given member has already been set
75
75
  def []=(member, value)
76
76
  if member.is_a? Integer
77
- if member >= @values.length
78
- raise IndexError.new("offset #{member} too large for struct(size:#{@values.length})")
77
+ length = synchronize { @values.length }
78
+ if member >= length
79
+ raise IndexError.new("offset #{member} too large for struct(size:#{length})")
79
80
  end
80
81
  synchronize do
81
82
  unless @values[member].nil?
@@ -101,10 +102,10 @@ module Concurrent
101
102
  FACTORY.define_struct(clazz_name, args, &block)
102
103
  end
103
104
 
104
- FACTORY = Class.new(Synchronization::Object) do
105
+ FACTORY = Class.new(Synchronization::LockableObject) do
105
106
  def define_struct(name, members, &block)
106
107
  synchronize do
107
- clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::Object, name, members, &block)
108
+ clazz = Synchronization::AbstractStruct.define_struct_class(SettableStruct, Synchronization::LockableObject, name, members, &block)
108
109
  members.each_with_index do |member, index|
109
110
  clazz.send(:define_method, member) do
110
111
  synchronize { @values[index] }
@@ -1,11 +1,19 @@
1
1
  require 'concurrent/utility/engine'
2
+
2
3
  require 'concurrent/synchronization/abstract_object'
3
- require 'concurrent/synchronization/java_object'
4
- require 'concurrent/synchronization/mutex_object'
5
- require 'concurrent/synchronization/monitor_object'
4
+ require 'concurrent/synchronization/mri_object'
5
+ require 'concurrent/synchronization/jruby_object'
6
6
  require 'concurrent/synchronization/rbx_object'
7
7
  require 'concurrent/synchronization/object'
8
8
 
9
+ require 'concurrent/synchronization/abstract_lockable_object'
10
+ require 'concurrent/synchronization/mri_lockable_object'
11
+ require 'concurrent/synchronization/jruby_lockable_object'
12
+ require 'concurrent/synchronization/rbx_lockable_object'
13
+
14
+ require 'concurrent/utility/native_extension_loader' # load native part first
15
+ require 'concurrent/synchronization/lockable_object'
16
+
9
17
  require 'concurrent/synchronization/condition'
10
18
  require 'concurrent/synchronization/lock'
11
19
 
@@ -0,0 +1,117 @@
1
+ module Concurrent
2
+ module Synchronization
3
+ # @!macro synchronization_object
4
+ # @!visibility private
5
+ class AbstractLockableObject < Object
6
+
7
+ protected
8
+
9
+ # @!macro [attach] synchronization_object_method_synchronize
10
+ #
11
+ # @yield runs the block synchronized against this object,
12
+ # equivalent of java's `synchronize(this) {}`
13
+ # @note can by made public in descendants if required by `public :synchronize`
14
+ def synchronize
15
+ raise NotImplementedError
16
+ end
17
+
18
+ # @!macro [attach] synchronization_object_method_ns_initialize
19
+ #
20
+ # initialization of the object called inside synchronize block
21
+ # @note has to be called manually when required in children of this class
22
+ # @example
23
+ # class Child < Concurrent::Synchornization::Object
24
+ # def initialize(*args, &block)
25
+ # super(&nil)
26
+ # synchronize { ns_initialize(*args, &block) }
27
+ # end
28
+ #
29
+ # def ns_initialize(*args, &block)
30
+ # @args = args
31
+ # end
32
+ # end
33
+ # TODO (pitr 12-Sep-2015): remove
34
+ def ns_initialize(*args, &block)
35
+ end
36
+
37
+ # @!macro [attach] synchronization_object_method_ns_wait_until
38
+ #
39
+ # Wait until condition is met or timeout passes,
40
+ # protects against spurious wake-ups.
41
+ # @param [Numeric, nil] timeout in seconds, `nil` means no timeout
42
+ # @yield condition to be met
43
+ # @yieldreturn [true, false]
44
+ # @return [true, false] if condition met
45
+ # @note only to be used inside synchronized block
46
+ # @note to provide direct access to this method in a descendant add method
47
+ # ```
48
+ # def wait_until(timeout = nil, &condition)
49
+ # synchronize { ns_wait_until(timeout, &condition) }
50
+ # end
51
+ # ```
52
+ def ns_wait_until(timeout = nil, &condition)
53
+ if timeout
54
+ wait_until = Concurrent.monotonic_time + timeout
55
+ loop do
56
+ now = Concurrent.monotonic_time
57
+ condition_result = condition.call
58
+ return condition_result if now >= wait_until || condition_result
59
+ ns_wait wait_until - now
60
+ end
61
+ else
62
+ ns_wait timeout until condition.call
63
+ true
64
+ end
65
+ end
66
+
67
+ # @!macro [attach] synchronization_object_method_ns_wait
68
+ #
69
+ # Wait until another thread calls #signal or #broadcast,
70
+ # spurious wake-ups can happen.
71
+ #
72
+ # @param [Numeric, nil] timeout in seconds, `nil` means no timeout
73
+ # @return [self]
74
+ # @note only to be used inside synchronized block
75
+ # @note to provide direct access to this method in a descendant add method
76
+ # ```
77
+ # def wait(timeout = nil)
78
+ # synchronize { ns_wait(timeout) }
79
+ # end
80
+ # ```
81
+ def ns_wait(timeout = nil)
82
+ raise NotImplementedError
83
+ end
84
+
85
+ # @!macro [attach] synchronization_object_method_ns_signal
86
+ #
87
+ # Signal one waiting thread.
88
+ # @return [self]
89
+ # @note only to be used inside synchronized block
90
+ # @note to provide direct access to this method in a descendant add method
91
+ # ```
92
+ # def signal
93
+ # synchronize { ns_signal }
94
+ # end
95
+ # ```
96
+ def ns_signal
97
+ raise NotImplementedError
98
+ end
99
+
100
+ # @!macro [attach] synchronization_object_method_ns_broadcast
101
+ #
102
+ # Broadcast to all waiting threads.
103
+ # @return [self]
104
+ # @note only to be used inside synchronized block
105
+ # @note to provide direct access to this method in a descendant add method
106
+ # ```
107
+ # def broadcast
108
+ # synchronize { ns_broadcast }
109
+ # end
110
+ # ```
111
+ def ns_broadcast
112
+ raise NotImplementedError
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -5,125 +5,13 @@ module Concurrent
5
5
  # @!visibility private
6
6
  class AbstractObject
7
7
 
8
- # @!macro [attach] synchronization_object_method_initialize
9
- #
10
- # @abstract for helper ivar initialization if needed,
11
- # otherwise it can be left empty. It has to call ns_initialize.
12
- def initialize(*args, &block)
13
- raise NotImplementedError
14
- end
15
-
16
- protected
17
-
18
- # @!macro [attach] synchronization_object_method_synchronize
19
- #
20
- # @yield runs the block synchronized against this object,
21
- # equivalent of java's `synchronize(this) {}`
22
- # @note can by made public in descendants if required by `public :synchronize`
23
- def synchronize
24
- raise NotImplementedError
25
- end
26
-
27
- # @!macro [attach] synchronization_object_method_ns_initialize
28
- #
29
- # initialization of the object called inside synchronize block
30
- # @note has to be called manually when required in children of this class
31
- # @example
32
- # class Child < Concurrent::Synchornization::Object
33
- # def initialize(*args, &block)
34
- # super(&nil)
35
- # synchronize { ns_initialize(*args, &block) }
36
- # end
37
- #
38
- # def ns_initialize(*args, &block)
39
- # @args = args
40
- # end
41
- # end
42
- def ns_initialize(*args, &block)
43
- end
44
-
45
- # @!macro [attach] synchronization_object_method_ns_wait_until
46
- #
47
- # Wait until condition is met or timeout passes,
48
- # protects against spurious wake-ups.
49
- # @param [Numeric, nil] timeout in seconds, `nil` means no timeout
50
- # @yield condition to be met
51
- # @yieldreturn [true, false]
52
- # @return [true, false] if condition met
53
- # @note only to be used inside synchronized block
54
- # @note to provide direct access to this method in a descendant add method
55
- # ```
56
- # def wait_until(timeout = nil, &condition)
57
- # synchronize { ns_wait_until(timeout, &condition) }
58
- # end
59
- # ```
60
- def ns_wait_until(timeout = nil, &condition)
61
- if timeout
62
- wait_until = Concurrent.monotonic_time + timeout
63
- loop do
64
- now = Concurrent.monotonic_time
65
- condition_result = condition.call
66
- # 0.001 correction to avoid error when `wait_until - now` is smaller than 0.0005 and rounded to 0
67
- # when passed to java #wait(long timeout)
68
- return condition_result if (now + 0.001) >= wait_until || condition_result
69
- ns_wait wait_until - now
70
- end
71
- else
72
- ns_wait timeout until condition.call
73
- true
74
- end
75
- end
76
-
77
- # @!macro [attach] synchronization_object_method_ns_wait
78
- #
79
- # Wait until another thread calls #signal or #broadcast,
80
- # spurious wake-ups can happen.
81
- #
82
- # @param [Numeric, nil] timeout in seconds, `nil` means no timeout
83
- # @return [self]
84
- # @note only to be used inside synchronized block
85
- # @note to provide direct access to this method in a descendant add method
86
- # ```
87
- # def wait(timeout = nil)
88
- # synchronize { ns_wait(timeout) }
89
- # end
90
- # ```
91
- def ns_wait(timeout = nil)
92
- raise NotImplementedError
93
- end
94
-
95
- # @!macro [attach] synchronization_object_method_ns_signal
96
- #
97
- # Signal one waiting thread.
98
- # @return [self]
99
- # @note only to be used inside synchronized block
100
- # @note to provide direct access to this method in a descendant add method
101
- # ```
102
- # def signal
103
- # synchronize { ns_signal }
104
- # end
105
- # ```
106
- def ns_signal
107
- raise NotImplementedError
108
- end
109
-
110
- # @!macro [attach] synchronization_object_method_ns_broadcast
111
- #
112
- # Broadcast to all waiting threads.
113
- # @return [self]
114
- # @note only to be used inside synchronized block
115
- # @note to provide direct access to this method in a descendant add method
116
- # ```
117
- # def broadcast
118
- # synchronize { ns_broadcast }
119
- # end
120
- # ```
121
- def ns_broadcast
8
+ # @abstract has to be implemented based on Ruby runtime
9
+ def initialize
122
10
  raise NotImplementedError
123
11
  end
124
12
 
125
13
  # @!macro [attach] synchronization_object_method_ensure_ivar_visibility
126
- #
14
+ #
127
15
  # Allows to construct immutable objects where all fields are visible after initialization, not requiring
128
16
  # further synchronization on access.
129
17
  # @example
@@ -135,28 +23,27 @@ module Concurrent
135
23
  # # now it can be shared as Java's final field
136
24
  # end
137
25
  # end
26
+ # @!visibility private
138
27
  def ensure_ivar_visibility!
28
+ # We have to prevent ivar writes to reordered with storing of the final instance reference
29
+ # Therefore wee need a fullFence to prevent reordering in both directions.
30
+ full_memory_barrier
31
+ end
32
+
33
+ protected
34
+
35
+ # @!visibility private
36
+ # @abstract
37
+ def full_memory_barrier
139
38
  raise NotImplementedError
140
39
  end
141
40
 
142
41
  # @!macro [attach] synchronization_object_method_self_attr_volatile
143
- #
42
+ #
144
43
  # creates methods for reading and writing to a instance variable with volatile (Java semantic) instance variable
145
44
  # return [Array<Symbol>] names of defined method names
146
45
  def self.attr_volatile(*names)
147
- names.each do |name|
148
- ivar = :"@volatile_#{name}"
149
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
150
- def #{name}
151
- #{ivar}
152
- end
153
-
154
- def #{name}=(value)
155
- #{ivar} = value
156
- end
157
- RUBY
158
- end
159
- names.map { |n| [n, :"#{n}="] }.flatten
46
+ raise NotImplementedError
160
47
  end
161
48
  end
162
49
  end