concurrent-ruby 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +478 -0
- data/Gemfile +41 -0
- data/LICENSE.md +23 -0
- data/README.md +381 -0
- data/Rakefile +327 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +159 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/concurrent-ruby.rb +1 -0
- data/lib/concurrent.rb +134 -0
- data/lib/concurrent/agent.rb +587 -0
- data/lib/concurrent/array.rb +66 -0
- data/lib/concurrent/async.rb +459 -0
- data/lib/concurrent/atom.rb +222 -0
- data/lib/concurrent/atomic/abstract_thread_local_var.rb +66 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +126 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +143 -0
- data/lib/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent/atomic/atomic_reference.rb +204 -0
- data/lib/concurrent/atomic/count_down_latch.rb +100 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +128 -0
- data/lib/concurrent/atomic/event.rb +109 -0
- data/lib/concurrent/atomic/java_count_down_latch.rb +42 -0
- data/lib/concurrent/atomic/java_thread_local_var.rb +37 -0
- data/lib/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
- data/lib/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
- data/lib/concurrent/atomic/mutex_count_down_latch.rb +44 -0
- data/lib/concurrent/atomic/mutex_semaphore.rb +115 -0
- data/lib/concurrent/atomic/read_write_lock.rb +254 -0
- data/lib/concurrent/atomic/reentrant_read_write_lock.rb +379 -0
- data/lib/concurrent/atomic/ruby_thread_local_var.rb +161 -0
- data/lib/concurrent/atomic/semaphore.rb +145 -0
- data/lib/concurrent/atomic/thread_local_var.rb +104 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +56 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +111 -0
- data/lib/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
- data/lib/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
- data/lib/concurrent/collection/map/mri_map_backend.rb +66 -0
- data/lib/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
- data/lib/concurrent/collection/map/synchronized_map_backend.rb +82 -0
- data/lib/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
- data/lib/concurrent/collection/ruby_non_concurrent_priority_queue.rb +150 -0
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +73 -0
- data/lib/concurrent/concern/logging.rb +32 -0
- data/lib/concurrent/concern/obligation.rb +220 -0
- data/lib/concurrent/concern/observable.rb +110 -0
- data/lib/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent/configuration.rb +184 -0
- data/lib/concurrent/constants.rb +8 -0
- data/lib/concurrent/dataflow.rb +81 -0
- data/lib/concurrent/delay.rb +199 -0
- data/lib/concurrent/errors.rb +69 -0
- data/lib/concurrent/exchanger.rb +352 -0
- data/lib/concurrent/executor/abstract_executor_service.rb +134 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +62 -0
- data/lib/concurrent/executor/executor_service.rb +185 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +206 -0
- data/lib/concurrent/executor/immediate_executor.rb +66 -0
- data/lib/concurrent/executor/indirect_immediate_executor.rb +44 -0
- data/lib/concurrent/executor/java_executor_service.rb +91 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +29 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +123 -0
- data/lib/concurrent/executor/ruby_executor_service.rb +78 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +22 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +362 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serial_executor_service.rb +34 -0
- data/lib/concurrent/executor/serialized_execution.rb +107 -0
- data/lib/concurrent/executor/serialized_execution_delegator.rb +28 -0
- data/lib/concurrent/executor/simple_executor_service.rb +100 -0
- data/lib/concurrent/executor/single_thread_executor.rb +56 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +87 -0
- data/lib/concurrent/executor/timer_set.rb +173 -0
- data/lib/concurrent/executors.rb +20 -0
- data/lib/concurrent/future.rb +141 -0
- data/lib/concurrent/hash.rb +59 -0
- data/lib/concurrent/immutable_struct.rb +93 -0
- data/lib/concurrent/ivar.rb +207 -0
- data/lib/concurrent/map.rb +337 -0
- data/lib/concurrent/maybe.rb +229 -0
- data/lib/concurrent/mutable_struct.rb +229 -0
- data/lib/concurrent/mvar.rb +242 -0
- data/lib/concurrent/options.rb +42 -0
- data/lib/concurrent/promise.rb +579 -0
- data/lib/concurrent/promises.rb +2167 -0
- data/lib/concurrent/re_include.rb +58 -0
- data/lib/concurrent/scheduled_task.rb +318 -0
- data/lib/concurrent/set.rb +66 -0
- data/lib/concurrent/settable_struct.rb +129 -0
- data/lib/concurrent/synchronization.rb +30 -0
- data/lib/concurrent/synchronization/abstract_lockable_object.rb +98 -0
- data/lib/concurrent/synchronization/abstract_object.rb +24 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +160 -0
- data/lib/concurrent/synchronization/condition.rb +60 -0
- data/lib/concurrent/synchronization/jruby_lockable_object.rb +13 -0
- data/lib/concurrent/synchronization/jruby_object.rb +45 -0
- data/lib/concurrent/synchronization/lock.rb +36 -0
- data/lib/concurrent/synchronization/lockable_object.rb +74 -0
- data/lib/concurrent/synchronization/mri_object.rb +44 -0
- data/lib/concurrent/synchronization/mutex_lockable_object.rb +76 -0
- data/lib/concurrent/synchronization/object.rb +183 -0
- data/lib/concurrent/synchronization/rbx_lockable_object.rb +65 -0
- data/lib/concurrent/synchronization/rbx_object.rb +49 -0
- data/lib/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/concurrent/synchronization/volatile.rb +36 -0
- data/lib/concurrent/thread_safe/synchronized_delegator.rb +50 -0
- data/lib/concurrent/thread_safe/util.rb +16 -0
- data/lib/concurrent/thread_safe/util/adder.rb +74 -0
- data/lib/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
- data/lib/concurrent/thread_safe/util/data_structures.rb +63 -0
- data/lib/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
- data/lib/concurrent/thread_safe/util/striped64.rb +246 -0
- data/lib/concurrent/thread_safe/util/volatile.rb +75 -0
- data/lib/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
- data/lib/concurrent/timer_task.rb +334 -0
- data/lib/concurrent/tuple.rb +86 -0
- data/lib/concurrent/tvar.rb +258 -0
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +56 -0
- data/lib/concurrent/utility/monotonic_time.rb +58 -0
- data/lib/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/concurrent/utility/native_integer.rb +53 -0
- data/lib/concurrent/utility/processor_counter.rb +158 -0
- data/lib/concurrent/version.rb +3 -0
- metadata +193 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# @!visibility private
|
5
|
+
module TruffleRubyAttrVolatile
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def attr_volatile(*names)
|
12
|
+
names.each do |name|
|
13
|
+
ivar = :"@volatile_#{name}"
|
14
|
+
|
15
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
16
|
+
def #{name}
|
17
|
+
full_memory_barrier
|
18
|
+
#{ivar}
|
19
|
+
end
|
20
|
+
|
21
|
+
def #{name}=(value)
|
22
|
+
#{ivar} = value
|
23
|
+
full_memory_barrier
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
end
|
27
|
+
|
28
|
+
names.map { |n| [n, :"#{n}="] }.flatten
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def full_memory_barrier
|
33
|
+
TruffleRuby.full_memory_barrier
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!visibility private
|
38
|
+
# @!macro internal_implementation_note
|
39
|
+
class TruffleRubyObject < AbstractObject
|
40
|
+
include TruffleRubyAttrVolatile
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
# nothing to do
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Concurrent
|
2
|
+
module Synchronization
|
3
|
+
|
4
|
+
# Volatile adds the attr_volatile class method when included.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# class Foo
|
8
|
+
# include Concurrent::Synchronization::Volatile
|
9
|
+
#
|
10
|
+
# attr_volatile :bar
|
11
|
+
#
|
12
|
+
# def initialize
|
13
|
+
# self.bar = 1
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# foo = Foo.new
|
18
|
+
# foo.bar
|
19
|
+
# => 1
|
20
|
+
# foo.bar = 2
|
21
|
+
# => 2
|
22
|
+
|
23
|
+
Volatile = case
|
24
|
+
when Concurrent.on_cruby?
|
25
|
+
MriAttrVolatile
|
26
|
+
when Concurrent.on_jruby?
|
27
|
+
JRubyAttrVolatile
|
28
|
+
when Concurrent.on_rbx?
|
29
|
+
RbxAttrVolatile
|
30
|
+
when Concurrent.on_truffleruby?
|
31
|
+
TruffleRubyAttrVolatile
|
32
|
+
else
|
33
|
+
MriAttrVolatile
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'monitor'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
unless defined?(SynchronizedDelegator)
|
6
|
+
|
7
|
+
# This class provides a trivial way to synchronize all calls to a given object
|
8
|
+
# by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
|
9
|
+
# around the delegated `#send`. Example:
|
10
|
+
#
|
11
|
+
# array = [] # not thread-safe on many impls
|
12
|
+
# array = SynchronizedDelegator.new([]) # thread-safe
|
13
|
+
#
|
14
|
+
# A simple `Monitor` provides a very coarse-grained way to synchronize a given
|
15
|
+
# object, in that it will cause synchronization for methods that have no need
|
16
|
+
# for it, but this is a trivial way to get thread-safety where none may exist
|
17
|
+
# currently on some implementations.
|
18
|
+
#
|
19
|
+
# This class is currently being considered for inclusion into stdlib, via
|
20
|
+
# https://bugs.ruby-lang.org/issues/8556
|
21
|
+
#
|
22
|
+
# @!visibility private
|
23
|
+
class SynchronizedDelegator < SimpleDelegator
|
24
|
+
def setup
|
25
|
+
@old_abort = Thread.abort_on_exception
|
26
|
+
Thread.abort_on_exception = true
|
27
|
+
end
|
28
|
+
|
29
|
+
def teardown
|
30
|
+
Thread.abort_on_exception = @old_abort
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(obj)
|
34
|
+
__setobj__(obj)
|
35
|
+
@monitor = Monitor.new
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(method, *args, &block)
|
39
|
+
monitor = @monitor
|
40
|
+
begin
|
41
|
+
monitor.enter
|
42
|
+
super
|
43
|
+
ensure
|
44
|
+
monitor.exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# @!visibility private
|
4
|
+
module ThreadSafe
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
module Util
|
8
|
+
|
9
|
+
# TODO (pitr-ch 15-Oct-2016): migrate to Utility::NativeInteger
|
10
|
+
FIXNUM_BIT_SIZE = (0.size * 8) - 2
|
11
|
+
MAX_INT = (2 ** FIXNUM_BIT_SIZE) - 1
|
12
|
+
# TODO (pitr-ch 15-Oct-2016): migrate to Utility::ProcessorCounter
|
13
|
+
CPU_COUNT = 16 # is there a way to determine this?
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'concurrent/thread_safe/util'
|
2
|
+
require 'concurrent/thread_safe/util/striped64'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
module ThreadSafe
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
module Util
|
11
|
+
|
12
|
+
# A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8
|
13
|
+
# available in public domain.
|
14
|
+
#
|
15
|
+
# Original source code available here:
|
16
|
+
# http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8
|
17
|
+
#
|
18
|
+
# One or more variables that together maintain an initially zero
|
19
|
+
# sum. When updates (method +add+) are contended across threads,
|
20
|
+
# the set of variables may grow dynamically to reduce contention.
|
21
|
+
# Method +sum+ returns the current total combined across the
|
22
|
+
# variables maintaining the sum.
|
23
|
+
#
|
24
|
+
# This class is usually preferable to single +Atomic+ reference when
|
25
|
+
# multiple threads update a common sum that is used for purposes such
|
26
|
+
# as collecting statistics, not for fine-grained synchronization
|
27
|
+
# control. Under low update contention, the two classes have similar
|
28
|
+
# characteristics. But under high contention, expected throughput of
|
29
|
+
# this class is significantly higher, at the expense of higher space
|
30
|
+
# consumption.
|
31
|
+
#
|
32
|
+
# @!visibility private
|
33
|
+
class Adder < Striped64
|
34
|
+
# Adds the given value.
|
35
|
+
def add(x)
|
36
|
+
if (current_cells = cells) || !cas_base_computed {|current_base| current_base + x}
|
37
|
+
was_uncontended = true
|
38
|
+
hash = hash_code
|
39
|
+
unless current_cells && (cell = current_cells.volatile_get_by_hash(hash)) && (was_uncontended = cell.cas_computed {|current_value| current_value + x})
|
40
|
+
retry_update(x, hash, was_uncontended) {|current_value| current_value + x}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def increment
|
46
|
+
add(1)
|
47
|
+
end
|
48
|
+
|
49
|
+
def decrement
|
50
|
+
add(-1)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the current sum. The returned value is _NOT_ an
|
54
|
+
# atomic snapshot: Invocation in the absence of concurrent
|
55
|
+
# updates returns an accurate result, but concurrent updates that
|
56
|
+
# occur while the sum is being calculated might not be
|
57
|
+
# incorporated.
|
58
|
+
def sum
|
59
|
+
x = base
|
60
|
+
if current_cells = cells
|
61
|
+
current_cells.each do |cell|
|
62
|
+
x += cell.value if cell
|
63
|
+
end
|
64
|
+
end
|
65
|
+
x
|
66
|
+
end
|
67
|
+
|
68
|
+
def reset
|
69
|
+
internal_reset(0)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'concurrent/thread_safe/util'
|
2
|
+
require 'concurrent/thread_safe/util/volatile'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
module ThreadSafe
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
module Util
|
11
|
+
|
12
|
+
# Provides a cheapest possible (mainly in terms of memory usage) +Mutex+
|
13
|
+
# with the +ConditionVariable+ bundled in.
|
14
|
+
#
|
15
|
+
# Usage:
|
16
|
+
# class A
|
17
|
+
# include CheapLockable
|
18
|
+
#
|
19
|
+
# def do_exlusively
|
20
|
+
# cheap_synchronize { yield }
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def wait_for_something
|
24
|
+
# cheap_synchronize do
|
25
|
+
# cheap_wait until resource_available?
|
26
|
+
# do_something
|
27
|
+
# cheap_broadcast # wake up others
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# @!visibility private
|
33
|
+
module CheapLockable
|
34
|
+
private
|
35
|
+
engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
36
|
+
if engine == 'rbx'
|
37
|
+
# Making use of the Rubinius' ability to lock via object headers to avoid the overhead of the extra Mutex objects.
|
38
|
+
def cheap_synchronize
|
39
|
+
Rubinius.lock(self)
|
40
|
+
begin
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
Rubinius.unlock(self)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def cheap_wait
|
48
|
+
wchan = Rubinius::Channel.new
|
49
|
+
|
50
|
+
begin
|
51
|
+
waiters = @waiters ||= []
|
52
|
+
waiters.push wchan
|
53
|
+
Rubinius.unlock(self)
|
54
|
+
signaled = wchan.receive_timeout nil
|
55
|
+
ensure
|
56
|
+
Rubinius.lock(self)
|
57
|
+
|
58
|
+
unless signaled or waiters.delete(wchan)
|
59
|
+
# we timed out, but got signaled afterwards (e.g. while waiting to
|
60
|
+
# acquire @lock), so pass that signal on to the next waiter
|
61
|
+
waiters.shift << true unless waiters.empty?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def cheap_broadcast
|
69
|
+
waiters = @waiters ||= []
|
70
|
+
waiters.shift << true until waiters.empty?
|
71
|
+
self
|
72
|
+
end
|
73
|
+
elsif engine == 'jruby'
|
74
|
+
# Use Java's native synchronized (this) { wait(); notifyAll(); } to avoid the overhead of the extra Mutex objects
|
75
|
+
require 'jruby'
|
76
|
+
|
77
|
+
def cheap_synchronize
|
78
|
+
JRuby.reference0(self).synchronized { yield }
|
79
|
+
end
|
80
|
+
|
81
|
+
def cheap_wait
|
82
|
+
JRuby.reference0(self).wait
|
83
|
+
end
|
84
|
+
|
85
|
+
def cheap_broadcast
|
86
|
+
JRuby.reference0(self).notify_all
|
87
|
+
end
|
88
|
+
else
|
89
|
+
require 'thread'
|
90
|
+
|
91
|
+
extend Volatile
|
92
|
+
attr_volatile :mutex
|
93
|
+
|
94
|
+
# Non-reentrant Mutex#syncrhonize
|
95
|
+
def cheap_synchronize
|
96
|
+
true until (my_mutex = mutex) || cas_mutex(nil, my_mutex = Mutex.new)
|
97
|
+
my_mutex.synchronize { yield }
|
98
|
+
end
|
99
|
+
|
100
|
+
# Releases this object's +cheap_synchronize+ lock and goes to sleep waiting for other threads to +cheap_broadcast+, reacquires the lock on wakeup.
|
101
|
+
# Must only be called in +cheap_broadcast+'s block.
|
102
|
+
def cheap_wait
|
103
|
+
conditional_variable = @conditional_variable ||= ConditionVariable.new
|
104
|
+
conditional_variable.wait(mutex)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Wakes up all threads waiting for this object's +cheap_synchronize+ lock.
|
108
|
+
# Must only be called in +cheap_broadcast+'s block.
|
109
|
+
def cheap_broadcast
|
110
|
+
if conditional_variable = @conditional_variable
|
111
|
+
conditional_variable.broadcast
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'concurrent/thread_safe/util'
|
2
|
+
|
3
|
+
# Shim for TruffleRuby.synchronized
|
4
|
+
if Concurrent.on_truffleruby? && !TruffleRuby.respond_to?(:synchronized)
|
5
|
+
module TruffleRuby
|
6
|
+
def self.synchronized(object, &block)
|
7
|
+
Truffle::System.synchronized(object, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Concurrent
|
13
|
+
module ThreadSafe
|
14
|
+
module Util
|
15
|
+
def self.make_synchronized_on_rbx(klass)
|
16
|
+
klass.class_eval do
|
17
|
+
private
|
18
|
+
|
19
|
+
def _mon_initialize
|
20
|
+
@_monitor = Monitor.new unless @_monitor # avoid double initialisation
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.new(*args)
|
24
|
+
obj = super(*args)
|
25
|
+
obj.send(:_mon_initialize)
|
26
|
+
obj
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
klass.superclass.instance_methods(false).each do |method|
|
31
|
+
case method
|
32
|
+
when :new_range, :new_reserved
|
33
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
34
|
+
def #{method}(*args)
|
35
|
+
obj = super
|
36
|
+
obj.send(:_mon_initialize)
|
37
|
+
obj
|
38
|
+
end
|
39
|
+
RUBY
|
40
|
+
else
|
41
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
42
|
+
def #{method}(*args)
|
43
|
+
monitor = @_monitor
|
44
|
+
monitor or raise("BUG: Internal monitor was not properly initialized. Please report this to the concurrent-ruby developers.")
|
45
|
+
monitor.synchronize { super }
|
46
|
+
end
|
47
|
+
RUBY
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.make_synchronized_on_truffleruby(klass)
|
53
|
+
klass.superclass.instance_methods(false).each do |method|
|
54
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
55
|
+
def #{method}(*args, &block)
|
56
|
+
TruffleRuby.synchronized(self) { super(*args, &block) }
|
57
|
+
end
|
58
|
+
RUBY
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'concurrent/thread_safe/util'
|
2
|
+
require 'concurrent/tuple'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
module ThreadSafe
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
module Util
|
11
|
+
|
12
|
+
# @!visibility private
|
13
|
+
class PowerOfTwoTuple < Concurrent::Tuple
|
14
|
+
|
15
|
+
def initialize(size)
|
16
|
+
raise ArgumentError, "size must be a power of 2 (#{size.inspect} provided)" unless size > 0 && size & (size - 1) == 0
|
17
|
+
super(size)
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash_to_index(hash)
|
21
|
+
(size - 1) & hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def volatile_get_by_hash(hash)
|
25
|
+
volatile_get(hash_to_index(hash))
|
26
|
+
end
|
27
|
+
|
28
|
+
def volatile_set_by_hash(hash, value)
|
29
|
+
volatile_set(hash_to_index(hash), value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def next_in_size_table
|
33
|
+
self.class.new(size << 1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|