concurrent-ruby 1.0.0.pre1-java → 1.0.0.pre2-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 (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