concurrent-ruby 1.1.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/Gemfile +2 -8
  4. data/README.md +49 -28
  5. data/Rakefile +59 -75
  6. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  9. data/lib/concurrent-ruby/concurrent/agent.rb +2 -1
  10. data/lib/concurrent-ruby/concurrent/array.rb +0 -10
  11. data/lib/concurrent-ruby/concurrent/async.rb +1 -0
  12. data/lib/concurrent-ruby/concurrent/atom.rb +1 -1
  13. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
  14. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
  15. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
  16. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +82 -151
  17. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
  18. data/lib/concurrent-ruby/concurrent/atomic/event.rb +3 -3
  19. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  20. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
  21. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +188 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
  24. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
  25. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
  26. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +19 -3
  27. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +2 -1
  28. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +9 -9
  29. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +32 -14
  30. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
  31. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +15 -4
  33. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
  34. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
  35. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
  36. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  37. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  38. data/lib/concurrent-ruby/concurrent/concern/logging.rb +86 -2
  39. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  40. data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
  41. data/lib/concurrent-ruby/concurrent/delay.rb +2 -2
  42. data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
  43. data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
  44. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +17 -14
  45. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +13 -3
  46. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +3 -3
  47. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +4 -0
  48. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +10 -4
  49. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +26 -37
  50. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +6 -6
  51. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
  52. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
  53. data/lib/concurrent-ruby/concurrent/hash.rb +0 -9
  54. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
  55. data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
  56. data/lib/concurrent-ruby/concurrent/map.rb +18 -8
  57. data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
  58. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
  59. data/lib/concurrent-ruby/concurrent/mvar.rb +1 -1
  60. data/lib/concurrent-ruby/concurrent/promise.rb +2 -1
  61. data/lib/concurrent-ruby/concurrent/promises.rb +7 -6
  62. data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
  63. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +30 -17
  64. data/lib/concurrent-ruby/concurrent/set.rb +12 -14
  65. data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
  66. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
  67. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
  68. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
  69. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  70. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
  71. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
  72. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +6 -5
  73. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +18 -5
  74. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +12 -44
  75. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  76. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
  77. data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
  78. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
  79. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +2 -39
  80. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +16 -27
  81. data/lib/concurrent-ruby/concurrent/timer_task.rb +11 -33
  82. data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
  83. data/lib/concurrent-ruby/concurrent/tvar.rb +22 -61
  84. data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
  85. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +7 -46
  86. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
  87. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
  88. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +36 -89
  89. data/lib/concurrent-ruby/concurrent/version.rb +1 -1
  90. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -1
  91. metadata +11 -12
  92. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  93. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
  94. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
  95. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
  96. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
  97. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -65
  98. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
  99. 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 < Synchronization::LockableObject
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
- synchronize { ns_initialize(initial) }
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 ns_initialize(initial)
50
- @value = !!initial
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 < Synchronization::LockableObject
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
- synchronize { ns_initialize(initial) }
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 ns_initialize(initial)
64
- ns_set(initial)
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
@@ -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
- nil
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,7 +1,8 @@
1
1
  require 'thread'
2
2
  require 'concurrent/atomic/atomic_fixnum'
3
3
  require 'concurrent/errors'
4
- require 'concurrent/synchronization'
4
+ require 'concurrent/synchronization/object'
5
+ require 'concurrent/synchronization/lock'
5
6
 
6
7
  module Concurrent
7
8
 
@@ -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/atomic/thread_local_var'
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 = ThreadLocalVar.new(0) # indicates # of R & W locks held by this thread
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 c == held
271
- # If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
272
- if @Counter.compare_and_set(c, c+RUNNING_WRITER)
273
- @HeldCount.value = held + WRITE_LOCK_HELD
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 or is less than zero
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 [Boolean] `false` if no permits are available, `true` when
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 one
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 = case
94
- when defined?(JavaSemaphore)
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