concurrent-ruby 1.1.8 → 1.2.0
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 +4 -4
- data/CHANGELOG.md +36 -0
- data/Gemfile +2 -8
- data/README.md +49 -28
- data/Rakefile +59 -75
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
- data/lib/concurrent-ruby/concurrent/array.rb +0 -10
- data/lib/concurrent-ruby/concurrent/async.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
- data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
- data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
- data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
- data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
- data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/map.rb +18 -8
- data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
- data/lib/concurrent-ruby/concurrent/promise.rb +2 -1
- data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
- data/lib/concurrent-ruby/concurrent/set.rb +12 -14
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +18 -5
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
- data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
- data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +16 -27
- data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
- metadata +11 -12
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -65
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/constants'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
# @!visibility private
|
6
|
+
# @!macro internal_implementation_note
|
7
|
+
#
|
8
|
+
# An abstract implementation of local storage, with sub-classes for
|
9
|
+
# per-thread and per-fiber locals.
|
10
|
+
#
|
11
|
+
# Each execution context (EC, thread or fiber) has a lazily initialized array
|
12
|
+
# of local variable values. Each time a new local variable is created, we
|
13
|
+
# allocate an "index" for it.
|
14
|
+
#
|
15
|
+
# For example, if the allocated index is 1, that means slot #1 in EVERY EC's
|
16
|
+
# locals array will be used for the value of that variable.
|
17
|
+
#
|
18
|
+
# The good thing about using a per-EC structure to hold values, rather than
|
19
|
+
# a global, is that no synchronization is needed when reading and writing
|
20
|
+
# those values (since the structure is only ever accessed by a single
|
21
|
+
# thread).
|
22
|
+
#
|
23
|
+
# Of course, when a local variable is GC'd, 1) we need to recover its index
|
24
|
+
# for use by other new local variables (otherwise the locals arrays could
|
25
|
+
# get bigger and bigger with time), and 2) we need to null out all the
|
26
|
+
# references held in the now-unused slots (both to avoid blocking GC of those
|
27
|
+
# objects, and also to prevent "stale" values from being passed on to a new
|
28
|
+
# local when the index is reused).
|
29
|
+
#
|
30
|
+
# Because we need to null out freed slots, we need to keep references to
|
31
|
+
# ALL the locals arrays, so we can null out the appropriate slots in all of
|
32
|
+
# them. This is why we need to use a finalizer to clean up the locals array
|
33
|
+
# when the EC goes out of scope.
|
34
|
+
class AbstractLocals
|
35
|
+
def initialize
|
36
|
+
@free = []
|
37
|
+
@lock = Mutex.new
|
38
|
+
@all_arrays = {}
|
39
|
+
@next = 0
|
40
|
+
end
|
41
|
+
|
42
|
+
def synchronize
|
43
|
+
@lock.synchronize { yield }
|
44
|
+
end
|
45
|
+
|
46
|
+
if Concurrent.on_cruby?
|
47
|
+
def weak_synchronize
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
else
|
51
|
+
alias_method :weak_synchronize, :synchronize
|
52
|
+
end
|
53
|
+
|
54
|
+
def next_index(local)
|
55
|
+
index = synchronize do
|
56
|
+
if @free.empty?
|
57
|
+
@next += 1
|
58
|
+
else
|
59
|
+
@free.pop
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# When the local goes out of scope, we should free the associated index
|
64
|
+
# and all values stored into it.
|
65
|
+
ObjectSpace.define_finalizer(local, local_finalizer(index))
|
66
|
+
|
67
|
+
index
|
68
|
+
end
|
69
|
+
|
70
|
+
def free_index(index)
|
71
|
+
weak_synchronize do
|
72
|
+
# The cost of GC'ing a TLV is linear in the number of ECs using local
|
73
|
+
# variables. But that is natural! More ECs means more storage is used
|
74
|
+
# per local variable. So naturally more CPU time is required to free
|
75
|
+
# more storage.
|
76
|
+
#
|
77
|
+
# DO NOT use each_value which might conflict with new pair assignment
|
78
|
+
# into the hash in #set method.
|
79
|
+
@all_arrays.values.each do |locals|
|
80
|
+
locals[index] = nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# free index has to be published after the arrays are cleared:
|
84
|
+
@free << index
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def fetch(index)
|
89
|
+
locals = self.locals
|
90
|
+
value = locals ? locals[index] : nil
|
91
|
+
|
92
|
+
if nil == value
|
93
|
+
yield
|
94
|
+
elsif NULL.equal?(value)
|
95
|
+
nil
|
96
|
+
else
|
97
|
+
value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def set(index, value)
|
102
|
+
locals = self.locals!
|
103
|
+
locals[index] = (nil == value ? NULL : value)
|
104
|
+
|
105
|
+
value
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# When the local goes out of scope, clean up that slot across all locals currently assigned.
|
111
|
+
def local_finalizer(index)
|
112
|
+
proc do
|
113
|
+
free_index(index)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# When a thread/fiber goes out of scope, remove the array from @all_arrays.
|
118
|
+
def thread_fiber_finalizer(array_object_id)
|
119
|
+
proc do
|
120
|
+
weak_synchronize do
|
121
|
+
@all_arrays.delete(array_object_id)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns the locals for the current scope, or nil if none exist.
|
127
|
+
def locals
|
128
|
+
raise NotImplementedError
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the locals for the current scope, creating them if necessary.
|
132
|
+
def locals!
|
133
|
+
raise NotImplementedError
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# @!visibility private
|
138
|
+
# @!macro internal_implementation_note
|
139
|
+
# An array-backed storage of indexed variables per thread.
|
140
|
+
class ThreadLocals < AbstractLocals
|
141
|
+
def locals
|
142
|
+
Thread.current.thread_variable_get(:concurrent_thread_locals)
|
143
|
+
end
|
144
|
+
|
145
|
+
def locals!
|
146
|
+
thread = Thread.current
|
147
|
+
locals = thread.thread_variable_get(:concurrent_thread_locals)
|
148
|
+
|
149
|
+
unless locals
|
150
|
+
locals = thread.thread_variable_set(:concurrent_thread_locals, [])
|
151
|
+
weak_synchronize do
|
152
|
+
@all_arrays[locals.object_id] = locals
|
153
|
+
end
|
154
|
+
# When the thread goes out of scope, we should delete the associated locals:
|
155
|
+
ObjectSpace.define_finalizer(thread, thread_fiber_finalizer(locals.object_id))
|
156
|
+
end
|
157
|
+
|
158
|
+
locals
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# @!visibility private
|
163
|
+
# @!macro internal_implementation_note
|
164
|
+
# An array-backed storage of indexed variables per fiber.
|
165
|
+
class FiberLocals < AbstractLocals
|
166
|
+
def locals
|
167
|
+
Thread.current[:concurrent_fiber_locals]
|
168
|
+
end
|
169
|
+
|
170
|
+
def locals!
|
171
|
+
thread = Thread.current
|
172
|
+
locals = thread[:concurrent_fiber_locals]
|
173
|
+
|
174
|
+
unless locals
|
175
|
+
locals = thread[:concurrent_fiber_locals] = []
|
176
|
+
weak_synchronize do
|
177
|
+
@all_arrays[locals.object_id] = locals
|
178
|
+
end
|
179
|
+
# When the fiber goes out of scope, we should delete the associated locals:
|
180
|
+
ObjectSpace.define_finalizer(Fiber.current, thread_fiber_finalizer(locals.object_id))
|
181
|
+
end
|
182
|
+
|
183
|
+
locals
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
private_constant :AbstractLocals, :ThreadLocals, :FiberLocals
|
188
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require_relative 'fiber_local_var'
|
3
|
+
require_relative 'thread_local_var'
|
4
|
+
|
5
|
+
module Concurrent
|
6
|
+
# @!visibility private
|
7
|
+
def self.mutex_owned_per_thread?
|
8
|
+
return false if Concurrent.on_jruby? || Concurrent.on_truffleruby?
|
9
|
+
|
10
|
+
mutex = Mutex.new
|
11
|
+
# Lock the mutex:
|
12
|
+
mutex.synchronize do
|
13
|
+
# Check if the mutex is still owned in a child fiber:
|
14
|
+
Fiber.new { mutex.owned? }.resume
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if mutex_owned_per_thread?
|
19
|
+
LockLocalVar = ThreadLocalVar
|
20
|
+
else
|
21
|
+
LockLocalVar = FiberLocalVar
|
22
|
+
end
|
23
|
+
|
24
|
+
# Either {FiberLocalVar} or {ThreadLocalVar} depending on whether Mutex (and Monitor)
|
25
|
+
# are held, respectively, per Fiber or per Thread.
|
26
|
+
class LockLocalVar
|
27
|
+
end
|
28
|
+
end
|
@@ -1,16 +1,18 @@
|
|
1
|
-
require 'concurrent/synchronization'
|
1
|
+
require 'concurrent/synchronization/safe_initialization'
|
2
2
|
|
3
3
|
module Concurrent
|
4
4
|
|
5
5
|
# @!macro atomic_boolean
|
6
6
|
# @!visibility private
|
7
7
|
# @!macro internal_implementation_note
|
8
|
-
class MutexAtomicBoolean
|
8
|
+
class MutexAtomicBoolean
|
9
|
+
extend Concurrent::Synchronization::SafeInitialization
|
9
10
|
|
10
11
|
# @!macro atomic_boolean_method_initialize
|
11
12
|
def initialize(initial = false)
|
12
13
|
super()
|
13
|
-
|
14
|
+
@Lock = ::Mutex.new
|
15
|
+
@value = !!initial
|
14
16
|
end
|
15
17
|
|
16
18
|
# @!macro atomic_boolean_method_value_get
|
@@ -46,8 +48,12 @@ module Concurrent
|
|
46
48
|
protected
|
47
49
|
|
48
50
|
# @!visibility private
|
49
|
-
def
|
50
|
-
@
|
51
|
+
def synchronize
|
52
|
+
if @Lock.owned?
|
53
|
+
yield
|
54
|
+
else
|
55
|
+
@Lock.synchronize { yield }
|
56
|
+
end
|
51
57
|
end
|
52
58
|
|
53
59
|
private
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'concurrent/synchronization'
|
1
|
+
require 'concurrent/synchronization/safe_initialization'
|
2
2
|
require 'concurrent/utility/native_integer'
|
3
3
|
|
4
4
|
module Concurrent
|
@@ -6,12 +6,14 @@ module Concurrent
|
|
6
6
|
# @!macro atomic_fixnum
|
7
7
|
# @!visibility private
|
8
8
|
# @!macro internal_implementation_note
|
9
|
-
class MutexAtomicFixnum
|
9
|
+
class MutexAtomicFixnum
|
10
|
+
extend Concurrent::Synchronization::SafeInitialization
|
10
11
|
|
11
12
|
# @!macro atomic_fixnum_method_initialize
|
12
13
|
def initialize(initial = 0)
|
13
14
|
super()
|
14
|
-
|
15
|
+
@Lock = ::Mutex.new
|
16
|
+
ns_set(initial)
|
15
17
|
end
|
16
18
|
|
17
19
|
# @!macro atomic_fixnum_method_value_get
|
@@ -60,8 +62,12 @@ module Concurrent
|
|
60
62
|
protected
|
61
63
|
|
62
64
|
# @!visibility private
|
63
|
-
def
|
64
|
-
|
65
|
+
def synchronize
|
66
|
+
if @Lock.owned?
|
67
|
+
yield
|
68
|
+
else
|
69
|
+
@Lock.synchronize { yield }
|
70
|
+
end
|
65
71
|
end
|
66
72
|
|
67
73
|
private
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'concurrent/synchronization'
|
1
|
+
require 'concurrent/synchronization/lockable_object'
|
2
2
|
require 'concurrent/utility/native_integer'
|
3
3
|
|
4
4
|
module Concurrent
|
@@ -23,7 +23,14 @@ module Concurrent
|
|
23
23
|
|
24
24
|
synchronize do
|
25
25
|
try_acquire_timed(permits, nil)
|
26
|
-
|
26
|
+
end
|
27
|
+
|
28
|
+
return unless block_given?
|
29
|
+
|
30
|
+
begin
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
release(permits)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
@@ -48,13 +55,22 @@ module Concurrent
|
|
48
55
|
Utility::NativeInteger.ensure_integer_and_bounds permits
|
49
56
|
Utility::NativeInteger.ensure_positive permits
|
50
57
|
|
51
|
-
synchronize do
|
58
|
+
acquired = synchronize do
|
52
59
|
if timeout.nil?
|
53
60
|
try_acquire_now(permits)
|
54
61
|
else
|
55
62
|
try_acquire_timed(permits, timeout)
|
56
63
|
end
|
57
64
|
end
|
65
|
+
|
66
|
+
return acquired unless block_given?
|
67
|
+
return unless acquired
|
68
|
+
|
69
|
+
begin
|
70
|
+
yield
|
71
|
+
ensure
|
72
|
+
release(permits)
|
73
|
+
end
|
58
74
|
end
|
59
75
|
|
60
76
|
# @!macro semaphore_method_release
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'thread'
|
2
2
|
require 'concurrent/atomic/atomic_reference'
|
3
|
+
require 'concurrent/atomic/atomic_fixnum'
|
3
4
|
require 'concurrent/errors'
|
4
|
-
require 'concurrent/synchronization'
|
5
|
-
require 'concurrent/
|
5
|
+
require 'concurrent/synchronization/object'
|
6
|
+
require 'concurrent/synchronization/lock'
|
7
|
+
require 'concurrent/atomic/lock_local_var'
|
6
8
|
|
7
9
|
module Concurrent
|
8
10
|
|
@@ -109,7 +111,7 @@ module Concurrent
|
|
109
111
|
@Counter = AtomicFixnum.new(0) # single integer which represents lock state
|
110
112
|
@ReadQueue = Synchronization::Lock.new # used to queue waiting readers
|
111
113
|
@WriteQueue = Synchronization::Lock.new # used to queue waiting writers
|
112
|
-
@HeldCount =
|
114
|
+
@HeldCount = LockLocalVar.new(0) # indicates # of R & W locks held by this thread
|
113
115
|
end
|
114
116
|
|
115
117
|
# Execute a block operation within a read lock.
|
@@ -267,12 +269,10 @@ module Concurrent
|
|
267
269
|
# running right now, AND no writers who came before us still waiting to
|
268
270
|
# acquire the lock
|
269
271
|
# Additionally, if any read locks have been taken, we must hold all of them
|
270
|
-
if
|
271
|
-
# If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
|
272
|
-
|
273
|
-
|
274
|
-
return true
|
275
|
-
end
|
272
|
+
if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
|
273
|
+
# If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
|
274
|
+
@HeldCount.value = held + WRITE_LOCK_HELD
|
275
|
+
return true
|
276
276
|
elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
|
277
277
|
while true
|
278
278
|
# Now we have successfully incremented, so no more readers will be able to increment
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'concurrent/atomic/mutex_semaphore'
|
2
|
-
require 'concurrent/synchronization'
|
3
2
|
|
4
3
|
module Concurrent
|
5
4
|
|
@@ -11,19 +10,20 @@ module Concurrent
|
|
11
10
|
#
|
12
11
|
# @param [Fixnum] count the initial count
|
13
12
|
#
|
14
|
-
# @raise [ArgumentError] if `count` is not an integer
|
13
|
+
# @raise [ArgumentError] if `count` is not an integer
|
15
14
|
|
16
15
|
# @!macro semaphore_method_acquire
|
17
16
|
#
|
18
17
|
# Acquires the given number of permits from this semaphore,
|
19
|
-
# blocking until all are available.
|
18
|
+
# blocking until all are available. If a block is given,
|
19
|
+
# yields to it and releases the permits afterwards.
|
20
20
|
#
|
21
21
|
# @param [Fixnum] permits Number of permits to acquire
|
22
22
|
#
|
23
|
-
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
24
|
-
# one
|
23
|
+
# @raise [ArgumentError] if `permits` is not an integer or is less than zero
|
25
24
|
#
|
26
|
-
# @return [nil]
|
25
|
+
# @return [nil, BasicObject] Without a block, `nil` is returned. If a block
|
26
|
+
# is given, its return value is returned.
|
27
27
|
|
28
28
|
# @!macro semaphore_method_available_permits
|
29
29
|
#
|
@@ -41,18 +41,21 @@ module Concurrent
|
|
41
41
|
#
|
42
42
|
# Acquires the given number of permits from this semaphore,
|
43
43
|
# only if all are available at the time of invocation or within
|
44
|
-
# `timeout` interval
|
44
|
+
# `timeout` interval. If a block is given, yields to it if the permits
|
45
|
+
# were successfully acquired, and releases them afterward, returning the
|
46
|
+
# block's return value.
|
45
47
|
#
|
46
48
|
# @param [Fixnum] permits the number of permits to acquire
|
47
49
|
#
|
48
50
|
# @param [Fixnum] timeout the number of seconds to wait for the counter
|
49
51
|
# or `nil` to return immediately
|
50
52
|
#
|
51
|
-
# @raise [ArgumentError] if `permits` is not an integer or is less than
|
52
|
-
# one
|
53
|
+
# @raise [ArgumentError] if `permits` is not an integer or is less than zero
|
53
54
|
#
|
54
|
-
# @return [
|
55
|
-
# acquired a permit
|
55
|
+
# @return [true, false, nil, BasicObject] `false` if no permits are
|
56
|
+
# available, `true` when acquired a permit. If a block is given, the
|
57
|
+
# block's return value is returned if the permits were acquired; if not,
|
58
|
+
# `nil` is returned.
|
56
59
|
|
57
60
|
# @!macro semaphore_method_release
|
58
61
|
#
|
@@ -60,7 +63,7 @@ module Concurrent
|
|
60
63
|
#
|
61
64
|
# @param [Fixnum] permits Number of permits to return to the semaphore.
|
62
65
|
#
|
63
|
-
# @raise [ArgumentError] if `permits` is not a number or is less than
|
66
|
+
# @raise [ArgumentError] if `permits` is not a number or is less than zero
|
64
67
|
#
|
65
68
|
# @return [nil]
|
66
69
|
|
@@ -90,8 +93,8 @@ module Concurrent
|
|
90
93
|
|
91
94
|
# @!visibility private
|
92
95
|
# @!macro internal_implementation_note
|
93
|
-
SemaphoreImplementation =
|
94
|
-
|
96
|
+
SemaphoreImplementation = if Concurrent.on_jruby?
|
97
|
+
require 'concurrent/utility/native_extension_loader'
|
95
98
|
JavaSemaphore
|
96
99
|
else
|
97
100
|
MutexSemaphore
|
@@ -106,6 +109,8 @@ module Concurrent
|
|
106
109
|
# releasing a blocking acquirer.
|
107
110
|
# However, no actual permit objects are used; the Semaphore just keeps a
|
108
111
|
# count of the number available and acts accordingly.
|
112
|
+
# Alternatively, permits may be acquired within a block, and automatically
|
113
|
+
# released after the block finishes executing.
|
109
114
|
#
|
110
115
|
# @!macro semaphore_public_api
|
111
116
|
# @example
|
@@ -140,6 +145,19 @@ module Concurrent
|
|
140
145
|
# # Thread 4 releasing semaphore
|
141
146
|
# # Thread 1 acquired semaphore
|
142
147
|
#
|
148
|
+
# @example
|
149
|
+
# semaphore = Concurrent::Semaphore.new(1)
|
150
|
+
#
|
151
|
+
# puts semaphore.available_permits
|
152
|
+
# semaphore.acquire do
|
153
|
+
# puts semaphore.available_permits
|
154
|
+
# end
|
155
|
+
# puts semaphore.available_permits
|
156
|
+
#
|
157
|
+
# # prints:
|
158
|
+
# # 1
|
159
|
+
# # 0
|
160
|
+
# # 1
|
143
161
|
class Semaphore < SemaphoreImplementation
|
144
162
|
end
|
145
163
|
end
|