concurrent-ruby 0.7.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. data/LICENSE.txt +21 -0
  2. data/README.md +217 -0
  3. data/lib/concurrent.rb +45 -0
  4. data/lib/concurrent/actor.rb +104 -0
  5. data/lib/concurrent/actor/behaviour.rb +70 -0
  6. data/lib/concurrent/actor/behaviour/abstract.rb +48 -0
  7. data/lib/concurrent/actor/behaviour/awaits.rb +21 -0
  8. data/lib/concurrent/actor/behaviour/buffer.rb +54 -0
  9. data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +12 -0
  10. data/lib/concurrent/actor/behaviour/executes_context.rb +18 -0
  11. data/lib/concurrent/actor/behaviour/linking.rb +42 -0
  12. data/lib/concurrent/actor/behaviour/pausing.rb +77 -0
  13. data/lib/concurrent/actor/behaviour/removes_child.rb +16 -0
  14. data/lib/concurrent/actor/behaviour/sets_results.rb +36 -0
  15. data/lib/concurrent/actor/behaviour/supervised.rb +58 -0
  16. data/lib/concurrent/actor/behaviour/supervising.rb +34 -0
  17. data/lib/concurrent/actor/behaviour/terminates_children.rb +13 -0
  18. data/lib/concurrent/actor/behaviour/termination.rb +54 -0
  19. data/lib/concurrent/actor/context.rb +153 -0
  20. data/lib/concurrent/actor/core.rb +213 -0
  21. data/lib/concurrent/actor/default_dead_letter_handler.rb +9 -0
  22. data/lib/concurrent/actor/envelope.rb +41 -0
  23. data/lib/concurrent/actor/errors.rb +27 -0
  24. data/lib/concurrent/actor/internal_delegations.rb +49 -0
  25. data/lib/concurrent/actor/public_delegations.rb +40 -0
  26. data/lib/concurrent/actor/reference.rb +81 -0
  27. data/lib/concurrent/actor/root.rb +37 -0
  28. data/lib/concurrent/actor/type_check.rb +48 -0
  29. data/lib/concurrent/actor/utils.rb +10 -0
  30. data/lib/concurrent/actor/utils/ad_hoc.rb +21 -0
  31. data/lib/concurrent/actor/utils/balancer.rb +40 -0
  32. data/lib/concurrent/actor/utils/broadcast.rb +52 -0
  33. data/lib/concurrent/actor/utils/pool.rb +59 -0
  34. data/lib/concurrent/actress.rb +3 -0
  35. data/lib/concurrent/agent.rb +230 -0
  36. data/lib/concurrent/async.rb +284 -0
  37. data/lib/concurrent/atomic.rb +91 -0
  38. data/lib/concurrent/atomic/atomic_boolean.rb +202 -0
  39. data/lib/concurrent/atomic/atomic_fixnum.rb +203 -0
  40. data/lib/concurrent/atomic/condition.rb +67 -0
  41. data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
  42. data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
  43. data/lib/concurrent/atomic/count_down_latch.rb +116 -0
  44. data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
  45. data/lib/concurrent/atomic/event.rb +98 -0
  46. data/lib/concurrent/atomic/synchronization.rb +51 -0
  47. data/lib/concurrent/atomic/thread_local_var.rb +82 -0
  48. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +8 -0
  49. data/lib/concurrent/atomic_reference/direct_update.rb +50 -0
  50. data/lib/concurrent/atomic_reference/jruby.rb +14 -0
  51. data/lib/concurrent/atomic_reference/mutex_atomic.rb +77 -0
  52. data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +25 -0
  53. data/lib/concurrent/atomic_reference/rbx.rb +19 -0
  54. data/lib/concurrent/atomic_reference/ruby.rb +37 -0
  55. data/lib/concurrent/atomics.rb +11 -0
  56. data/lib/concurrent/channel/buffered_channel.rb +85 -0
  57. data/lib/concurrent/channel/channel.rb +41 -0
  58. data/lib/concurrent/channel/unbuffered_channel.rb +35 -0
  59. data/lib/concurrent/channel/waitable_list.rb +40 -0
  60. data/lib/concurrent/channels.rb +5 -0
  61. data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
  62. data/lib/concurrent/collection/priority_queue.rb +305 -0
  63. data/lib/concurrent/collection/ring_buffer.rb +59 -0
  64. data/lib/concurrent/collections.rb +3 -0
  65. data/lib/concurrent/configuration.rb +161 -0
  66. data/lib/concurrent/dataflow.rb +108 -0
  67. data/lib/concurrent/delay.rb +104 -0
  68. data/lib/concurrent/dereferenceable.rb +101 -0
  69. data/lib/concurrent/errors.rb +30 -0
  70. data/lib/concurrent/exchanger.rb +34 -0
  71. data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
  72. data/lib/concurrent/executor/executor.rb +282 -0
  73. data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
  74. data/lib/concurrent/executor/immediate_executor.rb +65 -0
  75. data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
  76. data/lib/concurrent/executor/java_fixed_thread_pool.rb +41 -0
  77. data/lib/concurrent/executor/java_single_thread_executor.rb +22 -0
  78. data/lib/concurrent/executor/java_thread_pool_executor.rb +180 -0
  79. data/lib/concurrent/executor/per_thread_executor.rb +100 -0
  80. data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
  81. data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
  82. data/lib/concurrent/executor/ruby_single_thread_executor.rb +74 -0
  83. data/lib/concurrent/executor/ruby_thread_pool_executor.rb +288 -0
  84. data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
  85. data/lib/concurrent/executor/safe_task_executor.rb +35 -0
  86. data/lib/concurrent/executor/serialized_execution.rb +126 -0
  87. data/lib/concurrent/executor/single_thread_executor.rb +35 -0
  88. data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
  89. data/lib/concurrent/executor/timer_set.rb +143 -0
  90. data/lib/concurrent/executors.rb +9 -0
  91. data/lib/concurrent/future.rb +125 -0
  92. data/lib/concurrent/ivar.rb +111 -0
  93. data/lib/concurrent/lazy_register.rb +58 -0
  94. data/lib/concurrent/logging.rb +17 -0
  95. data/lib/concurrent/mvar.rb +200 -0
  96. data/lib/concurrent/obligation.rb +171 -0
  97. data/lib/concurrent/observable.rb +40 -0
  98. data/lib/concurrent/options_parser.rb +48 -0
  99. data/lib/concurrent/promise.rb +170 -0
  100. data/lib/concurrent/scheduled_task.rb +79 -0
  101. data/lib/concurrent/timer_task.rb +341 -0
  102. data/lib/concurrent/tvar.rb +248 -0
  103. data/lib/concurrent/utilities.rb +3 -0
  104. data/lib/concurrent/utility/processor_count.rb +152 -0
  105. data/lib/concurrent/utility/timeout.rb +35 -0
  106. data/lib/concurrent/utility/timer.rb +21 -0
  107. data/lib/concurrent/version.rb +3 -0
  108. data/lib/concurrent_ruby.rb +1 -0
  109. data/lib/concurrent_ruby_ext.jar +0 -0
  110. data/lib/concurrent_ruby_ext.so +0 -0
  111. data/lib/extension_helper.rb +28 -0
  112. metadata +163 -0
