async 2.36.0 → 2.37.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/clock.rb +2 -1
- data/lib/async/error.rb +17 -0
- data/lib/async/fork_handler.rb +2 -2
- data/lib/async/loop.rb +84 -0
- data/lib/async/node.rb +2 -2
- data/lib/async/promise.rb +45 -3
- data/lib/async/scheduler.rb +2 -2
- data/lib/async/task.rb +3 -13
- data/lib/async/version.rb +2 -2
- data/lib/async.rb +1 -0
- data/license.md +1 -1
- data/readme.md +4 -4
- data/releases.md +4 -0
- data.tar.gz.sig +0 -0
- metadata +3 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59107a94e01d137ccdba59b8204cfbb0a223418e3ce5374386fe9c12bf7f455d
|
|
4
|
+
data.tar.gz: 7a2dfe8b7b15bbe059e898faa3fd59b1b024e4156744897246368763ed35a106
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 03c75631a638f69c5e3bbd720c75c9edc1bb7f9a71a296fdc5f089532d0eb86d91babc5760cd97afdbe60ae1aabd226de2ae325b5d8f458538376faa18f3d820
|
|
7
|
+
data.tar.gz: c328e8223cd12b1e44329737156163fc152f981caedd7af7bb5b1f3920ed96cf6a93f18564a49d9d2e52f03bdd2e61aec39b519e5c13c9830553e2c709c0d486
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/lib/async/clock.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2018-
|
|
4
|
+
# Copyright, 2018-2026, by Samuel Williams.
|
|
5
|
+
# Copyright, 2026, by Shopify Inc.
|
|
5
6
|
|
|
6
7
|
module Async
|
|
7
8
|
# A convenient wrapper around the internal monotonic clock.
|
data/lib/async/error.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
module Async
|
|
7
|
+
# Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`.
|
|
8
|
+
# @public Since *Async v1*.
|
|
9
|
+
class TimeoutError < StandardError
|
|
10
|
+
# Create a new timeout error.
|
|
11
|
+
#
|
|
12
|
+
# @parameter message [String] The error message.
|
|
13
|
+
def initialize(message = "execution expired")
|
|
14
|
+
super
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/async/fork_handler.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2025, by Shopify Inc.
|
|
5
|
-
# Copyright, 2025, by Samuel Williams.
|
|
4
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
5
|
+
# Copyright, 2025-2026, by Samuel Williams.
|
|
6
6
|
|
|
7
7
|
module Async
|
|
8
8
|
# Private module that hooks into Process._fork to handle fork events.
|
data/lib/async/loop.rb
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require "console"
|
|
7
|
+
|
|
8
|
+
module Async
|
|
9
|
+
# @namespace
|
|
10
|
+
module Loop
|
|
11
|
+
# Execute a block repeatedly at quantized (time-aligned) intervals.
|
|
12
|
+
#
|
|
13
|
+
# The alignment is computed modulo the current clock time in seconds. For example, with
|
|
14
|
+
# `interval: 60`, executions will occur at 00:00, 01:00, 02:00, etc., regardless of when
|
|
15
|
+
# the loop is started. With `interval: 300` (5 minutes), executions align to 00:00, 00:05,
|
|
16
|
+
# 00:10, etc.
|
|
17
|
+
#
|
|
18
|
+
# This is particularly useful for tasks that should run at predictable wall-clock times,
|
|
19
|
+
# such as metrics collection, periodic cleanup, or scheduled jobs that need to align
|
|
20
|
+
# across multiple processes.
|
|
21
|
+
#
|
|
22
|
+
# If an error occurs during block execution, it is logged and the loop continues.
|
|
23
|
+
#
|
|
24
|
+
# @example Run every minute at :00 seconds:
|
|
25
|
+
# Async::Loop.quantized(interval: 60) do
|
|
26
|
+
# puts "Current time: #{Time.now}"
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# @example Run every 5 minutes aligned to the hour:
|
|
30
|
+
# Async::Loop.quantized(interval: 300) do
|
|
31
|
+
# collect_metrics
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# @parameter interval [Numeric] The interval in seconds. Executions will align to multiples of this interval based on the current time.
|
|
35
|
+
# @yields The block to execute at each interval.
|
|
36
|
+
#
|
|
37
|
+
# @public Since *Async v2.37*.
|
|
38
|
+
def self.quantized(interval: 60, &block)
|
|
39
|
+
while true
|
|
40
|
+
# Compute the wait time to the next interval:
|
|
41
|
+
wait = interval - (Time.now.to_f % interval)
|
|
42
|
+
if wait.positive?
|
|
43
|
+
# Sleep until the next interval boundary:
|
|
44
|
+
sleep(wait)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
yield
|
|
49
|
+
rescue => error
|
|
50
|
+
Console.error(self, "Loop error:", error)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Execute a block repeatedly with a fixed delay between executions.
|
|
56
|
+
#
|
|
57
|
+
# Unlike {quantized}, this method waits for the specified interval *after* each execution
|
|
58
|
+
# completes. This means the actual time between the start of successive executions will be
|
|
59
|
+
# `interval + execution_time`.
|
|
60
|
+
#
|
|
61
|
+
# If an error occurs during block execution, it is logged and the loop continues.
|
|
62
|
+
#
|
|
63
|
+
# @example Run every 5 seconds (plus execution time):
|
|
64
|
+
# Async::Loop.periodic(interval: 5) do
|
|
65
|
+
# process_queue
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# @parameter interval [Numeric] The delay in seconds between executions.
|
|
69
|
+
# @yields The block to execute periodically.
|
|
70
|
+
#
|
|
71
|
+
# @public Since *Async v2.37*.
|
|
72
|
+
def self.periodic(interval: 60, &block)
|
|
73
|
+
while true
|
|
74
|
+
begin
|
|
75
|
+
yield
|
|
76
|
+
rescue => error
|
|
77
|
+
Console.error(self, "Loop error:", error)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
sleep(interval)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
data/lib/async/node.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2017-
|
|
4
|
+
# Copyright, 2017-2026, by Samuel Williams.
|
|
5
5
|
# Copyright, 2017, by Kent Gruber.
|
|
6
6
|
# Copyright, 2022, by Shannon Skipper.
|
|
7
|
-
# Copyright, 2025, by Shopify Inc.
|
|
7
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
8
8
|
|
|
9
9
|
require "fiber/annotation"
|
|
10
10
|
|
data/lib/async/promise.rb
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
# Copyright, 2025, by Shopify Inc.
|
|
5
5
|
# Copyright, 2025-2026, by Samuel Williams.
|
|
6
6
|
|
|
7
|
+
require_relative "error"
|
|
8
|
+
require_relative "deadline"
|
|
9
|
+
|
|
7
10
|
module Async
|
|
8
11
|
# A promise represents a value that will be available in the future.
|
|
9
12
|
# Unlike Condition, once resolved (or rejected), all future waits return immediately
|
|
@@ -77,17 +80,23 @@ module Async
|
|
|
77
80
|
# Wait for the promise to be resolved and return the value.
|
|
78
81
|
# If already resolved, returns immediately. If rejected, raises the stored exception.
|
|
79
82
|
#
|
|
83
|
+
# @parameter timeout [Numeric | Nil] Maximum time to wait. If nil, waits indefinitely. If 0, raises immediately if not resolved.
|
|
80
84
|
# @returns [Object] The resolved value.
|
|
81
85
|
# @raises [Exception] The rejected or cancelled exception.
|
|
82
|
-
|
|
86
|
+
# @raises [Async::TimeoutError] If timeout expires before the promise is resolved.
|
|
87
|
+
def wait(timeout: nil)
|
|
83
88
|
@mutex.synchronize do
|
|
84
89
|
# Increment waiting count:
|
|
85
90
|
@waiting += 1
|
|
86
91
|
|
|
87
92
|
begin
|
|
88
93
|
# Wait for resolution if not already resolved:
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
unless @resolved
|
|
95
|
+
if timeout.nil?
|
|
96
|
+
wait_indefinitely
|
|
97
|
+
else
|
|
98
|
+
wait_with_timeout(timeout)
|
|
99
|
+
end
|
|
91
100
|
end
|
|
92
101
|
|
|
93
102
|
# Return value or raise exception based on resolution type:
|
|
@@ -104,6 +113,39 @@ module Async
|
|
|
104
113
|
end
|
|
105
114
|
end
|
|
106
115
|
|
|
116
|
+
# Wait indefinitely for the promise to be resolved.
|
|
117
|
+
private def wait_indefinitely
|
|
118
|
+
until @resolved
|
|
119
|
+
@condition.wait(@mutex)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Wait for the promise to be resolved, respecting the deadline timeout.
|
|
124
|
+
# @parameter timeout [Numeric] The timeout duration.
|
|
125
|
+
# @raises [Async::TimeoutError] If the timeout expires before resolution.
|
|
126
|
+
private def wait_with_timeout(timeout)
|
|
127
|
+
# Create deadline for timeout tracking:
|
|
128
|
+
deadline = Deadline.start(timeout)
|
|
129
|
+
|
|
130
|
+
# Handle immediate timeout (non-blocking):
|
|
131
|
+
if deadline == Deadline::Zero && !@resolved
|
|
132
|
+
raise Async::TimeoutError, "Promise wait not resolved!"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Wait with deadline tracking:
|
|
136
|
+
until @resolved
|
|
137
|
+
# Get remaining time for this wait iteration:
|
|
138
|
+
remaining = deadline.remaining
|
|
139
|
+
|
|
140
|
+
# Check if deadline has expired before waiting:
|
|
141
|
+
if remaining <= 0
|
|
142
|
+
raise Async::TimeoutError, "Promise wait timed out!"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
@condition.wait(@mutex, remaining)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
107
149
|
# Resolve the promise with a value.
|
|
108
150
|
# All current and future waiters will receive this value.
|
|
109
151
|
# Can only be called once - subsequent calls are ignored.
|
data/lib/async/scheduler.rb
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2020-
|
|
4
|
+
# Copyright, 2020-2026, by Samuel Williams.
|
|
5
5
|
# Copyright, 2020, by Jun Jiang.
|
|
6
6
|
# Copyright, 2021, by Julien Portalier.
|
|
7
|
-
# Copyright, 2025, by Shopify Inc.
|
|
7
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
8
8
|
|
|
9
9
|
require_relative "clock"
|
|
10
10
|
require_relative "task"
|
data/lib/async/task.rb
CHANGED
|
@@ -1,36 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
|
-
# Copyright, 2017-
|
|
4
|
+
# Copyright, 2017-2026, by Samuel Williams.
|
|
5
5
|
# Copyright, 2017, by Kent Gruber.
|
|
6
6
|
# Copyright, 2017, by Devin Christensen.
|
|
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
|
+
# Copyright, 2025-2026, by Shopify Inc.
|
|
11
11
|
|
|
12
12
|
require "fiber"
|
|
13
13
|
require "console"
|
|
14
14
|
|
|
15
15
|
require_relative "node"
|
|
16
16
|
require_relative "condition"
|
|
17
|
+
require_relative "error"
|
|
17
18
|
require_relative "promise"
|
|
18
19
|
require_relative "stop"
|
|
19
20
|
|
|
20
21
|
Fiber.attr_accessor :async_task
|
|
21
22
|
|
|
22
23
|
module Async
|
|
23
|
-
# Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`.
|
|
24
|
-
# @public Since *Async v1*.
|
|
25
|
-
class TimeoutError < StandardError
|
|
26
|
-
# Create a new timeout error.
|
|
27
|
-
#
|
|
28
|
-
# @parameter message [String] The error message.
|
|
29
|
-
def initialize(message = "execution expired")
|
|
30
|
-
super
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
24
|
# Represents a sequential unit of work, defined by a block, which is executed concurrently with other tasks. A task can be in one of the following states: `initialized`, `running`, `completed`, `failed`, `cancelled` or `stopped`.
|
|
35
25
|
#
|
|
36
26
|
# ```mermaid
|
data/lib/async/version.rb
CHANGED
data/lib/async.rb
CHANGED
data/license.md
CHANGED
|
@@ -31,7 +31,7 @@ Copyright, 2025, by Jahfer Husain.
|
|
|
31
31
|
Copyright, 2025, by Mark Montroy.
|
|
32
32
|
Copyright, 2025, by Shigeru Nakajima.
|
|
33
33
|
Copyright, 2025, by Alan Wu.
|
|
34
|
-
Copyright, 2025, by Shopify Inc.
|
|
34
|
+
Copyright, 2025-2026, by Shopify Inc.
|
|
35
35
|
Copyright, 2025, by Josh Teeter.
|
|
36
36
|
Copyright, 2025, by Jatin Goyal.
|
|
37
37
|
Copyright, 2025, by Yuhi Sato.
|
data/readme.md
CHANGED
|
@@ -35,6 +35,10 @@ 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.37.0
|
|
39
|
+
|
|
40
|
+
- Introduce `Async::Loop` for robust, time-aligned loops.
|
|
41
|
+
|
|
38
42
|
### v2.36.0
|
|
39
43
|
|
|
40
44
|
- Introduce `Task#wait_all` which recursively waits for all children and self, excepting the current task.
|
|
@@ -73,10 +77,6 @@ Please see the [project releases](https://socketry.github.io/async/releases/inde
|
|
|
73
77
|
|
|
74
78
|
- Introduce `Queue#waiting_count` and `PriorityQueue#waiting_count`. Generally for statistics/testing purposes only.
|
|
75
79
|
|
|
76
|
-
### v2.31.0
|
|
77
|
-
|
|
78
|
-
- Introduce `Async::Deadline` for precise timeout management in compound operations.
|
|
79
|
-
|
|
80
80
|
## See Also
|
|
81
81
|
|
|
82
82
|
- [async-http](https://github.com/socketry/async-http) — Asynchronous HTTP client/server.
|
data/releases.md
CHANGED
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: async
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.37.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -161,10 +161,12 @@ files:
|
|
|
161
161
|
- lib/async/condition.rb
|
|
162
162
|
- lib/async/console.rb
|
|
163
163
|
- lib/async/deadline.rb
|
|
164
|
+
- lib/async/error.rb
|
|
164
165
|
- lib/async/fork_handler.rb
|
|
165
166
|
- lib/async/idler.rb
|
|
166
167
|
- lib/async/limited_queue.rb
|
|
167
168
|
- lib/async/list.rb
|
|
169
|
+
- lib/async/loop.rb
|
|
168
170
|
- lib/async/node.rb
|
|
169
171
|
- lib/async/notification.rb
|
|
170
172
|
- lib/async/priority_queue.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|