async 2.21.1 → 2.23.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/barrier.md +1 -1
- data/lib/async/clock.rb +9 -0
- data/lib/async/condition.md +1 -1
- data/lib/async/limited_queue.rb +7 -0
- data/lib/async/queue.rb +7 -3
- data/lib/async/scheduler.rb +24 -8
- data/lib/async/task.rb +4 -1
- data/lib/async/variable.rb +8 -2
- data/lib/async/version.rb +1 -1
- data/lib/async/worker_pool.rb +14 -1
- data/lib/metrics/provider/async/task.rb +3 -0
- data/lib/traces/provider/async/task.rb +14 -3
- data/readme.md +7 -1
- data/releases.md +25 -1
- data.tar.gz.sig +0 -0
- metadata +32 -14
- 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: dedd26d802fb259e0eff2223a86a54f42b46753ab43014b091a83b97610346a0
|
4
|
+
data.tar.gz: 682ab4c4b3798df642e3937b902f4a0d0762ce0dfd91f5c2430205775b2bbe8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d45d3a428c1403b999be51a831e44206ed1636bbfd7603cee7c4e71d03ec2e2e3941b329deb30b18aa4b1021f0b0360726898017ee48933a2262ebaf6c1980df
|
7
|
+
data.tar.gz: da44d07b913e0efff2aff07ff545f5996806601b33ca69b707c5e462d0858ad85e613b5ad6b8d4d53cdd21dd1d80852acf5b71b7359b3b26c2ab10437730873c
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/barrier.md
CHANGED
data/lib/async/clock.rb
CHANGED
data/lib/async/condition.md
CHANGED
data/lib/async/queue.rb
CHANGED
@@ -45,7 +45,9 @@ module Async
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Compatibility with {::Queue#push}.
|
48
|
-
|
48
|
+
def <<(item)
|
49
|
+
self.push(item)
|
50
|
+
end
|
49
51
|
|
50
52
|
# Add multiple items to the queue.
|
51
53
|
def enqueue(*items)
|
@@ -64,7 +66,9 @@ module Async
|
|
64
66
|
end
|
65
67
|
|
66
68
|
# Compatibility with {::Queue#pop}.
|
67
|
-
|
69
|
+
def pop
|
70
|
+
self.dequeue
|
71
|
+
end
|
68
72
|
|
69
73
|
# Process each item in the queue.
|
70
74
|
#
|
@@ -125,7 +129,7 @@ module Async
|
|
125
129
|
# If the queue is full, this method will block until there is space available.
|
126
130
|
#
|
127
131
|
# @parameter item [Object] The item to add to the queue.
|
128
|
-
def
|
132
|
+
def push(item)
|
129
133
|
while limited?
|
130
134
|
@full.wait
|
131
135
|
end
|
data/lib/async/scheduler.rb
CHANGED
@@ -15,9 +15,17 @@ require "console"
|
|
15
15
|
require "resolv"
|
16
16
|
|
17
17
|
module Async
|
18
|
+
begin
|
19
|
+
require "fiber/profiler"
|
20
|
+
Profiler = Fiber::Profiler
|
21
|
+
rescue LoadError
|
22
|
+
# Fiber::Profiler is not available.
|
23
|
+
Profiler = nil
|
24
|
+
end
|
25
|
+
|
18
26
|
# Handles scheduling of fibers. Implements the fiber scheduler interface.
|
19
27
|
class Scheduler < Node
|
20
|
-
|
28
|
+
WORKER_POOL = ENV.fetch("ASYNC_SCHEDULER_WORKER_POOL", nil).then do |value|
|
21
29
|
value == "true" ? true : nil
|
22
30
|
end
|
23
31
|
|
@@ -42,10 +50,12 @@ module Async
|
|
42
50
|
# @public Since *Async v1*.
|
43
51
|
# @parameter parent [Node | Nil] The parent node to use for task hierarchy.
|
44
52
|
# @parameter selector [IO::Event::Selector] The selector to use for event handling.
|
45
|
-
def initialize(parent = nil, selector: nil, worker_pool:
|
53
|
+
def initialize(parent = nil, selector: nil, profiler: Profiler&.default, worker_pool: WORKER_POOL)
|
46
54
|
super(parent)
|
47
55
|
|
48
56
|
@selector = selector || ::IO::Event::Selector.new(Fiber.current)
|
57
|
+
@profiler = profiler
|
58
|
+
|
49
59
|
@interrupted = false
|
50
60
|
|
51
61
|
@blocked = 0
|
@@ -492,13 +502,19 @@ module Async
|
|
492
502
|
def run(...)
|
493
503
|
Kernel.raise ClosedError if @selector.nil?
|
494
504
|
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
505
|
+
begin
|
506
|
+
@profiler&.start
|
507
|
+
|
508
|
+
initial_task = self.async(...) if block_given?
|
509
|
+
|
510
|
+
self.run_loop do
|
511
|
+
run_once
|
512
|
+
end
|
513
|
+
|
514
|
+
return initial_task
|
515
|
+
ensure
|
516
|
+
@profiler&.stop
|
499
517
|
end
|
500
|
-
|
501
|
-
return initial_task
|
502
518
|
end
|
503
519
|
|
504
520
|
# Start an asynchronous task within the specified reactor. The task will be executed until the first blocking call, at which point it will yield and and this method will return.
|
data/lib/async/task.rb
CHANGED
@@ -181,7 +181,10 @@ module Async
|
|
181
181
|
@status == :completed
|
182
182
|
end
|
183
183
|
|
184
|
-
|
184
|
+
# Alias for {#completed?}.
|
185
|
+
def complete?
|
186
|
+
self.completed?
|
187
|
+
end
|
185
188
|
|
186
189
|
# @attribute [Symbol] The status of the execution of the task, one of `:initialized`, `:running`, `:complete`, `:stopped` or `:failed`.
|
187
190
|
attr :status
|
data/lib/async/variable.rb
CHANGED
@@ -31,7 +31,10 @@ module Async
|
|
31
31
|
condition.signal(value)
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
# Alias for {#resolve}.
|
35
|
+
def value=(value)
|
36
|
+
self.resolve(value)
|
37
|
+
end
|
35
38
|
|
36
39
|
# Whether the value has been resolved.
|
37
40
|
#
|
@@ -48,6 +51,9 @@ module Async
|
|
48
51
|
return @value
|
49
52
|
end
|
50
53
|
|
51
|
-
|
54
|
+
# Alias for {#wait}.
|
55
|
+
def value
|
56
|
+
self.wait
|
57
|
+
end
|
52
58
|
end
|
53
59
|
end
|
data/lib/async/version.rb
CHANGED
data/lib/async/worker_pool.rb
CHANGED
@@ -10,6 +10,7 @@ module Async
|
|
10
10
|
#
|
11
11
|
# @private
|
12
12
|
class WorkerPool
|
13
|
+
# Used to augment the scheduler to add support for blocking operations.
|
13
14
|
module BlockingOperationWait
|
14
15
|
# Wait for the given work to be executed.
|
15
16
|
#
|
@@ -23,7 +24,11 @@ module Async
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
# Execute the given work in a background thread.
|
26
28
|
class Promise
|
29
|
+
# Create a new promise.
|
30
|
+
#
|
31
|
+
# @parameter work [Proc] The work to be done.
|
27
32
|
def initialize(work)
|
28
33
|
@work = work
|
29
34
|
@state = :pending
|
@@ -33,6 +38,7 @@ module Async
|
|
33
38
|
@thread = nil
|
34
39
|
end
|
35
40
|
|
41
|
+
# Execute the work and resolve the promise.
|
36
42
|
def call
|
37
43
|
work = nil
|
38
44
|
|
@@ -67,6 +73,7 @@ module Async
|
|
67
73
|
end
|
68
74
|
end
|
69
75
|
|
76
|
+
# Cancel the work and raise an exception in the background thread.
|
70
77
|
def cancel
|
71
78
|
return unless @work
|
72
79
|
|
@@ -77,6 +84,9 @@ module Async
|
|
77
84
|
end
|
78
85
|
end
|
79
86
|
|
87
|
+
# Wait for the work to be done.
|
88
|
+
#
|
89
|
+
# @returns [Object] The result of the work.
|
80
90
|
def wait
|
81
91
|
@guard.synchronize do
|
82
92
|
while @state == :pending
|
@@ -92,19 +102,22 @@ module Async
|
|
92
102
|
end
|
93
103
|
end
|
94
104
|
|
95
|
-
# A
|
105
|
+
# A background worker thread.
|
96
106
|
class Worker
|
107
|
+
# Create a new worker.
|
97
108
|
def initialize
|
98
109
|
@work = ::Thread::Queue.new
|
99
110
|
@thread = ::Thread.new(&method(:run))
|
100
111
|
end
|
101
112
|
|
113
|
+
# Execute work until the queue is closed.
|
102
114
|
def run
|
103
115
|
while work = @work.pop
|
104
116
|
work.call
|
105
117
|
end
|
106
118
|
end
|
107
119
|
|
120
|
+
# Close the worker thread.
|
108
121
|
def close
|
109
122
|
if thread = @thread
|
110
123
|
@thread = nil
|
@@ -8,10 +8,13 @@ require "metrics/provider"
|
|
8
8
|
|
9
9
|
Metrics::Provider(Async::Task) do
|
10
10
|
ASYNC_TASK_SCHEDULED = Metrics.metric("async.task.scheduled", :counter, description: "The number of tasks scheduled.")
|
11
|
+
ASYNC_TASK_FINISHED = Metrics.metric("async.task.finished", :counter, description: "The number of tasks finished.")
|
11
12
|
|
12
13
|
def schedule(&block)
|
13
14
|
ASYNC_TASK_SCHEDULED.emit(1)
|
14
15
|
|
15
16
|
super(&block)
|
17
|
+
ensure
|
18
|
+
ASYNC_TASK_FINISHED.emit(1)
|
16
19
|
end
|
17
20
|
end
|
@@ -8,20 +8,31 @@ require "traces/provider"
|
|
8
8
|
|
9
9
|
Traces::Provider(Async::Task) do
|
10
10
|
def schedule(&block)
|
11
|
+
# If we are not actively tracing anything, then we can skip this:
|
12
|
+
unless Traces.active?
|
13
|
+
return super(&block)
|
14
|
+
end
|
15
|
+
|
11
16
|
unless self.transient?
|
12
17
|
trace_context = Traces.trace_context
|
13
18
|
end
|
14
19
|
|
20
|
+
attributes = {
|
21
|
+
# We use the instance variable as it corresponds to the user-provided block.
|
22
|
+
"block" => @block,
|
23
|
+
"transient" => self.transient?,
|
24
|
+
}
|
25
|
+
|
26
|
+
# Run the trace in the context of the child task:
|
15
27
|
super do
|
16
28
|
Traces.trace_context = trace_context
|
17
29
|
|
18
30
|
if annotation = self.annotation
|
19
|
-
attributes =
|
20
|
-
"annotation" => annotation
|
21
|
-
}
|
31
|
+
attributes["annotation"] = annotation
|
22
32
|
end
|
23
33
|
|
24
34
|
Traces.trace("async.task", attributes: attributes) do
|
35
|
+
# Yes, this is correct, we already called super above:
|
25
36
|
yield
|
26
37
|
end
|
27
38
|
end
|
data/readme.md
CHANGED
@@ -7,6 +7,7 @@ Async is a composable asynchronous I/O framework for Ruby based on [io-event](ht
|
|
7
7
|
> beautifully designed." *– [janko](https://github.com/janko)*
|
8
8
|
|
9
9
|
[](https://github.com/socketry/async/actions?workflow=Test)
|
10
|
+
[<img src="https://api.gitsponsors.com/api/badge/img?id=87380483" height="20"/>](https://api.gitsponsors.com/api/badge/link?p=U4gCxvzG7eUksiJSe0MSlPHWWhBYryqj6i48tx5L7/r/2NgkAToKb6dEm31bAftU3H+7BVwk3VhUBtE4GHqHJTPfWPR6xo2BQVoT15rFAGAsLFgdT2kKopIfCGV/QDOm7BrkodS2//R7NUMksAdaCQ==)
|
10
11
|
|
11
12
|
## Features
|
12
13
|
|
@@ -23,7 +24,7 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
|
|
23
24
|
|
24
25
|
- [Asynchronous Tasks](https://socketry.github.io/async/guides/asynchronous-tasks/index) - This guide explains how asynchronous tasks work and how to use them.
|
25
26
|
|
26
|
-
- [
|
27
|
+
- [Scheduler](https://socketry.github.io/async/guides/scheduler/index) - This guide gives an overview of how the scheduler is implemented.
|
27
28
|
|
28
29
|
- [Compatibility](https://socketry.github.io/async/guides/compatibility/index) - This guide gives an overview of the compatibility of Async with Ruby and other frameworks.
|
29
30
|
|
@@ -35,6 +36,11 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
|
|
35
36
|
|
36
37
|
Please see the [project releases](https://socketry.github.io/async/releases/index) for all releases.
|
37
38
|
|
39
|
+
### v2.23.0
|
40
|
+
|
41
|
+
- Rename `ASYNC_SCHEDULER_DEFAULT_WORKER_POOL` to `ASYNC_SCHEDULER_WORKER_POOL`.
|
42
|
+
- [Fiber Stall Profiler](https://socketry.github.io/async/releases/index#fiber-stall-profiler)
|
43
|
+
|
38
44
|
### v2.21.1
|
39
45
|
|
40
46
|
- [Worker Pool](https://socketry.github.io/async/releases/index#worker-pool)
|
data/releases.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v2.23.0
|
4
|
+
|
5
|
+
- Rename `ASYNC_SCHEDULER_DEFAULT_WORKER_POOL` to `ASYNC_SCHEDULER_WORKER_POOL`.
|
6
|
+
|
7
|
+
### Fiber Stall Profiler
|
8
|
+
|
9
|
+
After several iterations of experimentation, we are officially introducing the fiber stall profiler, implemented using the optional `fiber-profiler` gem. This gem is not included by default, but can be added to your project:
|
10
|
+
|
11
|
+
``` bash
|
12
|
+
$ bundle add fiber-profiler
|
13
|
+
```
|
14
|
+
|
15
|
+
After adding the gem, you can enable the fiber stall profiler by setting the `FIBER_PROFILER_CAPTURE=true` environment variable:
|
16
|
+
|
17
|
+
``` bash
|
18
|
+
$ FIBER_PROFILER_CAPTURE=true bundle exec ruby -rasync -e 'Async{Fiber.blocking{sleep 0.1}}'
|
19
|
+
Fiber stalled for 0.105 seconds
|
20
|
+
-e:1 in c-call '#<Class:Fiber>#blocking' (0.105s)
|
21
|
+
-e:1 in c-call 'Kernel#sleep' (0.105s)
|
22
|
+
Skipped 1 calls that were too short to be meaningful.
|
23
|
+
```
|
24
|
+
|
25
|
+
The fiber profiler will help you find problems with your code that cause the event loop to stall, which can be a common source of performance issues in asynchronous code.
|
26
|
+
|
3
27
|
## v2.21.1
|
4
28
|
|
5
29
|
### Worker Pool
|
@@ -8,7 +32,7 @@ Ruby 3.4 will feature a new fiber scheduler hook, `blocking_operation_wait` whic
|
|
8
32
|
|
9
33
|
The Async scheduler optionally supports this feature using a worker pool, by using the following environment variable:
|
10
34
|
|
11
|
-
|
35
|
+
ASYNC_SCHEDULER_WORKER_POOL=true
|
12
36
|
|
13
37
|
This will cause the scheduler to use a worker pool for general blocking operations, rather than blocking the event loop.
|
14
38
|
|
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.23.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -31,7 +31,6 @@ authors:
|
|
31
31
|
- Sokolov Yura
|
32
32
|
- Stefan Wrobel
|
33
33
|
- Trevor Turk
|
34
|
-
autorequire:
|
35
34
|
bindir: bin
|
36
35
|
cert_chain:
|
37
36
|
- |
|
@@ -63,7 +62,7 @@ cert_chain:
|
|
63
62
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
64
63
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
65
64
|
-----END CERTIFICATE-----
|
66
|
-
date:
|
65
|
+
date: 2025-02-13 00:00:00.000000000 Z
|
67
66
|
dependencies:
|
68
67
|
- !ruby/object:Gem::Dependency
|
69
68
|
name: console
|
@@ -99,22 +98,42 @@ dependencies:
|
|
99
98
|
requirements:
|
100
99
|
- - "~>"
|
101
100
|
- !ruby/object:Gem::Version
|
102
|
-
version: '1.
|
103
|
-
|
101
|
+
version: '1.9'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '1.9'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: traces
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
104
114
|
- !ruby/object:Gem::Version
|
105
|
-
version:
|
115
|
+
version: '0.15'
|
106
116
|
type: :runtime
|
107
117
|
prerelease: false
|
108
118
|
version_requirements: !ruby/object:Gem::Requirement
|
109
119
|
requirements:
|
110
120
|
- - "~>"
|
111
121
|
- !ruby/object:Gem::Version
|
112
|
-
version: '
|
113
|
-
|
122
|
+
version: '0.15'
|
123
|
+
- !ruby/object:Gem::Dependency
|
124
|
+
name: metrics
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
126
|
+
requirements:
|
127
|
+
- - "~>"
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0.12'
|
130
|
+
type: :runtime
|
131
|
+
prerelease: false
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - "~>"
|
114
135
|
- !ruby/object:Gem::Version
|
115
|
-
version:
|
116
|
-
description:
|
117
|
-
email:
|
136
|
+
version: '0.12'
|
118
137
|
executables: []
|
119
138
|
extensions: []
|
120
139
|
extra_rdoc_files: []
|
@@ -127,6 +146,7 @@ files:
|
|
127
146
|
- lib/async/condition.rb
|
128
147
|
- lib/async/console.rb
|
129
148
|
- lib/async/idler.rb
|
149
|
+
- lib/async/limited_queue.rb
|
130
150
|
- lib/async/list.rb
|
131
151
|
- lib/async/node.rb
|
132
152
|
- lib/async/notification.rb
|
@@ -160,7 +180,6 @@ metadata:
|
|
160
180
|
documentation_uri: https://socketry.github.io/async/
|
161
181
|
funding_uri: https://github.com/sponsors/ioquatix/
|
162
182
|
source_code_uri: https://github.com/socketry/async.git
|
163
|
-
post_install_message:
|
164
183
|
rdoc_options: []
|
165
184
|
require_paths:
|
166
185
|
- lib
|
@@ -175,8 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
194
|
- !ruby/object:Gem::Version
|
176
195
|
version: '0'
|
177
196
|
requirements: []
|
178
|
-
rubygems_version: 3.
|
179
|
-
signing_key:
|
197
|
+
rubygems_version: 3.6.2
|
180
198
|
specification_version: 4
|
181
199
|
summary: A concurrency framework for Ruby.
|
182
200
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|