async 2.23.1 → 2.24.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36ac96ecf26e32e4804f8e89909dcb7b5f8cdfbed24a4da1a189d027586bd0b1
4
- data.tar.gz: 35fb378abd8183a2a4950a2b57fd4ba6360c740b51a12ff3e67af5a2c443583c
3
+ metadata.gz: 95c1315007ff80d78bdd1538d2b2963ed951db5aa92906ea6f22560612540cbc
4
+ data.tar.gz: 03ffadcf7c2523827bd6c49340df0a846f8ab3214593d897b4359b548a765cef
5
5
  SHA512:
6
- metadata.gz: 5ff90e092ac86b1dd3e5991e48d44d2b6a9abaa1b72d785d3e9419630f3f8fadd241ab21837bbd5ddd4038792e3ac970bf2aa4b2f67f5a473d4be1caa067f1d5
7
- data.tar.gz: 5b71b8eae39aee81b3918f1012c76a4306529292626bf6dc95e79ecebcfa53cda304840655eee56c3392b5a912478ff3178ab4624a2f519ad02d6ce70098f1e2
6
+ metadata.gz: 62c15e0c19e7f3277ca9e4981b0304d5f039367fb0d6b996a8c562be170b3436f66e97f33fcfd5eb0ed8ec84e760ae2b565a022604e026f3b2c797613534fd59
7
+ data.tar.gz: 79c7148d35f3e06243b3845721b8a1ebcf5fd2e6244eab18ac5a0f42894088334df307c4da52d9d9c59b0d9499e457f0990022d570abf109202416cf168211a1
checksums.yaml.gz.sig CHANGED
Binary file
@@ -7,6 +7,7 @@
7
7
 
8
8
  require_relative "clock"
9
9
  require_relative "task"
10
+ require_relative "timeout"
10
11
  require_relative "worker_pool"
11
12
 
12
13
  require "io/event"
@@ -269,16 +270,6 @@ module Async
269
270
  ::Resolv.getaddresses(hostname)
270
271
  end
271
272
 
272
- if IO.method_defined?(:timeout)
273
- private def get_timeout(io)
274
- io.timeout
275
- end
276
- else
277
- private def get_timeout(io)
278
- nil
279
- end
280
- end
281
-
282
273
  # Wait for the specified IO to become ready for the specified events.
283
274
  #
284
275
  # @public Since *Async v2*.
@@ -295,7 +286,7 @@ module Async
295
286
  timer = @timers.after(timeout) do
296
287
  fiber.transfer
297
288
  end
298
- elsif timeout = get_timeout(io)
289
+ elsif timeout = io.timeout
299
290
  # Otherwise, if we default to the io's timeout, we raise an exception:
300
291
  timer = @timers.after(timeout) do
301
292
  fiber.raise(::IO::TimeoutError, "Timeout (#{timeout}s) while waiting for IO to become ready!")
@@ -320,7 +311,7 @@ module Async
320
311
  def io_read(io, buffer, length, offset = 0)
321
312
  fiber = Fiber.current
322
313
 
323
- if timeout = get_timeout(io)
314
+ if timeout = io.timeout
324
315
  timer = @timers.after(timeout) do
325
316
  fiber.raise(::IO::TimeoutError, "Timeout (#{timeout}s) while waiting for IO to become readable!")
326
317
  end
@@ -344,7 +335,7 @@ module Async
344
335
  def io_write(io, buffer, length, offset = 0)
345
336
  fiber = Fiber.current
346
337
 
347
- if timeout = get_timeout(io)
338
+ if timeout = io.timeout
348
339
  timer = @timers.after(timeout) do
349
340
  fiber.raise(::IO::TimeoutError, "Timeout (#{timeout}s) while waiting for IO to become writable!")
350
341
  end
@@ -549,7 +540,7 @@ module Async
549
540
  # @parameter duration [Numeric] The time in seconds, in which the task should complete.
550
541
  # @parameter exception [Class] The exception class to raise.
551
542
  # @parameter message [String] The message to pass to the exception.
552
- # @yields {|duration| ...} The block to execute with a timeout.
543
+ # @yields {|timeout| ...} The block to execute with a timeout.
553
544
  def with_timeout(duration, exception = TimeoutError, message = "execution expired", &block)
554
545
  fiber = Fiber.current
555
546
 
@@ -559,7 +550,11 @@ module Async
559
550
  end
560
551
  end
561
552
 
562
- yield timer
553
+ if block.arity.zero?
554
+ yield
555
+ else
556
+ yield Timeout.new(@timers, timer)
557
+ end
563
558
  ensure
564
559
  timer&.cancel!
565
560
  end
@@ -574,7 +569,7 @@ module Async
574
569
  # @parameter message [String] The message to pass to the exception.
