ratomic 0.2.1 → 0.3.1
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 +17 -0
- data/README.md +165 -98
- data/ext/ratomic/src/hashmap.rs +79 -3
- data/ext/ratomic/src/lib.rs +154 -25
- data/lib/ratomic/counter.rb +35 -29
- data/lib/ratomic/map.rb +295 -17
- data/lib/ratomic/pool.rb +10 -8
- data/lib/ratomic/queue.rb +60 -6
- data/lib/ratomic/undefined.rb +3 -1
- data/lib/ratomic/version.rb +2 -1
- data/lib/ratomic.rb +16 -11
- data/ratomic.gemspec +6 -5
- metadata +11 -7
data/lib/ratomic/map.rb
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Ratomic
|
|
4
|
-
# A Ractor-shareable concurrent
|
|
4
|
+
# A Ractor-shareable concurrent Hash backed by Rust's DashMap.
|
|
5
5
|
#
|
|
6
|
-
# Map
|
|
7
|
-
# for runtime state with shareable keys and values
|
|
8
|
-
# from multiple Ractors, such as counters or
|
|
6
|
+
# Map gives Ruby code a small, Ruby-shaped API over DashMap's concurrent
|
|
7
|
+
# storage. It is suitable for runtime state with shareable keys and values
|
|
8
|
+
# that are safe to access from multiple Ractors, such as integer counters or
|
|
9
|
+
# immutable offsets.
|
|
9
10
|
#
|
|
10
11
|
# This is not a full Hash replacement. Iteration and arbitrary mutable object
|
|
11
12
|
# borrowing are intentionally absent.
|
|
@@ -14,15 +15,190 @@ module Ratomic
|
|
|
14
15
|
# OFFSETS = Ratomic::Map.new
|
|
15
16
|
# OFFSETS[:source_a] = 42
|
|
16
17
|
# OFFSETS[:source_a] # => 42
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
#
|
|
20
|
-
|
|
18
|
+
#
|
|
19
|
+
# @!method get(key)
|
|
20
|
+
# Read a value by +key+.
|
|
21
|
+
#
|
|
22
|
+
# Missing keys return nil, so use #key? or #fetch when stored nil values
|
|
23
|
+
# need to be distinguished from missing entries.
|
|
24
|
+
#
|
|
25
|
+
# @param key [Object] lookup key
|
|
26
|
+
# @return [Object, nil] the stored value, or nil when the key is missing
|
|
27
|
+
#
|
|
28
|
+
# @!method set(key, value)
|
|
29
|
+
# Set a value for +key+.
|
|
30
|
+
#
|
|
31
|
+
# This is the method behind `#[]=` and follows Ruby setter semantics by
|
|
32
|
+
# returning the assigned value.
|
|
33
|
+
#
|
|
34
|
+
# @param key [Object] key to write
|
|
35
|
+
# @param value [Object] value to store
|
|
36
|
+
# @return [Object] the assigned value
|
|
37
|
+
#
|
|
38
|
+
# @!method []=(key, value)
|
|
39
|
+
# Set a value for +key+.
|
|
40
|
+
#
|
|
41
|
+
# In assignment form, Ruby returns the value assigned to the expression.
|
|
42
|
+
#
|
|
43
|
+
# @param key [Object] key to write
|
|
44
|
+
# @param value [Object] value to store
|
|
45
|
+
# @return [Object] the assigned value
|
|
46
|
+
#
|
|
47
|
+
# @!method key?(key)
|
|
48
|
+
# Check whether +key+ currently exists in the map.
|
|
49
|
+
#
|
|
50
|
+
# Unlike #get and #[], this distinguishes missing keys from stored nil
|
|
51
|
+
# values.
|
|
52
|
+
#
|
|
53
|
+
# @param key [Object] lookup key
|
|
54
|
+
# @return [Boolean] true when the key currently exists
|
|
55
|
+
#
|
|
56
|
+
# @!method delete(key)
|
|
57
|
+
# Remove +key+ and return its previous value.
|
|
58
|
+
#
|
|
59
|
+
# Missing keys return nil. Stored nil values also return nil; use #key?
|
|
60
|
+
# before deleting if that distinction matters.
|
|
61
|
+
#
|
|
62
|
+
# @param key [Object] key to remove
|
|
63
|
+
# @return [Object, nil] the previous value, or nil when the key was missing
|
|
64
|
+
#
|
|
65
|
+
# @!method clear
|
|
66
|
+
# Remove all entries from the map.
|
|
67
|
+
#
|
|
68
|
+
# @return [Ratomic::Map] self
|
|
69
|
+
#
|
|
70
|
+
# @!method size
|
|
71
|
+
# Return the current number of entries.
|
|
72
|
+
#
|
|
73
|
+
# Since this is a concurrent map, the value is a moment-in-time observation.
|
|
74
|
+
#
|
|
75
|
+
# @return [Integer] the current number of entries
|
|
76
|
+
#
|
|
77
|
+
# @!method fetch_and_modify(key)
|
|
78
|
+
# Replace the existing value for +key+ with the block return value.
|
|
79
|
+
#
|
|
80
|
+
# TODO: Revisit this name once the Map API settles. Prefer public method
|
|
81
|
+
# names that stay as close as possible to Ruby Hash semantics.
|
|
82
|
+
#
|
|
83
|
+
# The key must already exist. The operation is atomic for the key. The block
|
|
84
|
+
# runs while the map entry is locked, so avoid using this method for
|
|
85
|
+
# Ractor-hot loops or calling back into the same map from inside the block.
|
|
86
|
+
# If the block raises, the previous value is preserved.
|
|
87
|
+
#
|
|
88
|
+
# @param key [Object] key to modify in place
|
|
89
|
+
# @yieldparam value [Object] the current stored value
|
|
90
|
+
# @return [void] nothing useful is returned
|
|
91
|
+
# @raise [LocalJumpError] if no block is given
|
|
92
|
+
# @raise [Exception] any exception raised by the block
|
|
93
|
+
#
|
|
94
|
+
# @!method compute(key)
|
|
95
|
+
# Atomically compute and store a value for +key+.
|
|
96
|
+
#
|
|
97
|
+
# If +key+ exists, yields the current value. If +key+ is missing, yields
|
|
98
|
+
# nil. The block return value is stored and returned.
|
|
99
|
+
#
|
|
100
|
+
# The operation is atomic for the key. The block runs while the map entry is
|
|
101
|
+
# locked, so avoid using this method for Ractor-hot loops or calling back
|
|
102
|
+
# into the same map from inside the block. Prefer native update helpers such
|
|
103
|
+
# as #increment when they fit the workflow.
|
|
104
|
+
#
|
|
105
|
+
# If the block raises, the previous value is preserved. If the key was
|
|
106
|
+
# missing, no entry is inserted.
|
|
107
|
+
#
|
|
108
|
+
# @param key [Object] key to compute
|
|
109
|
+
# @yieldparam value [Object, nil] the current stored value, or nil when missing
|
|
110
|
+
# @return [Object] the newly stored value
|
|
111
|
+
# @raise [LocalJumpError] if no block is given
|
|
112
|
+
# @raise [Exception] any exception raised by the block
|
|
113
|
+
#
|
|
114
|
+
# @!method fetch_or_store(key)
|
|
115
|
+
# Return the existing value for +key+, or atomically store the block result.
|
|
116
|
+
#
|
|
117
|
+
# If +key+ exists, returns the current value and does not yield. If +key+
|
|
118
|
+
# is missing, yields once, stores the block return value, and returns it.
|
|
119
|
+
#
|
|
120
|
+
# The operation is atomic for the key. Under contention, only one stored
|
|
121
|
+
# value wins for a missing key. The block runs while the map entry is locked,
|
|
122
|
+
# so avoid using this method for Ractor-hot loops or calling back into the
|
|
123
|
+
# same map from inside the block.
|
|
124
|
+
#
|
|
125
|
+
# If the block raises, no entry is inserted.
|
|
126
|
+
#
|
|
127
|
+
# @param key [Object] key to read or initialize
|
|
128
|
+
# @return [Object] the existing or newly stored value
|
|
129
|
+
# @raise [LocalJumpError] if no block is given
|
|
130
|
+
# @raise [Exception] any exception raised by the block
|
|
131
|
+
#
|
|
132
|
+
# @!method upsert(key, initial)
|
|
133
|
+
# Atomically insert +initial+ for a missing key, or update an existing value.
|
|
134
|
+
#
|
|
135
|
+
# If +key+ is missing, stores and returns +initial+ without yielding. If
|
|
136
|
+
# +key+ exists, yields the current value, stores the block return value, and
|
|
137
|
+
# returns it.
|
|
138
|
+
#
|
|
139
|
+
# The operation is atomic for the key. The block runs while the map entry is
|
|
140
|
+
# locked, so avoid using this method for Ractor-hot loops or calling back
|
|
141
|
+
# into the same map from inside the block. Prefer native update helpers such
|
|
142
|
+
# as #increment when they fit the workflow.
|
|
143
|
+
#
|
|
144
|
+
# If the block raises, the previous value is preserved.
|
|
145
|
+
#
|
|
146
|
+
# @param key [Object] key to update
|
|
147
|
+
# @param initial [Object] value to use when the key is missing
|
|
148
|
+
# @yieldparam value [Object, nil] the current stored value, or nil when missing
|
|
149
|
+
# @return [Object] the inserted or newly stored value
|
|
150
|
+
# @raise [LocalJumpError] if no block is given
|
|
151
|
+
# @raise [Exception] any exception raised by the block
|
|
152
|
+
#
|
|
153
|
+
# @!method increment(key, by = 1)
|
|
154
|
+
# Atomically increment the numeric value for +key+.
|
|
155
|
+
#
|
|
156
|
+
# Missing keys start at zero. Existing non-numeric values raise TypeError
|
|
157
|
+
# and are left unchanged. This uses a native update path and is the preferred
|
|
158
|
+
# counter primitive for Ractor-heavy workloads.
|
|
159
|
+
#
|
|
160
|
+
# @param key [Object] counter key to increment
|
|
161
|
+
# @param by [Numeric] amount to add
|
|
162
|
+
# @return [Numeric] the newly stored value
|
|
163
|
+
# @raise [TypeError] if +by+ or the existing value is not numeric
|
|
164
|
+
#
|
|
165
|
+
# @!method decrement(key, by = 1)
|
|
166
|
+
# Atomically decrement the numeric value for +key+.
|
|
167
|
+
#
|
|
168
|
+
# Missing keys start at zero.
|
|
169
|
+
#
|
|
170
|
+
# @param key [Object] counter key to decrement
|
|
171
|
+
# @param by [Numeric] amount to subtract
|
|
172
|
+
# @return [Numeric] the newly stored value
|
|
173
|
+
# @raise [TypeError] if +by+ or the existing value is not numeric
|
|
174
|
+
#
|
|
175
|
+
# @!method append(key, value)
|
|
176
|
+
# Atomically append +value+ to an Array bucket for +key+.
|
|
177
|
+
#
|
|
178
|
+
# The stored Array is replaced rather than mutated in place.
|
|
179
|
+
#
|
|
180
|
+
# @param key [Object] bucket key to append into
|
|
181
|
+
# @param value [Object] value to append
|
|
182
|
+
# @return [Array] the newly stored frozen Array
|
|
183
|
+
# @raise [TypeError] if the existing value is not an Array
|
|
184
|
+
#
|
|
185
|
+
# @!method add_to_set(key, value)
|
|
186
|
+
# Atomically add +value+ to a Set bucket for +key+.
|
|
187
|
+
#
|
|
188
|
+
# The stored Set is replaced rather than mutated in place.
|
|
189
|
+
#
|
|
190
|
+
# @param key [Object] bucket key to update
|
|
191
|
+
# @param value [Object] value to add to the set
|
|
192
|
+
# @return [Set] the newly stored frozen Set
|
|
193
|
+
# @raise [TypeError] if the existing value is not a Set
|
|
194
|
+
class Map
|
|
21
195
|
# Set a value for +key+.
|
|
22
196
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# @
|
|
197
|
+
# In assignment form, Ruby returns the assigned value.
|
|
198
|
+
#
|
|
199
|
+
# @param key [Object] key to write
|
|
200
|
+
# @param value [Object] value to store
|
|
201
|
+
# @return [Object] the assigned value
|
|
26
202
|
def []=(key, value)
|
|
27
203
|
set(key, value)
|
|
28
204
|
end
|
|
@@ -31,26 +207,128 @@ module Ratomic
|
|
|
31
207
|
#
|
|
32
208
|
# Missing keys currently return nil, so storing nil is ambiguous.
|
|
33
209
|
#
|
|
34
|
-
# @param key [Object]
|
|
35
|
-
# @return [Object, nil]
|
|
210
|
+
# @param key [Object] lookup key
|
|
211
|
+
# @return [Object, nil] the stored value, or nil when the key is missing
|
|
36
212
|
def [](key)
|
|
37
213
|
get(key)
|
|
38
214
|
end
|
|
39
215
|
|
|
216
|
+
# Fetch a value by +key+.
|
|
217
|
+
#
|
|
218
|
+
# Unlike #[], this distinguishes missing keys from explicit nil values.
|
|
219
|
+
#
|
|
220
|
+
# @param key [Object] lookup key
|
|
221
|
+
# @param default [Object] fallback value to return when the key is missing
|
|
222
|
+
# @yieldparam key [Object] the missing key
|
|
223
|
+
# @return [Object] the found value, default, or block result
|
|
224
|
+
# @raise [KeyError] if +key+ is missing and no default or block is provided
|
|
225
|
+
def fetch(key, default = UNDEFINED)
|
|
226
|
+
return get(key) if key?(key)
|
|
227
|
+
return yield key if block_given?
|
|
228
|
+
return default unless default.equal?(UNDEFINED)
|
|
229
|
+
|
|
230
|
+
raise KeyError, "key not found: #{key.inspect}"
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Atomically increment the numeric value for +key+.
|
|
234
|
+
#
|
|
235
|
+
# Missing keys start at zero. Existing non-numeric values raise TypeError
|
|
236
|
+
# and are left unchanged. This uses a native update path and is the preferred
|
|
237
|
+
# counter primitive for Ractor-heavy workloads.
|
|
238
|
+
#
|
|
239
|
+
# @param key [Object] counter key to increment
|
|
240
|
+
# @param by [Numeric] amount to add
|
|
241
|
+
# @return [Numeric] the newly stored value
|
|
242
|
+
# @raise [TypeError] if +by+ or the existing value is not numeric
|
|
243
|
+
def increment(key, by = 1)
|
|
244
|
+
raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric)
|
|
245
|
+
|
|
246
|
+
__increment_numeric(key, by)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Atomically decrement the numeric value for +key+.
|
|
250
|
+
#
|
|
251
|
+
# Missing keys start at zero.
|
|
252
|
+
#
|
|
253
|
+
# @param key [Object] counter key to decrement
|
|
254
|
+
# @param by [Numeric] amount to subtract
|
|
255
|
+
# @return [Numeric] the newly stored value
|
|
256
|
+
# @raise [TypeError] if +by+ or the existing value is not numeric
|
|
257
|
+
def decrement(key, by = 1)
|
|
258
|
+
raise TypeError, "amount must be numeric: #{by.inspect}" unless by.is_a?(Numeric)
|
|
259
|
+
|
|
260
|
+
increment(key, -by)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Atomically append +value+ to an Array bucket for +key+.
|
|
264
|
+
#
|
|
265
|
+
# The stored Array is replaced rather than mutated in place.
|
|
266
|
+
#
|
|
267
|
+
# @param key [Object] bucket key to append into
|
|
268
|
+
# @param value [Object] value to append
|
|
269
|
+
# @return [Array] the newly stored frozen Array
|
|
270
|
+
# @raise [TypeError] if the existing value is not an Array
|
|
271
|
+
def append(key, value)
|
|
272
|
+
missing = !key?(key)
|
|
273
|
+
compute(key) do |old_value|
|
|
274
|
+
if old_value.nil? && missing
|
|
275
|
+
[value].freeze
|
|
276
|
+
else
|
|
277
|
+
unless old_value.is_a?(Array)
|
|
278
|
+
raise TypeError,
|
|
279
|
+
"existing value for #{key.inspect} must be an Array: #{old_value.inspect}"
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
(old_value + [value]).freeze
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Atomically add +value+ to a Set bucket for +key+.
|
|
288
|
+
#
|
|
289
|
+
# The stored Set is replaced rather than mutated in place.
|
|
290
|
+
#
|
|
291
|
+
# @param key [Object] bucket key to update
|
|
292
|
+
# @param value [Object] value to add to the set
|
|
293
|
+
# @return [Set] the newly stored frozen Set
|
|
294
|
+
# @raise [TypeError] if the existing value is not a Set
|
|
295
|
+
def add_to_set(key, value)
|
|
296
|
+
missing = !key?(key)
|
|
297
|
+
compute(key) do |old_value|
|
|
298
|
+
if old_value.nil? && missing
|
|
299
|
+
Set[value].freeze
|
|
300
|
+
else
|
|
301
|
+
unless old_value.is_a?(Set)
|
|
302
|
+
raise TypeError,
|
|
303
|
+
"existing value for #{key.inspect} must be a Set: #{old_value.inspect}"
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
(old_value | [value]).freeze
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
40
311
|
# Alias for #size.
|
|
41
312
|
#
|
|
42
|
-
# @return [Integer]
|
|
313
|
+
# @return [Integer] the current number of entries
|
|
43
314
|
def length
|
|
44
315
|
size
|
|
45
316
|
end
|
|
46
317
|
|
|
47
318
|
# Check whether the map currently has no entries.
|
|
48
319
|
#
|
|
49
|
-
# @return [Boolean]
|
|
320
|
+
# @return [Boolean] true when the map currently has no entries
|
|
50
321
|
def empty?
|
|
51
322
|
size.zero?
|
|
52
323
|
end
|
|
53
|
-
end
|
|
54
324
|
|
|
55
|
-
|
|
325
|
+
# Alias for #key?.
|
|
326
|
+
#
|
|
327
|
+
# @param key [Object] lookup key
|
|
328
|
+
# @return [Boolean] true when the key currently exists
|
|
329
|
+
def include?(key)
|
|
330
|
+
key?(key)
|
|
331
|
+
end
|
|
332
|
+
alias member? include?
|
|
333
|
+
end
|
|
56
334
|
end
|
data/lib/ratomic/pool.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "timeout"
|
|
4
|
+
|
|
3
5
|
module Ratomic
|
|
4
6
|
# A Ractor-safe ownership-transfer pool for mutable Ruby objects.
|
|
5
7
|
#
|
|
@@ -26,9 +28,9 @@ module Ratomic
|
|
|
26
28
|
class Pool
|
|
27
29
|
# Create a pool and seed it with +size+ objects from the factory block.
|
|
28
30
|
#
|
|
29
|
-
# @param size [Integer] number of pooled objects
|
|
31
|
+
# @param size [Integer] number of pooled objects to create up front
|
|
30
32
|
# @param timeout [Numeric, nil] checkout timeout in seconds, or nil to wait indefinitely
|
|
31
|
-
# @yieldreturn [Object] mutable object to
|
|
33
|
+
# @yieldreturn [Object] a mutable object to place into the pool
|
|
32
34
|
# @raise [ArgumentError] if +size+ is not positive
|
|
33
35
|
# @raise [LocalJumpError] if no factory block is given
|
|
34
36
|
def initialize(size = 5, timeout = 1.0)
|
|
@@ -47,7 +49,7 @@ module Ratomic
|
|
|
47
49
|
# The returned object has been moved from the pool to the caller. The caller
|
|
48
50
|
# owns it until it is passed to #checkin.
|
|
49
51
|
#
|
|
50
|
-
# @return [Object, nil]
|
|
52
|
+
# @return [Object, nil] the checked-out object, or nil if the timeout expires
|
|
51
53
|
def checkout
|
|
52
54
|
reply = Ractor::Port.new
|
|
53
55
|
request_id = reply.object_id
|
|
@@ -66,8 +68,8 @@ module Ratomic
|
|
|
66
68
|
# use the object after calling this method; Ruby raises Ractor::MovedError
|
|
67
69
|
# for stale references.
|
|
68
70
|
#
|
|
69
|
-
# @param object [Object] previously checked
|
|
70
|
-
# @return [nil]
|
|
71
|
+
# @param object [Object] the object previously checked out from the pool
|
|
72
|
+
# @return [nil] nothing useful is returned
|
|
71
73
|
def checkin(object)
|
|
72
74
|
@control.send([:checkin, object], move: true)
|
|
73
75
|
nil
|
|
@@ -78,7 +80,7 @@ module Ratomic
|
|
|
78
80
|
# This is primarily useful for tests and short-lived scripts. A closed pool
|
|
79
81
|
# should not be used for further checkout/checkin operations.
|
|
80
82
|
#
|
|
81
|
-
# @return [nil]
|
|
83
|
+
# @return [nil] nothing useful is returned
|
|
82
84
|
def close
|
|
83
85
|
@control << [:shutdown]
|
|
84
86
|
@control.value
|
|
@@ -92,9 +94,9 @@ module Ratomic
|
|
|
92
94
|
# This is the preferred API because it guarantees checkin through an ensure
|
|
93
95
|
# block. If checkout times out, raises Ratomic::Error and does not yield.
|
|
94
96
|
#
|
|
95
|
-
# @yieldparam object [Object] checked
|
|
97
|
+
# @yieldparam object [Object] the object checked out from the pool
|
|
96
98
|
# @raise [Ratomic::Error] if checkout times out
|
|
97
|
-
# @return [Object] block return value
|
|
99
|
+
# @return [Object] the block return value
|
|
98
100
|
def with
|
|
99
101
|
object = checkout
|
|
100
102
|
raise Ratomic::Error, "pool checkout timeout" if object.nil?
|
data/lib/ratomic/queue.rb
CHANGED
|
@@ -1,17 +1,71 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Ratomic
|
|
4
|
-
#
|
|
5
|
-
|
|
4
|
+
# A Ractor-shareable multi-producer, multi-consumer queue.
|
|
5
|
+
#
|
|
6
|
+
# Queue stores Ruby objects in a fixed-size native ring buffer. Push blocks
|
|
7
|
+
# when the queue is full; pop blocks when the queue is empty.
|
|
8
|
+
#
|
|
9
|
+
# @example Push and pop work
|
|
10
|
+
# queue = Ratomic::Queue.new(128)
|
|
11
|
+
# queue << "job"
|
|
12
|
+
# queue.pop # => "job"
|
|
13
|
+
#
|
|
14
|
+
# @!method self.new(capacity)
|
|
15
|
+
# Create a queue with a fixed capacity.
|
|
16
|
+
#
|
|
17
|
+
# @param capacity [Integer] maximum number of items the queue can hold
|
|
18
|
+
# @return [Ratomic::Queue] a new shareable queue
|
|
19
|
+
# @raise [ArgumentError] if +capacity+ is outside the supported range
|
|
20
|
+
# @raise [TypeError] if +capacity+ cannot be converted to an Integer
|
|
21
|
+
#
|
|
22
|
+
# @!method push(item)
|
|
23
|
+
# Push an item, blocking until space is available.
|
|
24
|
+
#
|
|
25
|
+
# @param item [Object] the item to append to the queue
|
|
26
|
+
# @return [Ratomic::Queue] self
|
|
27
|
+
#
|
|
28
|
+
# @!method pop
|
|
29
|
+
# Pop an item, blocking until one is available.
|
|
30
|
+
#
|
|
31
|
+
# @return [Object] the next queued item
|
|
32
|
+
#
|
|
33
|
+
# @!method peek
|
|
34
|
+
# Return the next item without removing it.
|
|
35
|
+
#
|
|
36
|
+
# Since this is a concurrent queue, the value is a moment-in-time
|
|
37
|
+
# observation.
|
|
38
|
+
#
|
|
39
|
+
# @return [Object, nil] the next item, or nil if the queue is empty
|
|
40
|
+
#
|
|
41
|
+
# @!method empty?
|
|
42
|
+
# Check whether the queue currently appears empty.
|
|
43
|
+
#
|
|
44
|
+
# Since this is a concurrent queue, the value is a moment-in-time
|
|
45
|
+
# observation.
|
|
46
|
+
#
|
|
47
|
+
# @return [Boolean] true when the queue currently has no items
|
|
48
|
+
#
|
|
49
|
+
# @!method size
|
|
50
|
+
# Return the current queue size.
|
|
51
|
+
#
|
|
52
|
+
# Since this is a concurrent queue, the value is a moment-in-time
|
|
53
|
+
# observation.
|
|
54
|
+
#
|
|
55
|
+
# @return [Integer] the current number of queued items
|
|
56
|
+
#
|
|
57
|
+
# @!method length
|
|
58
|
+
# Alias for #size.
|
|
59
|
+
#
|
|
60
|
+
# @return [Integer] the current number of queued items
|
|
61
|
+
class Queue
|
|
6
62
|
# Push an item and return the queue for chaining.
|
|
7
63
|
#
|
|
8
|
-
# @param item [Object]
|
|
9
|
-
# @return [Ratomic::Queue]
|
|
64
|
+
# @param item [Object] the item to append to the queue
|
|
65
|
+
# @return [Ratomic::Queue] self
|
|
10
66
|
def <<(item)
|
|
11
67
|
push(item)
|
|
12
68
|
self
|
|
13
69
|
end
|
|
14
70
|
end
|
|
15
|
-
|
|
16
|
-
Queue.prepend(QueueMethods)
|
|
17
71
|
end
|
data/lib/ratomic/undefined.rb
CHANGED
|
@@ -4,7 +4,9 @@ module Ratomic
|
|
|
4
4
|
# Internal sentinel object for future Hash-like APIs that need to distinguish
|
|
5
5
|
# missing keys from explicit nil values.
|
|
6
6
|
class Undefined
|
|
7
|
-
#
|
|
7
|
+
# Return the sentinel's stable inspection string.
|
|
8
|
+
#
|
|
9
|
+
# @return [String] a human-readable sentinel marker
|
|
8
10
|
def inspect
|
|
9
11
|
"#<Undefined>"
|
|
10
12
|
end
|
data/lib/ratomic/version.rb
CHANGED
data/lib/ratomic.rb
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
require_relative "ratomic/pool"
|
|
10
|
-
require_relative "ratomic/map"
|
|
11
|
-
require_relative "ratomic/queue"
|
|
12
|
-
|
|
3
|
+
# Ratomic provides mutable data structures for Ruby Ractors. Its primitives
|
|
4
|
+
# are backed by native Rust concurrency libraries so Ruby code can share useful
|
|
5
|
+
# state across Ractors without falling back to one global lock. Pool uses Ruby
|
|
6
|
+
# Ractor ownership-transfer primitives instead of the native Rust path.
|
|
7
|
+
#
|
|
8
|
+
# The public API currently includes {Counter}, {Map}, {Queue}, and {Pool}.
|
|
13
9
|
module Ratomic
|
|
14
|
-
# Base error for Ratomic-specific
|
|
10
|
+
# Base error class for Ratomic-specific failures.
|
|
15
11
|
class Error < StandardError; end
|
|
16
12
|
end
|
|
13
|
+
|
|
14
|
+
require "ratomic/ratomic"
|
|
15
|
+
require "ratomic/version"
|
|
16
|
+
|
|
17
|
+
require "ratomic/undefined"
|
|
18
|
+
require "ratomic/counter"
|
|
19
|
+
require "ratomic/map"
|
|
20
|
+
require "ratomic/queue"
|
|
21
|
+
require "ratomic/pool"
|
data/ratomic.gemspec
CHANGED
|
@@ -9,15 +9,16 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.email = ["mike@perham.net"]
|
|
10
10
|
spec.metadata["maintainers"] = "Ken C. Demanawa"
|
|
11
11
|
|
|
12
|
-
spec.summary = "
|
|
13
|
-
spec.description =
|
|
14
|
-
|
|
12
|
+
spec.summary = "Ractor-safe concurrent data structures for Ruby"
|
|
13
|
+
spec.description = "Ractor-safe counters, maps, queues, and ownership-transfer pools " \
|
|
14
|
+
"backed by native Rust concurrency primitives."
|
|
15
|
+
spec.homepage = "https://mperham.github.io/ratomic"
|
|
15
16
|
spec.license = "MIT"
|
|
16
|
-
spec.required_ruby_version = ">= 4.0.
|
|
17
|
+
spec.required_ruby_version = [">= 4.0", "< 4.1.dev"]
|
|
17
18
|
|
|
18
19
|
spec.metadata["homepage_uri"] = spec.homepage
|
|
19
20
|
spec.metadata["source_code_uri"] = "https://github.com/mperham/ratomic"
|
|
20
|
-
spec.metadata["changelog_uri"] = "https://github.com/mperham/ratomic"
|
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/mperham/ratomic/blob/trunk/CHANGELOG.md"
|
|
21
22
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
22
23
|
|
|
23
24
|
# Specify which files should be added to the gem when it is released.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ratomic
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Perham
|
|
@@ -24,7 +24,8 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: 0.9.128
|
|
27
|
-
description:
|
|
27
|
+
description: Ractor-safe counters, maps, queues, and ownership-transfer pools backed
|
|
28
|
+
by native Rust concurrency primitives.
|
|
28
29
|
email:
|
|
29
30
|
- mike@perham.net
|
|
30
31
|
executables: []
|
|
@@ -55,14 +56,14 @@ files:
|
|
|
55
56
|
- lib/ratomic/undefined.rb
|
|
56
57
|
- lib/ratomic/version.rb
|
|
57
58
|
- ratomic.gemspec
|
|
58
|
-
homepage: https://github.
|
|
59
|
+
homepage: https://mperham.github.io/ratomic
|
|
59
60
|
licenses:
|
|
60
61
|
- MIT
|
|
61
62
|
metadata:
|
|
62
63
|
maintainers: Ken C. Demanawa
|
|
63
|
-
homepage_uri: https://github.
|
|
64
|
+
homepage_uri: https://mperham.github.io/ratomic
|
|
64
65
|
source_code_uri: https://github.com/mperham/ratomic
|
|
65
|
-
changelog_uri: https://github.com/mperham/ratomic
|
|
66
|
+
changelog_uri: https://github.com/mperham/ratomic/blob/trunk/CHANGELOG.md
|
|
66
67
|
rubygems_mfa_required: 'true'
|
|
67
68
|
rdoc_options: []
|
|
68
69
|
require_paths:
|
|
@@ -71,7 +72,10 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
71
72
|
requirements:
|
|
72
73
|
- - ">="
|
|
73
74
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 4.0
|
|
75
|
+
version: '4.0'
|
|
76
|
+
- - "<"
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: 4.1.dev
|
|
75
79
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
80
|
requirements:
|
|
77
81
|
- - ">="
|
|
@@ -80,5 +84,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
80
84
|
requirements: []
|
|
81
85
|
rubygems_version: 4.0.10
|
|
82
86
|
specification_version: 4
|
|
83
|
-
summary:
|
|
87
|
+
summary: Ractor-safe concurrent data structures for Ruby
|
|
84
88
|
test_files: []
|