async 2.33.0 → 2.34.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: 7c7cade6749e8ff99e40808a8aaf7f334312e1975e4f4e349b96271cbc157caf
4
- data.tar.gz: 42d523251a8904087917cb000f26d5b17c5bb69ec45d88cfbb6deebaade1923f
3
+ metadata.gz: 05c05a5ba6d2dc436a12de9ff3cb562b3376892fd8c6615be8c4eb654d570fef
4
+ data.tar.gz: 2cb04cbecc3237b4476e5f42e4e130e8116ea10890c1b766140b238451c13f81
5
5
  SHA512:
6
- metadata.gz: 13d7fedf21c2ecf283d5e541af4e11f523eefe0c8cc084b7a174483a5483fe6f58d50d8472cd60e393e537d3c12c5312500b54be0f13092ae01ac1ebddd64488
7
- data.tar.gz: 2cbd88bab04832b9fc23773977e74dd494f4a9cc8b53e9ec45af04fa80bcdbbbe411c3c8b661a4205e36f7d70f1a789b70e20e1b3c4a77ded4d5b5a96658a5a2
6
+ metadata.gz: 16d4488b9d5fa9702bba6fead00c9c772402f792817daba79596cdf573e9f7b111ab77b85166d9b7b93c92babd9a717bf8fe11aa56ae67beded351fda99eaa7b
7
+ data.tar.gz: 289f3a854fcda83b35e92a1ebc14425722fdc856f8dfaa60e29f630ba79b3d72269816f7f01f940aa8926685701cd5ef1ba285a0576f91354107083cfaf96905
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/async/idler.rb CHANGED
@@ -13,10 +13,13 @@ module Async
13
13
  # @parameter maximum_load [Numeric] The maximum load before we start shedding work.
14
14
  # @parameter backoff [Numeric] The initial backoff time, used for delaying work.
15
15
  # @parameter parent [Interface(:async) | Nil] The parent task to use for async operations.
16
- def initialize(maximum_load = 0.8, backoff: 0.01, parent: nil)
16
+ def initialize(maximum_load = 0.8, backoff: 0.001, parent: nil)
17
17
  @maximum_load = maximum_load
18
18
  @backoff = backoff
19
+ @current = backoff
20
+
19
21
  @parent = parent
22
+ @mutex = Mutex.new
20
23
  end
21
24
 
22
25
  # Wait until the system is idle, then execute the given block in a new task.
@@ -38,20 +41,29 @@ module Async
38
41
  #
39
42
  # If the scheduler is overloaded, this method will sleep for an exponentially increasing amount of time.
40
43
  def wait
41
- scheduler = Fiber.scheduler
42
- backoff = nil
43
-
44
- while true
45
- load = scheduler.load
46
-
47
- break if load < @maximum_load
44
+ @mutex.synchronize do
45
+ scheduler = Fiber.scheduler
48
46
 
49
- if backoff
50
- sleep(backoff)
51
- backoff *= 2.0
52
- else
53
- scheduler.yield
54
- backoff = @backoff
47
+ while true
48
+ load = scheduler.load
49
+
50
+ if load <= @maximum_load
51
+ # Even though load is okay, if @current is high, we were recently overloaded. Sleep proportionally to prevent burst after load drop:
52
+ if @current > @backoff
53
+ # Sleep a fraction of @current to rate limit:
54
+ sleep(@current - @backoff)
55
+
56
+ # Decay @current gently towards @backoff:
57
+ alpha = 0.99
58
+ @current *= alpha + (1.0 - alpha) * (load / @maximum_load)
59
+ end
60
+
61
+ break
62
+ else
63
+ # We're overloaded, so increase backoff:
64
+ @current *= (load / @maximum_load)
65
+ sleep(@current)
66
+ end
55
67
  end
56
68
  end
57
69
  end
data/lib/async/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2017-2025, by Samuel Williams.
5
5
 
6
6
  module Async
7
- VERSION = "2.33.0"
7
+ VERSION = "2.34.0"
8
8
  end
