async 2.28.1 → 2.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/condition.rb +14 -32
- data/lib/async/deadline.rb +70 -0
- data/lib/async/notification.rb +7 -5
- data/lib/async/priority_queue.rb +253 -0
- data/lib/async/promise.rb +188 -0
- data/lib/async/queue.rb +39 -110
- data/lib/async/task.rb +62 -37
- data/lib/async/variable.rb +5 -0
- data/lib/async/version.rb +1 -1
- data/lib/async/waiter.rb +2 -0
- data/lib/kernel/sync.rb +6 -2
- data/readme.md +25 -28
- data/releases.md +93 -0
- data.tar.gz.sig +2 -2
- metadata +4 -2
- metadata.gz.sig +0 -0
- data/agent.md +0 -63
data/lib/async/queue.rb
CHANGED
@@ -10,10 +10,14 @@
|
|
10
10
|
require_relative "notification"
|
11
11
|
|
12
12
|
module Async
|
13
|
-
# A queue which allows items to be processed in order.
|
13
|
+
# A thread-safe queue which allows items to be processed in order.
|
14
|
+
#
|
15
|
+
# This implementation uses Thread::Queue internally for thread safety while
|
16
|
+
# maintaining compatibility with the fiber scheduler.
|
14
17
|
#
|
15
18
|
# It has a compatible interface with {Notification} and {Condition}, except that it's multi-value.
|
16
19
|
#
|
20
|
+
# @asynchronous This class is thread-safe.
|
17
21
|
# @public Since *Async v1*.
|
18
22
|
class Queue
|
19
23
|
# An error raised when trying to enqueue items to a closed queue.
|
@@ -21,53 +25,44 @@ module Async
|
|
21
25
|
class ClosedError < RuntimeError
|
22
26
|
end
|
23
27
|
|
24
|
-
# Create a new queue.
|
28
|
+
# Create a new thread-safe queue.
|
25
29
|
#
|
26
30
|
# @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
|
27
|
-
|
28
|
-
|
29
|
-
@items = []
|
30
|
-
@closed = false
|
31
|
+
def initialize(parent: nil, delegate: Thread::Queue.new)
|
32
|
+
@delegate = delegate
|
31
33
|
@parent = parent
|
32
|
-
@available = available
|
33
34
|
end
|
34
35
|
|
35
36
|
# @returns [Boolean] Whether the queue is closed.
|
36
37
|
def closed?
|
37
|
-
@closed
|
38
|
+
@delegate.closed?
|
38
39
|
end
|
39
40
|
|
40
41
|
# Close the queue, causing all waiting tasks to return `nil`. Any subsequent calls to {enqueue} will raise an exception.
|
41
42
|
def close
|
42
|
-
@
|
43
|
-
|
44
|
-
while @available.waiting?
|
45
|
-
@available.signal(nil)
|
46
|
-
end
|
43
|
+
@delegate.close
|
47
44
|
end
|
48
45
|
|
49
|
-
# @attribute [Array] The items in the queue.
|
50
|
-
attr :items
|
51
|
-
|
52
46
|
# @returns [Integer] The number of items in the queue.
|
53
47
|
def size
|
54
|
-
@
|
48
|
+
@delegate.size
|
55
49
|
end
|
56
50
|
|
57
51
|
# @returns [Boolean] Whether the queue is empty.
|
58
52
|
def empty?
|
59
|
-
@
|
53
|
+
@delegate.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
# @returns [Integer] The number of tasks waiting for an item.
|
57
|
+
def waiting_count
|
58
|
+
@delegate.num_waiting
|
60
59
|
end
|
61
60
|
|
62
61
|
# Add an item to the queue.
|
63
62
|
def push(item)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@items << item
|
69
|
-
|
70
|
-
@available.signal unless self.empty?
|
63
|
+
@delegate.push(item)
|
64
|
+
rescue ClosedQueueError
|
65
|
+
raise ClosedError, "Cannot enqueue items to a closed queue!"
|
71
66
|
end
|
72
67
|
|
73
68
|
# Compatibility with {::Queue#push}.
|
@@ -77,31 +72,23 @@ module Async
|
|
77
72
|
|
78
73
|
# Add multiple items to the queue.
|
79
74
|
def enqueue(*items)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@items.concat(items)
|
85
|
-
|
86
|
-
@available.signal unless self.empty?
|
75
|
+
items.each {|item| @delegate.push(item)}
|
76
|
+
rescue ClosedQueueError
|
77
|
+
raise ClosedError, "Cannot enqueue items to a closed queue!"
|
87
78
|
end
|
88
79
|
|
89
80
|
# Remove and return the next item from the queue.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
@available.wait
|
97
|
-
end
|
98
|
-
|
99
|
-
@items.shift
|
81
|
+
# @parameter timeout [Numeric, nil] Maximum time to wait for an item. If nil, waits indefinitely. If 0, returns immediately.
|
82
|
+
# @returns [Object, nil] The dequeued item, or nil if timeout expires.
|
83
|
+
def dequeue(timeout: nil)
|
84
|
+
@delegate.pop(timeout: timeout)
|
100
85
|
end
|
101
86
|
|
102
87
|
# Compatibility with {::Queue#pop}.
|
103
|
-
|
104
|
-
|
88
|
+
# @parameter timeout [Numeric, nil] Maximum time to wait for an item. If nil, waits indefinitely. If 0, returns immediately.
|
89
|
+
# @returns [Object, nil] The dequeued item, or nil if timeout expires.
|
90
|
+
def pop(timeout: nil)
|
91
|
+
@delegate.pop(timeout: timeout)
|
105
92
|
end
|
106
93
|
|
107
94
|
# Process each item in the queue.
|
@@ -136,7 +123,8 @@ module Async
|
|
136
123
|
end
|
137
124
|
end
|
138
125
|
|
139
|
-
# A queue which limits the number of items that can be enqueued.
|
126
|
+
# A thread-safe queue which limits the number of items that can be enqueued.
|
127
|
+
#
|
140
128
|
# @public Since *Async v1*.
|
141
129
|
class LimitedQueue < Queue
|
142
130
|
# @private This exists purely for emitting a warning.
|
@@ -149,78 +137,19 @@ module Async
|
|
149
137
|
# Create a new limited queue.
|
150
138
|
#
|
151
139
|
# @parameter limit [Integer] The maximum number of items that can be enqueued.
|
152
|
-
# @parameter full [Notification] The notification to use for signaling when the queue is full.
|
153
|
-
def initialize(limit = 1,
|
154
|
-
super(**options)
|
155
|
-
|
156
|
-
@limit = limit
|
157
|
-
@full = full
|
140
|
+
# @parameter full [Notification] The notification to use for signaling when the queue is full. (ignored, for compatibility)
|
141
|
+
def initialize(limit = 1, **options)
|
142
|
+
super(**options, delegate: Thread::SizedQueue.new(limit))
|
158
143
|
end
|
159
144
|
|
160
145
|
# @attribute [Integer] The maximum number of items that can be enqueued.
|
161
|
-
|
162
|
-
|
163
|
-
# Close the queue, causing all waiting tasks to return `nil`. Any subsequent calls to {enqueue} will raise an exception.
|
164
|
-
# Also signals all tasks waiting for the queue to be full.
|
165
|
-
def close
|
166
|
-
super
|
167
|
-
|
168
|
-
while @full.waiting?
|
169
|
-
@full.signal(nil)
|
170
|
-
end
|
146
|
+
def limit
|
147
|
+
@delegate.max
|
171
148
|
end
|
172
149
|
|
173
150
|
# @returns [Boolean] Whether trying to enqueue an item would block.
|
174
151
|
def limited?
|
175
|
-
!@closed && @
|
176
|
-
end
|
177
|
-
|
178
|
-
# Add an item to the queue.
|
179
|
-
#
|
180
|
-
# If the queue is full, this method will block until there is space available.
|
181
|
-
#
|
182
|
-
# @parameter item [Object] The item to add to the queue.
|
183
|
-
def push(item)
|
184
|
-
while limited?
|
185
|
-
@full.wait
|
186
|
-
end
|
187
|
-
|
188
|
-
super
|
189
|
-
end
|
190
|
-
|
191
|
-
# Add multiple items to the queue.
|
192
|
-
#
|
193
|
-
# If the queue is full, this method will block until there is space available.
|
194
|
-
#
|
195
|
-
# @parameter items [Array] The items to add to the queue.
|
196
|
-
def enqueue(*items)
|
197
|
-
while !items.empty?
|
198
|
-
while limited?
|
199
|
-
@full.wait
|
200
|
-
end
|
201
|
-
|
202
|
-
if @closed
|
203
|
-
raise ClosedError, "Cannot enqueue items to a closed queue."
|
204
|
-
end
|
205
|
-
|
206
|
-
available = @limit - @items.size
|
207
|
-
@items.concat(items.shift(available))
|
208
|
-
|
209
|
-
@available.signal unless self.empty?
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
# Remove and return the next item from the queue.
|
214
|
-
#
|
215
|
-
# If the queue is empty, this method will block until an item is available.
|
216
|
-
#
|
217
|
-
# @returns [Object] The next item in the queue.
|
218
|
-
def dequeue
|
219
|
-
item = super
|
220
|
-
|
221
|
-
@full.signal
|
222
|
-
|
223
|
-
return item
|
152
|
+
!@delegate.closed? && @delegate.size >= @delegate.max
|
224
153
|
end
|
225
154
|
end
|
226
155
|
end
|
data/lib/async/task.rb
CHANGED
@@ -7,12 +7,14 @@
|
|
7
7
|
# Copyright, 2020, by Patrik Wenger.
|
8
8
|
# Copyright, 2023, by Math Ieu.
|
9
9
|
# Copyright, 2025, by Shigeru Nakajima.
|
10
|
+
# Copyright, 2025, by Shopify Inc.
|
10
11
|
|
11
12
|
require "fiber"
|
12
13
|
require "console"
|
13
14
|
|
14
15
|
require_relative "node"
|
15
16
|
require_relative "condition"
|
17
|
+
require_relative "promise"
|
16
18
|
require_relative "stop"
|
17
19
|
|
18
20
|
Fiber.attr_accessor :async_task
|
@@ -63,14 +65,24 @@ module Async
|
|
63
65
|
|
64
66
|
# These instance variables are critical to the state of the task.
|
65
67
|
# In the initialized state, the @block should be set, but the @fiber should be nil.
|
66
|
-
# In the running state, the @fiber should be set.
|
68
|
+
# In the running state, the @fiber should be set, and @block should be nil.
|
67
69
|
# In a finished state, the @block should be nil, and the @fiber should be nil.
|
68
70
|
@block = block
|
69
71
|
@fiber = nil
|
70
72
|
|
71
|
-
@
|
72
|
-
|
73
|
-
|
73
|
+
@promise = Promise.new
|
74
|
+
|
75
|
+
# Handle finished: parameter for backward compatibility:
|
76
|
+
case finished
|
77
|
+
when false
|
78
|
+
# `finished: false` suppresses warnings for expected task failures:
|
79
|
+
@promise.suppress_warnings!
|
80
|
+
when nil
|
81
|
+
# `finished: nil` is the default, no special handling:
|
82
|
+
else
|
83
|
+
# All other `finished:` values are deprecated:
|
84
|
+
warn("finished: argument with non-false value is deprecated and will be removed.", uplevel: 1, category: :deprecated) if $VERBOSE
|
85
|
+
end
|
74
86
|
|
75
87
|
@defer_stop = nil
|
76
88
|
end
|
@@ -109,7 +121,7 @@ module Async
|
|
109
121
|
|
110
122
|
# @returns [String] A description of the task and it's current status.
|
111
123
|
def to_s
|
112
|
-
"\#<#{self.description} (#{
|
124
|
+
"\#<#{self.description} (#{self.status})>"
|
113
125
|
end
|
114
126
|
|
115
127
|
# @deprecated Prefer {Kernel#sleep} except when compatibility with `stable-v1` is required.
|
@@ -146,22 +158,22 @@ module Async
|
|
146
158
|
|
147
159
|
# @returns [Boolean] Whether the task is running.
|
148
160
|
def running?
|
149
|
-
|
161
|
+
self.alive?
|
150
162
|
end
|
151
163
|
|
152
164
|
# @returns [Boolean] Whether the task failed with an exception.
|
153
165
|
def failed?
|
154
|
-
@
|
166
|
+
@promise.failed?
|
155
167
|
end
|
156
168
|
|
157
169
|
# @returns [Boolean] Whether the task has been stopped.
|
158
170
|
def stopped?
|
159
|
-
@
|
171
|
+
@promise.cancelled?
|
160
172
|
end
|
161
173
|
|
162
174
|
# @returns [Boolean] Whether the task has completed execution and generated a result.
|
163
175
|
def completed?
|
164
|
-
@
|
176
|
+
@promise.completed?
|
165
177
|
end
|
166
178
|
|
167
179
|
# Alias for {#completed?}.
|
@@ -170,20 +182,32 @@ module Async
|
|
170
182
|
end
|
171
183
|
|
172
184
|
# @attribute [Symbol] The status of the execution of the task, one of `:initialized`, `:running`, `:complete`, `:stopped` or `:failed`.
|
173
|
-
|
185
|
+
def status
|
186
|
+
case @promise.resolved
|
187
|
+
when :cancelled
|
188
|
+
:stopped
|
189
|
+
when :failed
|
190
|
+
:failed
|
191
|
+
when :completed
|
192
|
+
:completed
|
193
|
+
when nil
|
194
|
+
self.running? ? :running : :initialized
|
195
|
+
end
|
196
|
+
end
|
174
197
|
|
175
198
|
# Begin the execution of the task.
|
176
199
|
#
|
177
200
|
# @raises [RuntimeError] If the task is already running.
|
178
201
|
def run(*arguments)
|
179
|
-
|
180
|
-
|
202
|
+
# Move from initialized to running by clearing @block
|
203
|
+
if block = @block
|
204
|
+
@block = nil
|
181
205
|
|
182
206
|
schedule do
|
183
|
-
|
207
|
+
block.call(self, *arguments)
|
184
208
|
rescue => error
|
185
209
|
# I'm not completely happy with this overhead, but the alternative is to not log anything which makes debugging extremely difficult. Maybe we can introduce a debug wrapper which adds extra logging.
|
186
|
-
|
210
|
+
unless @promise.waiting?
|
187
211
|
warn(self, "Task may have ended with unhandled exception.", exception: error)
|
188
212
|
end
|
189
213
|
|
@@ -225,24 +249,29 @@ module Async
|
|
225
249
|
#
|
226
250
|
# @raises [RuntimeError] If the task's fiber is the current fiber.
|
227
251
|
# @returns [Object] The final expression/result of the task's block.
|
252
|
+
# @asynchronous This method is thread-safe.
|
228
253
|
def wait
|
229
254
|
raise "Cannot wait on own fiber!" if Fiber.current.equal?(@fiber)
|
230
255
|
|
231
|
-
#
|
232
|
-
|
233
|
-
@
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
if @status == :failed
|
238
|
-
raise @result
|
239
|
-
else
|
240
|
-
return @result
|
256
|
+
# Wait for the task to complete - Promise handles all the complexity:
|
257
|
+
begin
|
258
|
+
@promise.wait
|
259
|
+
rescue Promise::Cancel
|
260
|
+
# For backward compatibility, stopped tasks return nil
|
261
|
+
return nil
|
241
262
|
end
|
242
263
|
end
|
243
264
|
|
244
265
|
# Access the result of the task without waiting. May be nil if the task is not completed. Does not raise exceptions.
|
245
|
-
|
266
|
+
def result
|
267
|
+
value = @promise.value
|
268
|
+
# For backward compatibility, return nil for stopped tasks
|
269
|
+
if @promise.cancelled?
|
270
|
+
nil
|
271
|
+
else
|
272
|
+
value
|
273
|
+
end
|
274
|
+
end
|
246
275
|
|
247
276
|
# Stop the task and all of its children.
|
248
277
|
#
|
@@ -375,29 +404,25 @@ module Async
|
|
375
404
|
|
376
405
|
# Attempt to remove this node from the task tree.
|
377
406
|
consume
|
378
|
-
|
379
|
-
# If this task was being used as a future, signal completion here:
|
380
|
-
if @finished
|
381
|
-
@finished.signal(self)
|
382
|
-
@finished = nil
|
383
|
-
end
|
384
407
|
end
|
385
408
|
|
386
409
|
# State transition into the completed state.
|
387
410
|
def completed!(result)
|
388
|
-
|
389
|
-
@
|
411
|
+
# Resolve the promise with the result:
|
412
|
+
@promise&.resolve(result)
|
390
413
|
end
|
391
414
|
|
392
415
|
# State transition into the failed state.
|
393
416
|
def failed!(exception = false)
|
394
|
-
|
395
|
-
@
|
417
|
+
# Reject the promise with the exception:
|
418
|
+
@promise&.reject(exception)
|
396
419
|
end
|
397
420
|
|
398
421
|
def stopped!
|
399
422
|
# Console.info(self, status:) {"Task #{self} was stopped with #{@children&.size.inspect} children!"}
|
400
|
-
|
423
|
+
|
424
|
+
# Cancel the promise:
|
425
|
+
@promise&.cancel
|
401
426
|
|
402
427
|
stopped = false
|
403
428
|
|
@@ -442,7 +467,7 @@ module Async
|
|
442
467
|
|
443
468
|
@fiber.async_task = self
|
444
469
|
|
445
|
-
self.
|
470
|
+
(Fiber.scheduler || self.reactor).resume(@fiber)
|
446
471
|
end
|
447
472
|
end
|
448
473
|
end
|
data/lib/async/variable.rb
CHANGED
@@ -2,16 +2,21 @@
|
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2021-2025, by Samuel Williams.
|
5
|
+
# Copyright, 2025, by Shopify Inc.
|
5
6
|
|
6
7
|
require_relative "condition"
|
7
8
|
|
8
9
|
module Async
|
9
10
|
# A synchronization primitive that allows one task to wait for another task to resolve a value.
|
11
|
+
#
|
12
|
+
# @deprecated Use {Async::Promise} instead.
|
10
13
|
class Variable
|
11
14
|
# Create a new variable.
|
12
15
|
#
|
13
16
|
# @parameter condition [Condition] The condition to use for synchronization.
|
14
17
|
def initialize(condition = Condition.new)
|
18
|
+
warn("`Async::Variable` is deprecated, use `Async::Promise` instead.", category: :deprecated, uplevel: 1) if $VERBOSE
|
19
|
+
|
15
20
|
@condition = condition
|
16
21
|
@value = nil
|
17
22
|
end
|
data/lib/async/version.rb
CHANGED
data/lib/async/waiter.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2022-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2024, by Patrik Wenger.
|
6
|
+
# Copyright, 2025, by Shopify Inc.
|
6
7
|
|
7
8
|
module Async
|
8
9
|
# A composable synchronization primitive, which allows one task to wait for a number of other tasks to complete. It can be used in conjunction with {Semaphore} and/or {Barrier}.
|
10
|
+
#
|
9
11
|
# @deprecated `Async::Waiter` is deprecated, use `Async::Barrier` instead.
|
10
12
|
class Waiter
|
11
13
|
# Create a waiter instance.
|
data/lib/kernel/sync.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
# Copyright, 2020, by Brian Morearty.
|
6
6
|
# Copyright, 2024, by Patrik Wenger.
|
7
|
+
# Copyright, 2025, by Shopify Inc.
|
7
8
|
|
8
9
|
require_relative "../async/reactor"
|
9
10
|
|
@@ -30,7 +31,10 @@ module Kernel
|
|
30
31
|
reactor = Async::Reactor.new
|
31
32
|
|
32
33
|
begin
|
33
|
-
|
34
|
+
# Use finished: false to suppress warnings since we're handling exceptions explicitly
|
35
|
+
task = reactor.async(annotation: annotation, finished: false, &block)
|
36
|
+
reactor.run
|
37
|
+
return task.wait
|
34
38
|
ensure
|
35
39
|
Fiber.set_scheduler(nil)
|
36
40
|
end
|
data/readme.md
CHANGED
@@ -35,6 +35,31 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
|
|
35
35
|
|
36
36
|
Please see the [project releases](https://socketry.github.io/async/releases/index) for all releases.
|
37
37
|
|
38
|
+
### v2.32.0
|
39
|
+
|
40
|
+
- Introduce `Queue#waiting_count` and `PriorityQueue#waiting_count`. Generally for statistics/testing purposes only.
|
41
|
+
|
42
|
+
### v2.31.0
|
43
|
+
|
44
|
+
- Introduce `Async::Deadline` for precise timeout management in compound operations.
|
45
|
+
|
46
|
+
### v2.30.0
|
47
|
+
|
48
|
+
- Add timeout support to `Async::Queue#dequeue` and `Async::Queue#pop` methods.
|
49
|
+
- Add timeout support to `Async::PriorityQueue#dequeue` and `Async::PriorityQueue#pop` methods.
|
50
|
+
- Add `closed?` method to `Async::PriorityQueue` for full queue interface compatibility.
|
51
|
+
- Support non-blocking operations using `timeout: 0` parameter.
|
52
|
+
|
53
|
+
### v2.29.0
|
54
|
+
|
55
|
+
This release introduces thread-safety as a core concept of Async. Many core classes now have thread-safe guarantees, allowing them to be used safely across multiple threads.
|
56
|
+
|
57
|
+
- Thread-safe `Async::Condition` and `Async::Notification`, implemented using `Thread::Queue`.
|
58
|
+
- Thread-safe `Async::Queue` and `Async::LimitedQueue`, implemented using `Thread::Queue` and `Thread::LimitedQueue` respectively.
|
59
|
+
- `Async::Variable` is deprecated in favor of `Async::Promise`.
|
60
|
+
- [Introduce `Async::Promise`](https://socketry.github.io/async/releases/index#introduce-async::promise)
|
61
|
+
- [Introduce `Async::PriorityQueue`](https://socketry.github.io/async/releases/index#introduce-async::priorityqueue)
|
62
|
+
|
38
63
|
### v2.28.1
|
39
64
|
|
40
65
|
- Fix race condition between `Async::Barrier#stop` and finish signalling.
|
@@ -59,34 +84,6 @@ Please see the [project releases](https://socketry.github.io/async/releases/inde
|
|
59
84
|
|
60
85
|
- Updated documentation and agent context.
|
61
86
|
|
62
|
-
### v2.27.0
|
63
|
-
|
64
|
-
- `Async::Task#stop` supports an optional `cause:` argument (that defaults to `$!`), which allows you to specify the cause (exception) for stopping the task.
|
65
|
-
- Add thread-safety agent context.
|
66
|
-
|
67
|
-
### v2.26.0
|
68
|
-
|
69
|
-
- `Async::Notification#signal` now returns `true` if a task was signaled, `false` otherwise, providing better feedback for notification operations.
|
70
|
-
- `require "async/limited_queue"` is required to use `Async::LimitedQueue` without a deprecation warning. `Async::LimitedQueue` is not deprecated, but it's usage via `async/queue` is deprecated.
|
71
|
-
- `Async::Task#sleep` is deprecated with no replacement.
|
72
|
-
- `Async::Task.yield` is deprecated with no replacement.
|
73
|
-
- `Async::Scheduler#async` is deprecated, use `Async{}`, `Sync{}` or `Async::Task#async` instead.
|
74
|
-
- Agent context is now available, via the [`agent-context` gem](https://github.com/ioquatix/agent-context).
|
75
|
-
- [`Async::Barrier` Improvements](https://socketry.github.io/async/releases/index#async::barrier-improvements)
|
76
|
-
- [Introduce `Async::Queue#close`](https://socketry.github.io/async/releases/index#introduce-async::queue#close)
|
77
|
-
|
78
|
-
### v2.25.0
|
79
|
-
|
80
|
-
- Added support for `io_select` hook in the fiber scheduler, allowing non-blocking `IO.select` operations. This enables better integration with code that uses `IO.select` for multiplexing IO operations.
|
81
|
-
- [Use `IO::Event::WorkerPool` for Blocking Operations](https://socketry.github.io/async/releases/index#use-io::event::workerpool-for-blocking-operations)
|
82
|
-
- [Better handling of `IO#close` using `fiber_interrupt`](https://socketry.github.io/async/releases/index#better-handling-of-io#close-using-fiber_interrupt)
|
83
|
-
|
84
|
-
### v2.24.0
|
85
|
-
|
86
|
-
- Ruby v3.1 support is dropped.
|
87
|
-
- `Async::Wrapper` which was previously deprecated, is now removed.
|
88
|
-
- [Flexible Timeouts](https://socketry.github.io/async/releases/index#flexible-timeouts)
|
89
|
-
|
90
87
|
## See Also
|
91
88
|
|
92
89
|
- [async-http](https://github.com/socketry/async-http) — Asynchronous HTTP client/server.
|
data/releases.md
CHANGED
@@ -1,5 +1,98 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v2.32.0
|
4
|
+
|
5
|
+
- Introduce `Queue#waiting_count` and `PriorityQueue#waiting_count`. Generally for statistics/testing purposes only.
|
6
|
+
|
7
|
+
## v2.31.0
|
8
|
+
|
9
|
+
- Introduce `Async::Deadline` for precise timeout management in compound operations.
|
10
|
+
|
11
|
+
## v2.30.0
|
12
|
+
|
13
|
+
- Add timeout support to `Async::Queue#dequeue` and `Async::Queue#pop` methods.
|
14
|
+
- Add timeout support to `Async::PriorityQueue#dequeue` and `Async::PriorityQueue#pop` methods.
|
15
|
+
- Add `closed?` method to `Async::PriorityQueue` for full queue interface compatibility.
|
16
|
+
- Support non-blocking operations using `timeout: 0` parameter.
|
17
|
+
|
18
|
+
## v2.29.0
|
19
|
+
|
20
|
+
This release introduces thread-safety as a core concept of Async. Many core classes now have thread-safe guarantees, allowing them to be used safely across multiple threads.
|
21
|
+
|
22
|
+
- Thread-safe `Async::Condition` and `Async::Notification`, implemented using `Thread::Queue`.
|
23
|
+
- Thread-safe `Async::Queue` and `Async::LimitedQueue`, implemented using `Thread::Queue` and `Thread::LimitedQueue` respectively.
|
24
|
+
- `Async::Variable` is deprecated in favor of `Async::Promise`.
|
25
|
+
|
26
|
+
### Introduce `Async::Promise`
|
27
|
+
|
28
|
+
This release introduces the new `Async::Promise` class and refactors `Async::Task` to use promises for state management internally. This architectural improvement achieves the design goal that "a task should be a promise with attached computation and cancellation handling."
|
29
|
+
|
30
|
+
- **Thread-safe promise implementation** with immutable state transitions.
|
31
|
+
- **Consistent state management** using symbols: `:completed`, `:failed`, `:cancelled`.
|
32
|
+
- **Promise cancellation** with `cancel()` method and `Cancel` exception class.
|
33
|
+
- **Comprehensive test coverage** with 47 new test cases covering all edge cases.
|
34
|
+
|
35
|
+
<!-- end list -->
|
36
|
+
|
37
|
+
``` ruby
|
38
|
+
require 'async/promise'
|
39
|
+
|
40
|
+
# Basic promise usage - works independently of Async framework
|
41
|
+
promise = Async::Promise.new
|
42
|
+
|
43
|
+
# In another thread or fiber, resolve the promise
|
44
|
+
Thread.new do
|
45
|
+
sleep(1) # Simulate some work
|
46
|
+
promise.resolve("Hello, World!")
|
47
|
+
end
|
48
|
+
|
49
|
+
# Wait for the result
|
50
|
+
result = promise.wait
|
51
|
+
puts result # => "Hello, World!"
|
52
|
+
|
53
|
+
# Check promise state
|
54
|
+
puts promise.resolved? # => true
|
55
|
+
puts promise.completed? # => true
|
56
|
+
```
|
57
|
+
|
58
|
+
Promises bridge Thread and Fiber concurrency models - a promise resolved in one thread can be awaited in a fiber, and vice versa.
|
59
|
+
|
60
|
+
### Introduce `Async::PriorityQueue`
|
61
|
+
|
62
|
+
The new `Async::PriorityQueue` provides a thread-safe, fiber-aware queue where consumers can specify priority levels. Higher priority consumers are served first when items become available, with FIFO ordering maintained for equal priorities. This is useful for implementing priority-based task processing systems where critical operations need to be handled before lower priority work.
|
63
|
+
|
64
|
+
``` ruby
|
65
|
+
require 'async'
|
66
|
+
require 'async/priority_queue'
|
67
|
+
|
68
|
+
Async do
|
69
|
+
queue = Async::PriorityQueue.new
|
70
|
+
|
71
|
+
# Start consumers with different priorities
|
72
|
+
low_priority = async do
|
73
|
+
puts "Low priority consumer got: #{queue.dequeue(priority: 1)}"
|
74
|
+
end
|
75
|
+
|
76
|
+
medium_priority = async do
|
77
|
+
puts "Medium priority consumer got: #{queue.dequeue(priority: 5)}"
|
78
|
+
end
|
79
|
+
|
80
|
+
high_priority = async do
|
81
|
+
puts "High priority consumer got: #{queue.dequeue(priority: 10)}"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add items to the queue
|
85
|
+
queue.push("first item")
|
86
|
+
queue.push("second item")
|
87
|
+
queue.push("third item")
|
88
|
+
|
89
|
+
# Output:
|
90
|
+
# High priority consumer got: first item
|
91
|
+
# Medium priority consumer got: second item
|
92
|
+
# Low priority consumer got: third item
|
93
|
+
end
|
94
|
+
```
|
95
|
+
|
3
96
|
## v2.28.1
|
4
97
|
|
5
98
|
- Fix race condition between `Async::Barrier#stop` and finish signalling.
|
data.tar.gz.sig
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
���\��v�]3^į38��h�V��&,���a�j�5P��t�h���)'��C��,� AGp���G#�zi)c��d���n��.:���檾A�|F~O^�D�Ж���,���Z46J!{���m���6���p�k~S
|
2
|
+
{z+�s������_ԗ�����i�B��lv�w��ӛQ~9p��_�m������2���)r�e}M�u��Y��IL;�����ҳ��C_zɀt�����ͦ/�����+��� �}�ve7��F�94�0� 6"�� eڜ���+s�Oz�RL���;hGS��#Q{���=~2=n��?P�;��Xy��N(6�%��~��^��\�M\��N�������>I/6D��ƍ��
|