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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +16 -18
- data/lib/concurrent.rb +3 -3
- data/lib/concurrent/agent.rb +583 -0
- data/lib/concurrent/array.rb +1 -0
- data/lib/concurrent/async.rb +236 -111
- data/lib/concurrent/atom.rb +101 -46
- data/lib/concurrent/atomic/atomic_boolean.rb +2 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +2 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +1 -1
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +1 -1
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent/atomic/mutex_semaphore.rb +2 -2
- data/lib/concurrent/atomic/read_write_lock.rb +5 -4
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +3 -1
- data/lib/concurrent/atomic/thread_local_var.rb +2 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +1 -1
- data/lib/concurrent/atomics.rb +6 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +7 -15
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +7 -15
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +5 -0
- data/lib/concurrent/concern/observable.rb +38 -13
- data/lib/concurrent/configuration.rb +5 -4
- data/lib/concurrent/delay.rb +9 -8
- data/lib/concurrent/exchanger.rb +2 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +2 -2
- data/lib/concurrent/executor/java_single_thread_executor.rb +0 -1
- data/lib/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +10 -68
- data/lib/concurrent/executor/safe_task_executor.rb +7 -8
- data/lib/concurrent/executor/serialized_execution.rb +4 -4
- data/lib/concurrent/executor/single_thread_executor.rb +20 -10
- data/lib/concurrent/executor/timer_set.rb +4 -2
- data/lib/concurrent/executors.rb +0 -1
- data/lib/concurrent/future.rb +3 -2
- data/lib/concurrent/hash.rb +1 -1
- data/lib/concurrent/immutable_struct.rb +5 -1
- data/lib/concurrent/ivar.rb +1 -1
- data/lib/concurrent/mutable_struct.rb +7 -6
- data/lib/concurrent/{executor/executor.rb → options.rb} +4 -3
- data/lib/concurrent/promise.rb +3 -2
- data/lib/concurrent/scheduled_task.rb +3 -2
- data/lib/concurrent/settable_struct.rb +5 -4
- data/lib/concurrent/synchronization.rb +11 -3
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +117 -0
- data/lib/concurrent/synchronization/abstract_object.rb +16 -129
- data/lib/concurrent/synchronization/abstract_struct.rb +2 -3
- data/lib/concurrent/synchronization/condition.rb +6 -4
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/{java_object.rb → jruby_object.rb} +5 -3
- data/lib/concurrent/synchronization/lock.rb +3 -2
- data/lib/concurrent/synchronization/lockable_object.rb +59 -0
- data/lib/concurrent/synchronization/mri_lockable_object.rb +71 -0
- data/lib/concurrent/synchronization/mri_object.rb +35 -0
- data/lib/concurrent/synchronization/object.rb +111 -39
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +64 -0
- data/lib/concurrent/synchronization/rbx_object.rb +17 -68
- data/lib/concurrent/thread_safe/util.rb +0 -9
- data/lib/concurrent/thread_safe/util/adder.rb +3 -0
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +3 -1
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +3 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +1 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +6 -1
- data/lib/concurrent/thread_safe/util/volatile.rb +2 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +2 -0
- data/lib/concurrent/tvar.rb +36 -0
- data/lib/concurrent/utility/at_exit.rb +1 -1
- data/lib/concurrent/utility/monotonic_time.rb +3 -4
- data/lib/concurrent/utility/native_extension_loader.rb +1 -1
- data/lib/concurrent/version.rb +2 -2
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +11 -6
- data/lib/concurrent/synchronization/monitor_object.rb +0 -27
- 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
|
20
|
-
#
|
21
|
-
#
|
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
|
-
#
|
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
|
-
#
|
37
|
-
#
|
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 =
|
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
|
-
# `
|
120
|
+
# `ExecutorService` callback called during shutdown.
|
119
121
|
#
|
120
122
|
# @!visibility private
|
121
123
|
def ns_shutdown_execution
|
data/lib/concurrent/executors.rb
CHANGED
@@ -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'
|
data/lib/concurrent/future.rb
CHANGED
@@ -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 =
|
133
|
+
@executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
|
133
134
|
@args = get_arguments_from(opts)
|
134
135
|
end
|
135
136
|
end
|
data/lib/concurrent/hash.rb
CHANGED
@@ -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::
|
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)
|
data/lib/concurrent/ivar.rb
CHANGED
@@ -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::
|
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
|
-
|
188
|
-
|
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::
|
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::
|
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.
|
2
|
-
|
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
|
8
|
+
module Options
|
8
9
|
|
9
10
|
# Get the requested `Executor` based on the values set in the options hash.
|
10
11
|
#
|
data/lib/concurrent/promise.rb
CHANGED
@@ -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 =
|
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 =
|
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
|
-
|
78
|
-
|
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::
|
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::
|
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/
|
4
|
-
require 'concurrent/synchronization/
|
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
|
-
#
|
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
|
-
|
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
|