farce 0.4.1-java → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +80 -2
- data/README.md +3 -1
- data/docs/benchmarks.md +37 -21
- data/docs/compatibility.md +13 -0
- data/docs/data-structures.md +37 -0
- data/docs/integrations.md +1 -0
- data/docs/introduction.md +19 -0
- data/docs/modes.md +2 -0
- data/docs/scheduling.md +35 -3
- data/docs/scopes.md +3 -1
- data/docs/shims.md +53 -0
- data/docs/utilities.md +31 -0
- data/docs/variants.md +9 -0
- data/lib/farce/abstract/concurrent_map.rb +122 -0
- data/lib/farce/abstract/counter.rb +35 -1
- data/lib/farce/abstract/executor.rb +30 -7
- data/lib/farce/abstract/flag.rb +12 -0
- data/lib/farce/abstract/pool.rb +158 -47
- data/lib/farce/abstract/priority_queue.rb +1 -1
- data/lib/farce/abstract/publisher.rb +189 -0
- data/lib/farce/abstract/queue.rb +29 -3
- data/lib/farce/abstract/scheduler.rb +41 -14
- data/lib/farce/abstract/timer_queue.rb +3 -3
- data/lib/farce/abstract/vector.rb +3 -1
- data/lib/farce/atom.rb +9 -2
- data/lib/farce/clock.rb +12 -0
- data/lib/farce/concurrent/count_down_latch.rb +37 -0
- data/lib/farce/concurrent/cyclic_barrier.rb +109 -0
- data/lib/farce/concurrent/event.rb +60 -0
- data/lib/farce/concurrent/fiber_local_var.rb +31 -0
- data/lib/farce/concurrent/local_var.rb +65 -0
- data/lib/farce/concurrent/thread_local_var.rb +28 -0
- data/lib/farce/concurrent/tuple.rb +74 -0
- data/lib/farce/concurrent.rb +50 -0
- data/lib/farce/config.rb +112 -2
- data/lib/farce/counter.rb +0 -16
- data/lib/farce/engine/jruby/map.rb +8 -6
- data/lib/farce/engine/jruby/signal.rb +2 -2
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/priority_queue.rb +14 -4
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -1
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +1 -4
- data/lib/farce/engine/ruby/shared/weak_map.rb +9 -12
- data/lib/farce/engine/shared/exchanger.rb +2 -2
- data/lib/farce/engine/shared/queue.rb +43 -5
- data/lib/farce/engine/shared/ractor_selector.rb +1 -1
- data/lib/farce/engine/shared/strict_map.rb +5 -0
- data/lib/farce/engine/shared/unshared_atom.rb +1 -1
- data/lib/farce/engine/shared/unshared_vector.rb +8 -7
- data/lib/farce/engine/shared/unshared_weak_map.rb +8 -6
- data/lib/farce/engine/shared/weak_atom/base.rb +5 -10
- data/lib/farce/engine/shared/weak_map/cell.rb +1 -1
- data/lib/farce/engine/truffleruby/map.rb +8 -6
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +18 -4
- data/lib/farce/engine/truffleruby/signal.rb +1 -1
- data/lib/farce/integrations/concurrent.rb +16 -3
- data/lib/farce/internal/blocking_priority_queue.rb +4 -9
- data/lib/farce/internal/flag_waiting.rb +1 -1
- data/lib/farce/internal/lease_initialization.rb +1 -1
- data/lib/farce/internal/lease_pool_state.rb +1 -1
- data/lib/farce/internal/lease_state.rb +2 -2
- data/lib/farce/internal/local_executor_methods.rb +3 -3
- data/lib/farce/internal/map_value_modes.rb +9 -2
- data/lib/farce/internal/pool_settings.rb +9 -0
- data/lib/farce/internal/pool_worker.rb +34 -21
- data/lib/farce/internal/reservation_waiting.rb +1 -1
- data/lib/farce/internal/scheduler_lifecycle.rb +2 -2
- data/lib/farce/internal/weak_map_value_modes.rb +5 -0
- data/lib/farce/internal.rb +8 -8
- data/lib/farce/lazy.rb +3 -0
- data/lib/farce/lazy_ref.rb +3 -0
- data/lib/farce/local/lease.rb +2 -9
- data/lib/farce/local/lease_map.rb +2 -9
- data/lib/farce/local/pool.rb +38 -5
- data/lib/farce/local/publisher.rb +30 -0
- data/lib/farce/local/scheduler.rb +18 -5
- data/lib/farce/local/vector.rb +3 -2
- data/lib/farce/memoize.rb +314 -0
- data/lib/farce/pool.rb +5 -0
- data/lib/farce/priority_queue.rb +1 -1
- data/lib/farce/publisher.rb +57 -0
- data/lib/farce/signal.rb +1 -1
- data/lib/farce/strict/publisher.rb +31 -0
- data/lib/farce/strict/vector.rb +3 -2
- data/lib/farce/subscription/definitions.rb +27 -0
- data/lib/farce/subscription.rb +121 -0
- data/lib/farce/transaction/map.rb +35 -1
- data/lib/farce/unshared/pool.rb +1 -1
- data/lib/farce/unshared/publisher.rb +25 -0
- data/lib/farce/unshared/scheduler.rb +3 -3
- data/lib/farce/unshared/vector.rb +3 -2
- data/lib/farce/vector.rb +12 -9
- data/lib/farce/version.rb +1 -1
- data/lib/farce/walker/definitions.rb +13 -2
- data/lib/farce/weak_atom.rb +5 -2
- data/lib/farce.rb +3 -1
- metadata +19 -2
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
module Farce
|
|
6
6
|
module Abstract
|
|
7
7
|
# @abstract Super class for {Map maps} with added concurrency features.
|
|
8
|
+
#
|
|
9
|
+
# Maps implement most of the interface provided by Hash,
|
|
10
|
+
# as well as `Concurrent::Map` from the `concurrent-ruby` gem.
|
|
8
11
|
class ConcurrentMap < Map
|
|
9
12
|
include DuplicableMap
|
|
10
13
|
|
|
@@ -34,6 +37,24 @@ module Farce
|
|
|
34
37
|
end
|
|
35
38
|
end
|
|
36
39
|
|
|
40
|
+
# Return an existing value or store a default for an absent key.
|
|
41
|
+
# A block takes precedence over the default and receives the original key.
|
|
42
|
+
# The lookup and store are separate operations. Concurrent writes can be overwritten.
|
|
43
|
+
# @param key [BasicObject] The key to look up or store.
|
|
44
|
+
# @param defaults [Array<BasicObject>] At most one default value to store when the key is absent.
|
|
45
|
+
# @see #store For supported transfer and timeout options.
|
|
46
|
+
# @yieldparam key [BasicObject] The original key.
|
|
47
|
+
# @yieldreturn [BasicObject] The value to store.
|
|
48
|
+
# @return [BasicObject, nil] The existing or newly stored value.
|
|
49
|
+
# @raise [KeyError] If the key is absent and neither a default nor a block is provided.
|
|
50
|
+
def fetch_or_store(key, *defaults, **)
|
|
51
|
+
if defaults.size > 1
|
|
52
|
+
raise ArgumentError, "wrong number of arguments (given #{defaults.size + 1}, expected 1..2)"
|
|
53
|
+
end
|
|
54
|
+
return fetch(key) if defaults.empty? && !block_given?
|
|
55
|
+
fetch(key) { store(key, block_given? ? yield(key) : defaults.first, **) }
|
|
56
|
+
end
|
|
57
|
+
|
|
37
58
|
# Read the value associated with a key, waiting for atomic-update access if necessary.
|
|
38
59
|
# @param key [BasicObject] The key to look up.
|
|
39
60
|
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
@@ -43,6 +64,12 @@ module Farce
|
|
|
43
64
|
# The associated value, nil if the key is absent, or the fallback result if the operation times out.
|
|
44
65
|
def get(key, timeout: nil, &) = internal_map.get(key, timeout:, &)
|
|
45
66
|
|
|
67
|
+
# Sets a value and returns the previous value.
|
|
68
|
+
# @param key [BasicObject] The key to update.
|
|
69
|
+
# @param value [BasicObject] The value to store.
|
|
70
|
+
# @return [BasicObject, nil] The previous value, or nil if the key was absent.
|
|
71
|
+
def get_and_set(...) = swap(...)
|
|
72
|
+
|
|
46
73
|
# Associate a value with a key, waiting for atomic-update access if necessary.
|
|
47
74
|
# @param key [BasicObject] The key to store.
|
|
48
75
|
# @param value [BasicObject] The value to store.
|
|
@@ -52,6 +79,9 @@ module Farce
|
|
|
52
79
|
# @return [BasicObject, nil] `value` on success, or the fallback result or nil on timeout.
|
|
53
80
|
def store(key, value, timeout: nil, &) = internal_map.store(key, value, timeout:, &)
|
|
54
81
|
|
|
82
|
+
# (see #store)
|
|
83
|
+
def put(...) = store(...)
|
|
84
|
+
|
|
55
85
|
# Replace the value associated with a key and return its previous value.
|
|
56
86
|
# @param key [BasicObject] The key to update.
|
|
57
87
|
# @param replacement [BasicObject] The value to store.
|
|
@@ -71,6 +101,69 @@ module Farce
|
|
|
71
101
|
# @raise [LocalJumpError] If no block is given.
|
|
72
102
|
def store_if_absent(key, timeout: nil, &) = internal_map.store_if_absent(key, timeout:, &)
|
|
73
103
|
|
|
104
|
+
# (see #store_if_absent)
|
|
105
|
+
def compute_if_absent(...) = store_if_absent(...)
|
|
106
|
+
|
|
107
|
+
# Atomically store a value only when the key is absent.
|
|
108
|
+
# Existing nil and false values are preserved.
|
|
109
|
+
# @param key [BasicObject] The key to look up or store.
|
|
110
|
+
# @param value [BasicObject] The value to store when the key is absent.
|
|
111
|
+
# @see #store_if_absent For supported transfer and timeout options.
|
|
112
|
+
# @return [BasicObject, nil] The existing value, or nil when a value was inserted or access timed out.
|
|
113
|
+
def put_if_absent(key, value, **)
|
|
114
|
+
inserted = false
|
|
115
|
+
previous = store_if_absent(key, **) do
|
|
116
|
+
inserted = true
|
|
117
|
+
value
|
|
118
|
+
end
|
|
119
|
+
inserted ? nil : previous
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Atomically replace an existing value and return its previous value.
|
|
123
|
+
# An absent key is left unchanged. Existing nil and false values can be replaced.
|
|
124
|
+
# @param key [BasicObject] The key to update.
|
|
125
|
+
# @param replacement [BasicObject] The value to store.
|
|
126
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
127
|
+
# @see #store For supported transfer options.
|
|
128
|
+
# @return [BasicObject, nil] The previous value, or nil when the key is absent or access timed out.
|
|
129
|
+
def replace_if_exists(key, replacement, timeout: nil, **)
|
|
130
|
+
map = internal_map
|
|
131
|
+
previous = nil
|
|
132
|
+
changed = map.modify(key, timeout:) do |present, value|
|
|
133
|
+
next Internal::MAP_KEEP unless present
|
|
134
|
+
previous = value
|
|
135
|
+
map.check_mutation
|
|
136
|
+
wrap_value(replacement, **)
|
|
137
|
+
end
|
|
138
|
+
unwrap_value(previous) if changed
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Atomically replace the value associated with a key if it is present.
|
|
142
|
+
# The block receives the current value and should return the new value to store.
|
|
143
|
+
# The block is not called for an absent key. A stored nil is passed to the block.
|
|
144
|
+
# @param key [BasicObject] The key to update.
|
|
145
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
146
|
+
# @yield [value] Called with the current value if the key is present.
|
|
147
|
+
# @yieldparam value [BasicObject] The current value.
|
|
148
|
+
# @yieldreturn [BasicObject] The new value to store.
|
|
149
|
+
# @return [BasicObject, nil] The newly stored value, or nil if the key was absent or the operation timed out.
|
|
150
|
+
# @raise [LocalJumpError] If no block is given.
|
|
151
|
+
def store_if_present(key, timeout: nil)
|
|
152
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
153
|
+
map = internal_map
|
|
154
|
+
result = nil
|
|
155
|
+
changed = map.modify(key, timeout:) do |present, value|
|
|
156
|
+
next Internal::MAP_KEEP unless present
|
|
157
|
+
replacement = yield(unwrap_value(value))
|
|
158
|
+
map.check_mutation
|
|
159
|
+
result = wrap_value(replacement)
|
|
160
|
+
end
|
|
161
|
+
unwrap_value(result) if changed
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# (see #store_if_present)
|
|
165
|
+
def compute_if_present(...) = store_if_present(...)
|
|
166
|
+
|
|
74
167
|
# Atomically replace a value if the key is present and its current value matches `expected`.
|
|
75
168
|
# The configured value-comparison mode determines whether matching uses equality or identity.
|
|
76
169
|
# @param key [BasicObject] The key to update.
|
|
@@ -82,6 +175,23 @@ module Farce
|
|
|
82
175
|
internal_map.compare_and_set(key, expected, replacement, timeout:)
|
|
83
176
|
end
|
|
84
177
|
|
|
178
|
+
# (see #compare_and_set)
|
|
179
|
+
def replace_pair(...) = compare_and_set(...)
|
|
180
|
+
|
|
181
|
+
# Atomically remove a key if its current value matches `expected`.
|
|
182
|
+
# The configured value-comparison mode determines whether matching uses equality or identity.
|
|
183
|
+
# Missing keys and mismatched values are left unchanged.
|
|
184
|
+
# @param key [BasicObject] The key to remove.
|
|
185
|
+
# @param expected [BasicObject] The expected current value.
|
|
186
|
+
# @param timeout [Numeric, nil] The maximum number of seconds to wait for access.
|
|
187
|
+
# @return [Boolean] Whether the entry was removed. Returns false on timeout.
|
|
188
|
+
def delete_pair(key, expected, timeout: nil)
|
|
189
|
+
expected = wrap_comparison(expected)
|
|
190
|
+
internal_map.modify(key, timeout:) do |present, value|
|
|
191
|
+
present && values_equal?(value, expected) ? Internal::MAP_DELETE : Internal::MAP_KEEP
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
85
195
|
# Atomically replace the value associated with a key with the block result.
|
|
86
196
|
# The block receives nil when the key is absent or its current value is nil.
|
|
87
197
|
# @param key [BasicObject] The key to update.
|
|
@@ -93,6 +203,9 @@ module Farce
|
|
|
93
203
|
# @raise [LocalJumpError] If no block is given.
|
|
94
204
|
def update(key, timeout: nil, &) = internal_map.update(key, timeout:, &)
|
|
95
205
|
|
|
206
|
+
# (see #update)
|
|
207
|
+
def compute(...) = update(...)
|
|
208
|
+
|
|
96
209
|
# Atomically insert `initial` for an absent key or replace an existing value with the block result.
|
|
97
210
|
# The block is not called when the key is absent.
|
|
98
211
|
# @param key [BasicObject] The key to insert or update.
|
|
@@ -105,6 +218,9 @@ module Farce
|
|
|
105
218
|
# @raise [LocalJumpError] If no block is given.
|
|
106
219
|
def upsert(key, initial, timeout: nil, &) = internal_map.upsert(key, initial, timeout:, &)
|
|
107
220
|
|
|
221
|
+
# (see #upsert)
|
|
222
|
+
def merge_pair(...) = upsert(...)
|
|
223
|
+
|
|
108
224
|
# Wait until a block condition matches the current value.
|
|
109
225
|
# One timeout budget covers all checks and waits. The block is not interrupted.
|
|
110
226
|
# @yieldparam value [BasicObject, nil] the current value
|
|
@@ -344,6 +460,12 @@ module Farce
|
|
|
344
460
|
|
|
345
461
|
private
|
|
346
462
|
|
|
463
|
+
def wrap_comparison(value) = value
|
|
464
|
+
|
|
465
|
+
def values_equal?(left, right)
|
|
466
|
+
compare_values_by_identity? ? left.equal?(right) : left == right
|
|
467
|
+
end
|
|
468
|
+
|
|
347
469
|
def modify_entry(key, canonical: false)
|
|
348
470
|
map = internal_map
|
|
349
471
|
map = map.instance_variable_get(:@map) if canonical && map.is_a?(Internal::KeyNormalizer::ConcurrentMap)
|
|
@@ -7,6 +7,22 @@ module Farce
|
|
|
7
7
|
# @abstract Common numeric interface and atomic operations for counters.
|
|
8
8
|
# @note This is a module rather than a class so {Farce::Counter} can inherit
|
|
9
9
|
# directly from the native counter and avoid delegation overhead.
|
|
10
|
+
#
|
|
11
|
+
# @!method initial
|
|
12
|
+
# @return [Integer] The initial value retained by reset and copies.
|
|
13
|
+
#
|
|
14
|
+
# @!method value
|
|
15
|
+
# @return [Integer] The current value of the counter.
|
|
16
|
+
#
|
|
17
|
+
# @!method increment(by = 1)
|
|
18
|
+
# Increment the counter by the given amount (default is 1).
|
|
19
|
+
# @param [Numeric, String, #to_int] by The amount, converted to an Integer.
|
|
20
|
+
# @return [self] Returns self for chaining.
|
|
21
|
+
#
|
|
22
|
+
# @!method decrement(by = 1)
|
|
23
|
+
# Decrement the counter by the given amount (default is 1).
|
|
24
|
+
# @param [Numeric, String, #to_int] by The amount, converted to an Integer.
|
|
25
|
+
# @return [self] Returns self for chaining.
|
|
10
26
|
module Counter
|
|
11
27
|
include Internal::MarshalSupport::Counter
|
|
12
28
|
include Internal::Copyable
|
|
@@ -24,6 +40,24 @@ module Farce
|
|
|
24
40
|
include Value
|
|
25
41
|
include Internal::ValueSerialization
|
|
26
42
|
|
|
43
|
+
# Pass the current value to the given block, replacing it with the block's result.
|
|
44
|
+
# May retry if the value changes during the block's execution.
|
|
45
|
+
#
|
|
46
|
+
# Provided for compatibility with `Concurrent::AtomicFixnum`.
|
|
47
|
+
#
|
|
48
|
+
# @yield [count] Calculate the new value based on the current value.
|
|
49
|
+
# @yieldparam count [Integer] the current value of the atomic fixnum.
|
|
50
|
+
# @yieldreturn [Integer] the new value to replace the current value.
|
|
51
|
+
# @return [Integer] the new value after the block has been applied.
|
|
52
|
+
def update
|
|
53
|
+
raise LocalJumpError, "no block given" unless block_given?
|
|
54
|
+
while true
|
|
55
|
+
current = value
|
|
56
|
+
new_value = yield(current)
|
|
57
|
+
return new_value if compare_and_set(current, new_value)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
27
61
|
# Wait until the current value differs from the expected value.
|
|
28
62
|
# @param expected [Integer] the value to wait to change from
|
|
29
63
|
# @param timeout [Numeric, nil] the total seconds available
|
|
@@ -35,7 +69,7 @@ module Farce
|
|
|
35
69
|
observed = signal.generation
|
|
36
70
|
current = value
|
|
37
71
|
return current unless expected == current
|
|
38
|
-
break unless signal.wait(observed, timeout:
|
|
72
|
+
break unless signal.wait(observed, timeout: Clock.remaining(deadline))
|
|
39
73
|
end
|
|
40
74
|
yield if block_given?
|
|
41
75
|
end
|
|
@@ -12,7 +12,7 @@ module Farce
|
|
|
12
12
|
#
|
|
13
13
|
# @abstract Use a scheduler or pool class.
|
|
14
14
|
#
|
|
15
|
-
# @!method schedule(*args, mode: :smart, auto_local: true)
|
|
15
|
+
# @!method schedule(*args, mode: :smart, auto_local: true, timeout: nil)
|
|
16
16
|
# @abstract This method must be implemented by concrete executor subclasses.
|
|
17
17
|
#
|
|
18
18
|
# Schedules a block for execution.
|
|
@@ -36,12 +36,18 @@ module Farce
|
|
|
36
36
|
# Whether to automatically use a local mode when possible. Defaults to `true`.
|
|
37
37
|
# May be ignored by some executors.
|
|
38
38
|
#
|
|
39
|
+
# @param timeout [Numeric, nil]
|
|
40
|
+
# Maximum seconds to wait for ownership and queue space. Nil waits indefinitely.
|
|
41
|
+
# Zero only submits if no waiting is needed. Must be finite and non-negative.
|
|
42
|
+
# An expired attempt returns `nil` and does not enqueue the task.
|
|
43
|
+
# This does not limit task execution time. Executors without a queue may ignore it.
|
|
44
|
+
#
|
|
39
45
|
# @yield [*args] The block to be executed by the scheduler.
|
|
40
46
|
# @yieldparam args [Array] The arguments passed to the block.
|
|
41
47
|
# @yieldreceiver [BasicObject, nil]
|
|
42
48
|
# Local mode will not touch the block's binding (self stays the same), but other modes may change it to `nil`.
|
|
43
49
|
#
|
|
44
|
-
# @return [self]
|
|
50
|
+
# @return [self, nil] self when submitted, or nil when the scheduling timeout expires.
|
|
45
51
|
#
|
|
46
52
|
# @!method close
|
|
47
53
|
# @abstract This method must be implemented by concrete executor subclasses.
|
|
@@ -84,7 +90,9 @@ module Farce
|
|
|
84
90
|
# Attempts to submit a task. Queue-based executors return `nil` when full
|
|
85
91
|
# instead of waiting for space. Other executors behave like {#schedule}.
|
|
86
92
|
#
|
|
87
|
-
# @param (see #schedule)
|
|
93
|
+
# @param args (see #schedule)
|
|
94
|
+
# @param mode (see #schedule)
|
|
95
|
+
# @param auto_local (see #schedule)
|
|
88
96
|
# @return [self, nil] self when submitted, or nil when the queue is full.
|
|
89
97
|
def try_schedule(...) = schedule(...)
|
|
90
98
|
|
|
@@ -98,14 +106,16 @@ module Farce
|
|
|
98
106
|
# 1. If {#local?} returns true, and it would run in local mode (either due to `auto_local` or explicitly set
|
|
99
107
|
# `mode: :local`), it executes the block directly without enqueuing it.
|
|
100
108
|
# 2. It blocks until the block has been executed, either immediately in local mode or after being scheduled.
|
|
109
|
+
# A `timeout` only limits scheduling waits. Once submitted, this waits for completion without a timeout.
|
|
101
110
|
#
|
|
102
111
|
# @param (see #schedule)
|
|
103
|
-
# @return [self]
|
|
104
|
-
def execute(*, mode: :smart, auto_local: true, &callback)
|
|
112
|
+
# @return [self, nil] self after execution, or nil when the scheduling timeout expires.
|
|
113
|
+
def execute(*, mode: :smart, auto_local: true, timeout: nil, &callback)
|
|
105
114
|
raise LocalJumpError, "Cannot yield without a block" unless block_given?
|
|
106
115
|
raise SchedulerClosedError, "cannot execute task on a closed scheduler" if closed?
|
|
107
116
|
|
|
108
|
-
|
|
117
|
+
deadline = scheduling_deadline(timeout)
|
|
118
|
+
if (auto_local || mode == :local) && local?(wait: timeout.nil?)
|
|
109
119
|
raise SchedulerClosedError, "cannot execute task on a closed scheduler" if closed?
|
|
110
120
|
yield(*)
|
|
111
121
|
return self
|
|
@@ -115,16 +125,29 @@ module Farce
|
|
|
115
125
|
signal = Signal.new
|
|
116
126
|
callback = Ractor.shareable_proc(&callback) unless Ractor.shareable?(callback)
|
|
117
127
|
|
|
118
|
-
schedule(callback, ran, signal, *, mode:, auto_local
|
|
128
|
+
submitted = schedule(callback, ran, signal, *, mode:, auto_local:,
|
|
129
|
+
timeout: Clock.remaining(deadline)) do |callback, ran, signal, *args|
|
|
119
130
|
callback.call(*args)
|
|
120
131
|
ensure
|
|
121
132
|
ran.set
|
|
122
133
|
signal.broadcast
|
|
123
134
|
end
|
|
135
|
+
return unless submitted
|
|
124
136
|
|
|
125
137
|
signal.wait_until { ran.value }
|
|
126
138
|
self
|
|
127
139
|
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def scheduling_deadline(timeout)
|
|
144
|
+
return if timeout.nil?
|
|
145
|
+
timeout = Float(timeout)
|
|
146
|
+
unless timeout.finite? && !timeout.negative?
|
|
147
|
+
raise ArgumentError, "timeout must be a finite, non-negative number or nil"
|
|
148
|
+
end
|
|
149
|
+
Clock.timeout(timeout)
|
|
150
|
+
end
|
|
128
151
|
end
|
|
129
152
|
end
|
|
130
153
|
end
|
data/lib/farce/abstract/flag.rb
CHANGED
|
@@ -61,9 +61,21 @@ module Farce
|
|
|
61
61
|
|
|
62
62
|
# @return [Boolean] whether the flag is set
|
|
63
63
|
def set? = value
|
|
64
|
+
alias true? set?
|
|
64
65
|
|
|
65
66
|
# @return [Boolean] whether the flag is unset
|
|
66
67
|
def unset? = !value
|
|
68
|
+
alias false? unset?
|
|
69
|
+
|
|
70
|
+
# Explicitly sets the value to true.
|
|
71
|
+
# Provided for compatibility with `Concurrent::AtomicBoolean`.
|
|
72
|
+
# @return [Boolean] true if the value has changed
|
|
73
|
+
def make_true = compare_and_set(false, true)
|
|
74
|
+
|
|
75
|
+
# Explicitly sets the value to false.
|
|
76
|
+
# Provided for compatibility with `Concurrent::AtomicBoolean`.
|
|
77
|
+
# @return [Boolean] true if the value has changed
|
|
78
|
+
def make_false = compare_and_set(true, false)
|
|
67
79
|
|
|
68
80
|
# Wait until the flag becomes true.
|
|
69
81
|
# @param timeout [Numeric, nil] the total seconds available
|