575
570
  # @yields {|duration| ...} The block to execute with a timeout.
576
571
  def timeout_after(duration, exception, message, &block)
577
- with_timeout(duration, exception, message) do |timer|
572
+ with_timeout(duration, exception, message) do
578
573
  yield duration
579
574
  end
580
575
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ module Async
7
+ # Represents a flexible timeout that can be rescheduled or extended.
8
+ # @public Since *Async v2.24*.
9
+ class Timeout
10
+ # Initialize a new timeout.
11
+ def initialize(timers, handle)
12
+ @timers = timers
13
+ @handle = handle
14
+ end
15
+
16
+ # @returns [Numeric] The time remaining until the timeout occurs, in seconds.
17
+ def duration
18
+ @handle.time - @timers.now
19
+ end
20
+
21
+ # Update the duration of the timeout.
22
+ #
23
+ # The duration is relative to the current time, e.g. setting the duration to 5 means the timeout will occur in 5 seconds from now.
24
+ #
25
+ # @parameter value [Numeric] The new duration to assign to the timeout, in seconds.
26
+ def duration=(value)
27
+ self.reschedule(@timers.now + value)
28
+ end
29
+
30
+ # Adjust the timeout by the specified duration.
31
+ #
32
+ # The duration is relative to the timeout time, e.g. adjusting the timeout by 5 increases the current duration by 5 seconds.
33
+ #
34
+ # @parameter duration [Numeric] The duration to adjust the timeout by, in seconds.
35
+ # @returns [Numeric] The new time at which the timeout will occur.
36
+ def adjust(duration)
37
+ self.reschedule(time + duration)
38
+ end
39
+
40
+ # @returns [Numeric] The time at which the timeout will occur, in seconds since {now}.
41
+ def time
42
+ @handle.time
43
+ end
44
+
45
+ # Assign a new time to the timeout, rescheduling it if necessary.
46
+ #
47
+ # @parameter value [Numeric] The new time to assign to the timeout.
48
+ # @returns [Numeric] The new time at which the timeout will occur.
49
+ def time=(value)
50
+ self.reschedule(value)
51
+ end
52
+
53
+ # @returns [Numeric] The current time in the scheduler, relative to the time of this timeout, in seconds.
54
+ def now
55
+ @timers.now
56
+ end
57
+
58
+ # Cancel the timeout, preventing it from executing.
59
+ def cancel!
60
+ @handle.cancel!
61
+ end
62
+
63
+ # @returns [Boolean] Whether the timeout has been cancelled.
64
+ def cancelled?
65
+ @handle.cancelled?
66
+ end
67
+
68
+ # Raised when attempting to reschedule a cancelled timeout.
69
+ class CancelledError < RuntimeError
70
+ end
71
+
72
+ # Reschedule the timeout to occur at the specified time.
73
+ #
74
+ # @parameter time [Numeric] The new time to schedule the timeout for.
75
+ # @returns [Numeric] The new time at which the timeout will occur.
76
+ private def reschedule(time)
77
+ if block = @handle&.block
78
+ @handle.cancel!
79
+
80
+ @handle = @timers.schedule(time, block)
81
+
82
+ return time
83
+ else
84
+ raise CancelledError, "Cannot reschedule a cancelled timeout!"
85
+ end
86
+ end
87
+ end
88
+ end
data/lib/async/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2017-2024, by Samuel Williams.
5
5
 
6
6
  module Async
7
- VERSION = "2.23.1"
7
+ VERSION = "2.24.0"
8
8
  end
@@ -14,7 +14,7 @@ module Async
14
14
  module BlockingOperationWait
15
15
  # Wait for the given work to be executed.
16
16
  #
17
- # @public Since *Async v2.19* and *Ruby v3.4*.
17
+ # @public Since *Async v2.21* and *Ruby v3.4*.
18
18
  # @asynchronous May be non-blocking.
19
19
  #
20
20
  # @parameter work [Proc] The work to execute on a background thread.