data/lib/async.rb CHANGED
@@ -9,6 +9,7 @@ require_relative "async/reactor"
9
9
 
10
10
  require_relative "kernel/async"
11
11
  require_relative "kernel/sync"
12
+ require_relative "kernel/barrier"
12
13
 
13
14
  # Asynchronous programming framework.
14
15
  module Async
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require_relative "sync"
7
+ require_relative "../async/barrier"
8
+ require_relative "../async/idler"
9
+
10
+ module Kernel
11
+ # Create a barrier, yield it to the block, and then wait for all tasks to complete.
12
+ #
13
+ # If no scheduler is running, one will be created automatically for the duration of the block.
14
+ #
15
+ # By default, the barrier uses an `Async::Idler` to manage load, but this can be overridden by providing a different parent or `nil` to disable load management.
16
+ #
17
+ # @parameter parent [Task | Semaphore | Nil] The parent for holding any children tasks.
18
+ # @parameter **options [Hash] Additional options passed to {Kernel::Sync}.
19
+ # @public Since *Async v2.34*.
20
+ def Barrier(parent: Async::Idler.new, **options)
21
+ Sync(**options) do |task|
22
+ barrier = ::Async::Barrier.new(parent: parent)
23
+
24
+ yield barrier
25
+
26
+ barrier.wait
27
+ ensure
28
+ barrier&.stop
29
+ end
30
+ end
31
+ end
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.34.0
39
+
40
+ - [`Kernel::Barrier` Convenience Interface](https://socketry.github.io/async/releases/index#kernel::barrier-convenience-interface)
41
+
38
42
  ### v2.33.0
39
43
 
40
44
  - Introduce `Async::Promise.fulfill` for optional promise resolution.
@@ -80,10 +84,6 @@ This release introduces thread-safety as a core concept of Async. Many core clas
80
84
 
81
85
  - Suppress excessive warning in `Async::Scheduler#async`.
82
86
 
83
- ### v2.27.3
84
-
85
- - Ensure trace attributes are strings, fixes integration with OpenTelemetry.
86
-
87
87
  ## See Also
88
88
 
89
89
  - [async-http](https://github.com/socketry/async-http) — Asynchronous HTTP client/server.
data/releases.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # Releases
2
2
 
3
+ ## v2.34.0
4
+
5
+ ### `Kernel::Barrier` Convenience Interface
6
+
7
+ Starting multiple concurrent tasks and waiting for them to finish is a common pattern. This change introduces a small ergonomic helper, `Barrier`, defined in `Kernel`, that encapsulates this behavior: it creates an `Async::Barrier`, yields it to a block, waits for completion (using `Sync` to run a reactor if needed), and ensures remaining tasks are stopped on exit.
8
+
9
+ ``` ruby
10
+ require 'async'
11
+
12
+ Barrier do |barrier|
13
+ 3.times do |i|
14
+ barrier.async do |task|
15
+ sleep(rand * 0.1) # Simulate work
16
+ puts "Task #{i} completed"
17
+ end
18
+ end
19
+ end
20
+
21
+ # All tasks are guaranteed to complete or be stopped when the block exits.
22
+ ```
23
+
24
+ If an exception is raised by a task, it will be propagated to the caller, and any remaining tasks will be stopped. The `parent:` parameter can be used to specify a parent task for the barrier, otherwise it will use the current task if available, or create a new reactor if not.
25
+
3
26
  ## v2.33.0
4
27
 
5
28
  - Introduce `Async::Promise.fulfill` for optional promise resolution.
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.33.0
4
+ version: 2.34.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -180,6 +180,7 @@ files:
180
180
  - lib/async/version.rb
181
181
  - lib/async/waiter.rb
182
182
  - lib/kernel/async.rb
183
+ - lib/kernel/barrier.rb
183
184
  - lib/kernel/sync.rb
184
185
  - lib/metrics/provider/async.rb
185
186
  - lib/metrics/provider/async/task.rb
metadata.gz.sig CHANGED
Binary file