@@ -0,0 +1,67 @@
1
+ module Concurrent
2
+
3
+ # Condition is a better implementation of standard Ruby ConditionVariable.
4
+ # The biggest difference is the wait return value: Condition#wait returns
5
+ # Condition::Result which make possible to know if waiting thread has been woken up
6
+ # by an another thread (using #signal or #broadcast) or due to timeout.
7
+ #
8
+ # Every #wait must be guarded by a locked Mutex or a ThreadError will be risen.
9
+ # Although it's not mandatory, it's recommended to call also #signal and #broadcast within
10
+ # the same mutex
11
+ class Condition
12
+
13
+ class Result
14
+ def initialize(remaining_time)
15
+ @remaining_time = remaining_time
16
+ end
17
+
18
+ attr_reader :remaining_time
19
+
20
+ # @return [Boolean] true if current thread has been waken up by a #signal or a #broadcast call, otherwise false
21
+ def woken_up?
22
+ @remaining_time.nil? || @remaining_time > 0
23
+ end
24
+
25
+ # @return [Boolean] true if current thread has been waken up due to a timeout, otherwise false
26
+ def timed_out?
27
+ @remaining_time != nil && @remaining_time <= 0
28
+ end
29
+
30
+ alias_method :can_wait?, :woken_up?
31
+
32
+ end
33
+
34
+ def initialize
35
+ @condition = ConditionVariable.new
36
+ end
37
+
38
+ # @param [Mutex] mutex the locked mutex guarding the wait
39
+ # @param [Object] timeout nil means no timeout
40
+ # @return [Result]
41
+ def wait(mutex, timeout = nil)
42
+ start_time = Time.now.to_f
43
+ @condition.wait(mutex, timeout)
44
+
45
+ if timeout.nil?
46
+ Result.new(nil)
47
+ else
48
+ Result.new(start_time + timeout - Time.now.to_f)
49
+ end
50
+ end
51
+
52
+ # Wakes up a waiting thread
53
+ # @return [true]
54
+ def signal
55
+ @condition.signal
56
+ true
57
+ end
58
+
59
+ # Wakes up all waiting threads
60
+ # @return [true]
61
+ def broadcast
62
+ @condition.broadcast
63
+ true
64
+ end
65
+
66
+ end
67
+ end
@@ -0,0 +1,118 @@
1
+ module Concurrent
2
+
3
+ # A thread safe observer set implemented using copy-on-read approach:
4
+ # observers are added and removed from a thread safe collection; every time
5
+ # a notification is required the internal data structure is copied to
6
+ # prevent concurrency issues
7
+ class CopyOnNotifyObserverSet
8
+
9
+ def initialize
10
+ @mutex = Mutex.new
11
+ @observers = {}
12
+ end
13
+
14
+ # Adds an observer to this set
15
+ # If a block is passed, the observer will be created by this method and no other params should be passed
16
+ # @param [Object] observer the observer to add
17
+ # @param [Symbol] func the function to call on the observer during notification. Default is :update
18
+ # @return [Object] the added observer
19
+ def add_observer(observer=nil, func=:update, &block)
20
+ if observer.nil? && block.nil?
21
+ raise ArgumentError, 'should pass observer as a first argument or block'
22
+ elsif observer && block
23
+ raise ArgumentError.new('cannot provide both an observer and a block')
24
+ end
25
+
26
+ if block
27
+ observer = block
28
+ func = :call
29
+ end
30
+
31
+ begin
32
+ @mutex.lock
33
+ @observers[observer] = func
34
+ ensure
35
+ @mutex.unlock
36
+ end
37
+
38
+ observer
39
+ end
40
+
41
+ # @param [Object] observer the observer to remove
42
+ # @return [Object] the deleted observer
43
+ def delete_observer(observer)
44
+ @mutex.lock
45
+ @observers.delete(observer)
46
+ @mutex.unlock
47
+
48
+ observer
49
+ end
50
+
51
+ # Deletes all observers
52
+ # @return [CopyOnWriteObserverSet] self
53
+ def delete_observers
54
+ @mutex.lock
55
+ @observers.clear
56
+ @mutex.unlock
57
+
58
+ self
59
+ end
60
+
61
+ # @return [Integer] the observers count
62
+ def count_observers
63
+ @mutex.lock
64
+ result = @observers.count
65
+ @mutex.unlock
66
+
67
+ result
68
+ end
69
+
70
+ # Notifies all registered observers with optional args
71
+ # @param [Object] args arguments to be passed to each observer
72
+ # @return [CopyOnWriteObserverSet] self
73
+ def notify_observers(*args, &block)
74
+ observers = duplicate_observers
75
+ notify_to(observers, *args, &block)
76
+
77
+ self
78
+ end
79
+
80
+ # Notifies all registered observers with optional args and deletes them.
81
+ #
82
+ # @param [Object] args arguments to be passed to each observer
83
+ # @return [CopyOnWriteObserverSet] self
84
+ def notify_and_delete_observers(*args, &block)
85
+ observers = duplicate_and_clear_observers
86
+ notify_to(observers, *args, &block)
87
+
88
+ self
89
+ end
90
+
91
+ private
92
+
93
+ def duplicate_and_clear_observers
94
+ @mutex.lock
95
+ observers = @observers.dup
96
+ @observers.clear
97
+ @mutex.unlock
98
+
99
+ observers
100
+ end
101
+
102
+ def duplicate_observers
103
+ @mutex.lock
104
+ observers = @observers.dup
105
+ @mutex.unlock
106
+
107
+ observers
108
+ end
109
+
110
+ def notify_to(observers, *args)
111
+ raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
112
+ observers.each do |observer, function|
113
+ args = yield if block_given?
114
+ observer.send(function, *args)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,117 @@
1
+ module Concurrent
2
+
3
+ # A thread safe observer set implemented using copy-on-write approach:
4
+ # every time an observer is added or removed the whole internal data structure is
5
+ # duplicated and replaced with a new one.
6
+ class CopyOnWriteObserverSet
7
+
8
+ def initialize
9
+ @mutex = Mutex.new
10
+ @observers = {}
11
+ end
12
+
13
+ # Adds an observer to this set
14
+ # If a block is passed, the observer will be created by this method and no other params should be passed
15
+ # @param [Object] observer the observer to add
16
+ # @param [Symbol] func the function to call on the observer during notification. Default is :update
17
+ # @return [Object] the added observer
18
+ def add_observer(observer=nil, func=:update, &block)
19
+ if observer.nil? && block.nil?
20
+ raise ArgumentError, 'should pass observer as a first argument or block'
21
+ elsif observer && block
22
+ raise ArgumentError.new('cannot provide both an observer and a block')
23
+ end
24
+
25
+ if block
26
+ observer = block
27
+ func = :call
28
+ end
29
+
30
+ begin
31
+ @mutex.lock
32
+ new_observers = @observers.dup
33
+ new_observers[observer] = func
34
+ @observers = new_observers
35
+ observer
36
+ ensure
37
+ @mutex.unlock
38
+ end
39
+ end
40
+
41
+ # @param [Object] observer the observer to remove
42
+ # @return [Object] the deleted observer
43
+ def delete_observer(observer)
44
+ @mutex.lock
45
+ new_observers = @observers.dup
46
+ new_observers.delete(observer)
47
+ @observers = new_observers
48
+ observer
49
+ ensure
50
+ @mutex.unlock
51
+ end
52
+
53
+ # Deletes all observers
54
+ # @return [CopyOnWriteObserverSet] self
55
+ def delete_observers
56
+ self.observers = {}
57
+ self
58
+ end
59
+
60
+
61
+ # @return [Integer] the observers count
62
+ def count_observers
63
+ observers.count
64
+ end
65
+
66
+ # Notifies all registered observers with optional args
67
+ # @param [Object] args arguments to be passed to each observer
68
+ # @return [CopyOnWriteObserverSet] self
69
+ def notify_observers(*args, &block)
70
+ notify_to(observers, *args, &block)
71
+ self
72
+ end
73
+
74
+ # Notifies all registered observers with optional args and deletes them.
75
+ #
76
+ # @param [Object] args arguments to be passed to each observer
77
+ # @return [CopyOnWriteObserverSet] self
78
+ def notify_and_delete_observers(*args, &block)
79
+ old = clear_observers_and_return_old
80
+ notify_to(old, *args, &block)
81
+ self
82
+ end
83
+
84
+ private
85
+
86
+ def notify_to(observers, *args)
87
+ raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
88
+ observers.each do |observer, function|
89
+ args = yield if block_given?
90
+ observer.send(function, *args)
91
+ end
92
+ end
93
+
94
+ def observers
95
+ @mutex.lock
96
+ @observers
97
+ ensure
98
+ @mutex.unlock
99
+ end
100
+
101
+ def observers=(new_set)
102
+ @mutex.lock
103
+ @observers = new_set
104
+ ensure
105
+ @mutex.unlock
106
+ end
107
+
108
+ def clear_observers_and_return_old
109
+ @mutex.lock
110
+ old_observers = @observers
111
+ @observers = {}
112
+ old_observers
113
+ ensure
114
+ @mutex.unlock
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,116 @@
1
+ require 'concurrent/atomic/condition'
2
+
3
+ module Concurrent
4
+
5
+ # @!macro [attach] count_down_latch
6
+ #
7
+ # A synchronization object that allows one thread to wait on multiple other threads.
8
+ # The thread that will wait creates a `CountDownLatch` and sets the initial value
9
+ # (normally equal to the number of other threads). The initiating thread passes the
10
+ # latch to the other threads then waits for the other threads by calling the `#wait`
11
+ # method. Each of the other threads calls `#count_down` when done with its work.
12
+ # When the latch counter reaches zero the waiting thread is unblocked and continues
13
+ # with its work. A `CountDownLatch` can be used only once. Its value cannot be reset.
14
+ class MutexCountDownLatch
15
+
16
+ # @!macro [attach] count_down_latch_method_initialize
17
+ #
18
+ # Create a new `CountDownLatch` with the initial `count`.
19
+ #
20
+ # @param [Fixnum] count the initial count
21
+ #
22
+ # @raise [ArgumentError] if `count` is not an integer or is less than zero
23
+ def initialize(count)
24
+ unless count.is_a?(Fixnum) && count >= 0
25
+ raise ArgumentError.new('count must be in integer greater than or equal zero')
26
+ end
27
+ @mutex = Mutex.new
28
+ @condition = Condition.new
29
+ @count = count
30
+ end
31
+
32
+ # @!macro [attach] count_down_latch_method_wait
33
+ #
34
+ # Block on the latch until the counter reaches zero or until `timeout` is reached.
35
+ #
36
+ # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil`
37
+ # to block indefinitely
38
+ # @return [Boolean] `true` if the `count` reaches zero else false on `timeout`
39
+ def wait(timeout = nil)
40
+ @mutex.synchronize do
41
+
42
+ remaining = Condition::Result.new(timeout)
43
+ while @count > 0 && remaining.can_wait?
44
+ remaining = @condition.wait(@mutex, remaining.remaining_time)
45
+ end
46
+
47
+ @count == 0
48
+ end
49
+ end
50
+
51
+ # @!macro [attach] count_down_latch_method_count_down
52
+ #
53
+ # Signal the latch to decrement the counter. Will signal all blocked threads when
54
+ # the `count` reaches zero.
55
+ def count_down
56
+ @mutex.synchronize do
57
+ @count -= 1 if @count > 0
58
+ @condition.broadcast if @count == 0
59
+ end
60
+ end
61
+
62
+ # @!macro [attach] count_down_latch_method_count
63
+ #
64
+ # The current value of the counter.
65
+ #
66
+ # @return [Fixnum] the current value of the counter
67
+ def count
68
+ @mutex.synchronize { @count }
69
+ end
70
+ end
71
+
72
+ if RUBY_PLATFORM == 'java'
73
+
74
+ # @!macro count_down_latch
75
+ class JavaCountDownLatch
76
+
77
+ # @!macro count_down_latch_method_initialize
78
+ def initialize(count)
79
+ unless count.is_a?(Fixnum) && count >= 0
80
+ raise ArgumentError.new('count must be in integer greater than or equal zero')
81
+ end
82
+ @latch = java.util.concurrent.CountDownLatch.new(count)
83
+ end
84
+
85
+ # @!macro count_down_latch_method_wait
86
+ def wait(timeout = nil)
87
+ if timeout.nil?
88
+ @latch.await
89
+ true
90
+ else
91
+ @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
92
+ end
93
+ end
94
+
95
+ # @!macro count_down_latch_method_count_down
96
+ def count_down
97
+ @latch.countDown
98
+ end
99
+
100
+ # @!macro count_down_latch_method_count
101
+ def count
102
+ @latch.getCount
103
+ end
104
+ end
105
+
106
+ # @!macro count_down_latch
107
+ class CountDownLatch < JavaCountDownLatch
108
+ end
109
+
110
+ else
111
+
112
+ # @!macro count_down_latch
113
+ class CountDownLatch < MutexCountDownLatch
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,106 @@
1
+ module Concurrent
2
+
3
+ class CyclicBarrier
4
+
5
+ Generation = Struct.new(:status)
6
+ private_constant :Generation
7
+
8
+ # Create a new `CyclicBarrier` that waits for `parties` threads
9
+ #
10
+ # @param [Fixnum] parties the number of parties
11
+ # @yield an optional block that will be executed that will be executed after the last thread arrives and before the others are released
12
+ #
13
+ # @raise [ArgumentError] if `parties` is not an integer or is less than zero
14
+ def initialize(parties, &block)
15
+ raise ArgumentError.new('count must be in integer greater than or equal zero') if !parties.is_a?(Fixnum) || parties < 1
16
+ @parties = parties
17
+ @mutex = Mutex.new
18
+ @condition = Condition.new
19
+ @number_waiting = 0
20
+ @action = block
21
+ @generation = Generation.new(:waiting)
22
+ end
23
+
24
+ # @return [Fixnum] the number of threads needed to pass the barrier
25
+ def parties
26
+ @parties
27
+ end
28
+
29
+ # @return [Fixnum] the number of threads currently waiting on the barrier
30
+ def number_waiting
31
+ @number_waiting
32
+ end
33
+
34
+ # Blocks on the barrier until the number of waiting threads is equal to `parties` or until `timeout` is reached or `reset` is called
35
+ # If a block has been passed to the constructor, it will be executed once by the last arrived thread before releasing the others
36
+ # @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` to block indefinitely
37
+ # @return [Boolean] `true` if the `count` reaches zero else false on `timeout` or on `reset` or if the barrier is broken
38
+ def wait(timeout = nil)
39
+ @mutex.synchronize do
40
+
41
+ return false unless @generation.status == :waiting
42
+
43
+ @number_waiting += 1
44
+
45
+ if @number_waiting == @parties
46
+ @action.call if @action
47
+ set_status_and_restore(:fulfilled)
48
+ true
49
+ else
50
+ wait_for_wake_up(@generation, timeout)
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+
57
+ # resets the barrier to its initial state
58
+ # If there is at least one waiting thread, it will be woken up, the `wait` method will return false and the barrier will be broken
59
+ # If the barrier is broken, this method restores it to the original state
60
+ #
61
+ # @return [nil]
62
+ def reset
63
+ @mutex.synchronize do
64
+ set_status_and_restore(:reset)
65
+ end
66
+ end
67
+
68
+ # A barrier can be broken when:
69
+ # - a thread called the `reset` method while at least one other thread was waiting
70
+ # - at least one thread timed out on `wait` method
71
+ #
72
+ # A broken barrier can be restored using `reset` it's safer to create a new one
73
+ # @return [Boolean] true if the barrier is broken otherwise false
74
+ def broken?
75
+ @mutex.synchronize { @generation.status != :waiting }
76
+ end
77
+
78
+ private
79
+
80
+ def set_status_and_restore(new_status)
81
+ @generation.status = new_status
82
+ @condition.broadcast
83
+ @generation = Generation.new(:waiting)
84
+ @number_waiting = 0
85
+ end
86
+
87
+ def wait_for_wake_up(generation, timeout)
88
+ if wait_while_waiting(generation, timeout)
89
+ generation.status == :fulfilled
90
+ else
91
+ generation.status = :broken
92
+ @condition.broadcast
93
+ false
94
+ end
95
+ end
96
+
97
+ def wait_while_waiting(generation, timeout)
98
+ remaining = Condition::Result.new(timeout)
99
+ while generation.status == :waiting && remaining.can_wait?
100
+ remaining = @condition.wait(@mutex, remaining.remaining_time)
101
+ end
102
+ remaining.woken_up?
103
+ end
104
+
105
+ end
106
+ end