data/readme.md CHANGED
@@ -35,6 +35,12 @@ 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.24.0
39
+
40
+ - Ruby v3.1 support is dropped.
41
+ - `Async::Wrapper` which was previously deprecated, is now removed.
42
+ - [Flexible Timeouts](https://socketry.github.io/async/releases/index#flexible-timeouts)
43
+
38
44
  ### v2.23.0
39
45
 
40
46
  - Rename `ASYNC_SCHEDULER_DEFAULT_WORKER_POOL` to `ASYNC_SCHEDULER_WORKER_POOL`.
data/releases.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Releases
2
2
 
3
+ ## v2.24.0
4
+
5
+ - Ruby v3.1 support is dropped.
6
+ - `Async::Wrapper` which was previously deprecated, is now removed.
7
+
8
+ ### Flexible Timeouts
9
+
10
+ When {ruby Async::Scheduler\#with\_timeout} is invoked with a block, it can receive a {ruby Async::Timeout} instance. This allows you to adjust or cancel the timeout while the block is executing. This is useful for long-running tasks that may need to adjust their timeout based on external factors.
11
+
12
+ ``` ruby
13
+ Async do
14
+ Async::Scheduler.with_timeout(5) do |timeout|
15
+ # Do some work that may take a while...
16
+
17
+ if some_condition
18
+ timeout.cancel! # Cancel the timeout
19
+ else
20
+ # Add 10 seconds to the current timeout:
21
+ timeout.adjust(10)
22
+
23
+ # Reduce the timeout by 10 seconds:
24
+ timeout.adjust(-10)
25
+
26
+ # Set the timeout to 10 seconds from now:
27
+ timeout.duration = 10
28
+
29
+ # Increase the current duration:
30
+ timeout.duration += 10
31
+ end
32
+ end
33
+ end
34
+ ```
35
+
3
36
  ## v2.23.0
4
37
 
5
38
  - Rename `ASYNC_SCHEDULER_DEFAULT_WORKER_POOL` to `ASYNC_SCHEDULER_WORKER_POOL`.
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.23.1
4
+ version: 2.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -62,7 +62,7 @@ cert_chain:
62
62
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
63
63
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
64
64
  -----END CERTIFICATE-----
65
- date: 2025-03-10 00:00:00.000000000 Z
65
+ date: 2025-05-03 00:00:00.000000000 Z
66
66
  dependencies:
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: console
@@ -157,12 +157,12 @@ files:
157
157
  - lib/async/semaphore.rb
158
158
  - lib/async/task.md
159
159
  - lib/async/task.rb
160
+ - lib/async/timeout.rb
160
161
  - lib/async/variable.rb
161
162
  - lib/async/version.rb
162
163
  - lib/async/waiter.md
163
164
  - lib/async/waiter.rb
164
165
  - lib/async/worker_pool.rb
165
- - lib/async/wrapper.rb
166
166
  - lib/kernel/async.rb
167
167
  - lib/kernel/sync.rb
168
168
  - lib/metrics/provider/async.rb
metadata.gz.sig CHANGED
Binary file
data/lib/async/wrapper.rb DELETED
@@ -1,67 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2017-2024, by Samuel Williams.
5
- # Copyright, 2017, by Kent Gruber.
6
-
7
- warn "Async::Wrapper is deprecated and will be removed on 2025-03-31. Please use native interfaces instead.", uplevel: 1, category: :deprecated
8
-
9
- module Async
10
- # Represents an asynchronous IO within a reactor.
11
- # @deprecated With no replacement. Prefer native interfaces.
12
- class Wrapper
13
- # An exception that occurs when the asynchronous operation was cancelled.
14
- class Cancelled < StandardError
15
- end
16
-
17
- # @parameter io the native object to wrap.
18
- # @parameter reactor [Reactor] the reactor that is managing this wrapper, or not specified, it's looked up by way of {Task.current}.
19
- def initialize(io, reactor = nil)
20
- @io = io
21
- @reactor = reactor
22
-
23
- @timeout = nil
24
- end
25
-
26
- attr_accessor :reactor
27
-
28
- # Dup the underlying IO.
29
- def dup
30
- self.class.new(@io.dup)
31
- end
32
-
33
- # The underlying native `io`.
34
- attr :io
35
-
36
- # Wait for the io to become readable.
37
- def wait_readable(timeout = @timeout)
38
- @io.to_io.wait_readable(timeout) or raise TimeoutError
39
- end
40
-
41
- # Wait for the io to become writable.
42
- def wait_priority(timeout = @timeout)
43
- @io.to_io.wait_priority(timeout) or raise TimeoutError
44
- end
45
-
46
- # Wait for the io to become writable.
47
- def wait_writable(timeout = @timeout)
48
- @io.to_io.wait_writable(timeout) or raise TimeoutError
49
- end
50
-
51
- # Wait fo the io to become either readable or writable.
52
- # @parameter duration [Float] timeout after the given duration if not `nil`.
53
- def wait_any(timeout = @timeout)
54
- @io.to_io.wait(::IO::READABLE|::IO::WRITABLE|::IO::PRIORITY, timeout) or raise TimeoutError
55
- end
56
-
57
- # Close the underlying IO.
58
- def close
59
- @io.close
60
- end
61
-
62
- # Whether the underlying IO is closed.
63
- def closed?
64
- @io.closed?
65
- end
66
- end
67
- end