async 2.5.1 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 862d2b83e50785cb72e11913b43433c48f996176da829a247f7bc777bdec3e4d
4
- data.tar.gz: 3854347d6ad621746deb3420f6fec9e81f6fac6c2f76322c0e1dfccdb6faa628
3
+ metadata.gz: 0f62c8150ba7dba6f26532b080a4c99ecde97d86cf5f31746aec706d12644550
4
+ data.tar.gz: 99bbe4f65da2c18ab75bc67377896b7b6c26f6056eb789ef22d5601f43526228
5
5
  SHA512:
6
- metadata.gz: 9e62e8e2be2aa0f79f122258049850e016190a72fbe5756520ca07250a020f7e656239beaba61cf0a5cd1d948130bab704c6f96370b7870830c737e4872059c9
7
- data.tar.gz: 49fa1924bda3bbe42cc1ab374c70f2d6916c46528343e37d5423cf12864db2a4def8316ba394804acc3197f73d9e461bbb0fc4dc75a4ccf9a5441e46850e417d
6
+ metadata.gz: 3c6a7fee5fb6d429b7533afb80dd38ad499c8dd313ee5f881cdd19479a8f1f28072f16f07f83305f6d2e6ddaa7907859938fcd69e2fc7b0e945b194588fd22e2
7
+ data.tar.gz: f10a739ba6013c928199c4abe884e9dbf9d4d48d7f8329ed3977bb9230584d100505d617a859c113bf04c25cbf9ae4353e525221563f930227467cbaebf153b1
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/async/node.rb CHANGED
@@ -5,6 +5,8 @@
5
5
  # Copyright, 2017, by Kent Gruber.
6
6
  # Copyright, 2022, by Shannon Skipper.
7
7
 
8
+ require 'fiber/annotation'
9
+
8
10
  require_relative 'list'
9
11
 
10
12
  module Async
@@ -109,20 +111,27 @@ module Async
109
111
 
110
112
  def annotate(annotation)
111
113
  if block_given?
112
- previous_annotation = @annotation
113
- @annotation = annotation
114
- yield
115
- @annotation = previous_annotation
114
+ begin
115
+ current_annotation = @annotation
116
+ @annotation = annotation
117
+ return yield
118
+ ensure
119
+ @annotation = current_annotation
120
+ end
116
121
  else
117
122
  @annotation = annotation
118
123
  end
119
124
  end
120
125
 
126
+ def annotation
127
+ @annotation
128
+ end
129
+
121
130
  def description
122
131
  @object_name ||= "#{self.class}:#{format '%#018x', object_id}#{@transient ? ' transient' : nil}"
123
132
 
124
- if @annotation
125
- "#{@object_name} #{@annotation}"
133
+ if annotation = self.annotation
134
+ "#{@object_name} #{annotation}"
126
135
  elsif line = self.backtrace(0, 1)&.first
127
136
  "#{@object_name} #{line}"
128
137
  else
@@ -17,6 +17,12 @@ require 'resolv'
17
17
  module Async
18
18
  # Handles scheduling of fibers. Implements the fiber scheduler interface.
19
19
  class Scheduler < Node
20
+ class ClosedError < RuntimeError
21
+ def initialize(message = "Scheduler is closed!")
22
+ super
23
+ end
24
+ end
25
+
20
26
  # Whether the fiber scheduler is supported.
21
27
  # @public Since `stable-v1`.
22
28
  def self.supported?
@@ -150,6 +156,9 @@ module Async
150
156
 
151
157
  # @asynchronous May be non-blocking..
152
158
  def address_resolve(hostname)
159
+ # On some platforms, hostnames may contain a device-specific suffix (e.g. %en0). We need to strip this before resolving.
160
+ # See <https://github.com/socketry/async/issues/180> for more details.
161
+ hostname = hostname.split("%", 2).first
153
162
  ::Resolv.getaddresses(hostname)
154
163
  end
155
164
 
@@ -233,7 +242,7 @@ module Async
233
242
 
234
243
  # Run the reactor until all tasks are finished. Proxies arguments to {#async} immediately before entering the loop, if a block is provided.
235
244
  def run(...)
236
- Kernel::raise RuntimeError, 'Reactor has been closed' if @selector.nil?
245
+ Kernel::raise ClosedError if @selector.nil?
237
246
 
238
247
  initial_task = self.async(...) if block_given?
239
248
 
@@ -260,6 +269,8 @@ module Async
260
269
  # @returns [Task] The task that was scheduled into the reactor.
261
270
  # @deprecated With no replacement.
262
271
  def async(*arguments, **options, &block)
272
+ Kernel::raise ClosedError if @selector.nil?
273
+
263
274
  task = Task.new(Task.current? || self, **options, &block)
264
275
 
265
276
  # I want to take a moment to explain the logic of this.
data/lib/async/task.rb CHANGED
@@ -59,6 +59,12 @@ module Async
59
59
  #
60
60
  # @public Since `stable-v1`.
61
61
  class Task < Node
62
+ class FinishedError < RuntimeError
63
+ def initialize(message = "Cannot create child task within a task that has finished execution!")
64
+ super
65
+ end
66
+ end
67
+
62
68
  # @deprecated With no replacement.
63
69
  def self.yield
64
70
  Fiber.scheduler.transfer
@@ -90,6 +96,22 @@ module Async
90
96
  @fiber&.backtrace(*arguments)
91
97
  end
92
98
 
99
+ def annotate(annotation, &block)
100
+ if @fiber
101
+ @fiber.annotate(annotation, &block)
102
+ else
103
+ super
104
+ end
105
+ end
106
+
107
+ def annotation
108
+ if @fiber
109
+ @fiber.annotation
110
+ else
111
+ super
112
+ end
113
+ end
114
+
93
115
  def to_s
94
116
  "\#<#{self.description} (#{@status})>"
95
117
  end
@@ -164,7 +186,7 @@ module Async
164
186
 
165
187
  # Run an asynchronous task as a child of the current task.
166
188
  def async(*arguments, **options, &block)
167
- raise "Cannot create child task within a task that has finished execution!" if self.finished?
189
+ raise FinishedError if self.finished?
168
190
 
169
191
  task = Task.new(self, **options, &block)
170
192
 
@@ -199,6 +221,10 @@ module Async
199
221
  attr :result
200
222
 
201
223
  # Stop the task and all of its children.
224
+ #
225
+ # If `later` is false, it means that `stop` has been invoked directly. When `later` is true, it means that `stop` is invoked by `stop_children` or some other indirect mechanism. In that case, if we encounter the "current" fiber, we can't stop it right away, as it's currently performing `#stop`. Stopping it immediately would interrupt the current stop traversal, so we need to schedule the stop to occur later.
226
+ #
227
+ # @parameter later [Boolean] Whether to stop the task later, or immediately.
202
228
  def stop(later = false)
203
229
  if self.stopped?
204
230
  # If we already stopped this task... don't try to stop it again:
@@ -208,6 +234,7 @@ module Async
208
234
  # If the fiber is alive, we need to stop it:
209
235
  if @fiber&.alive?
210
236
  if self.current?
237
+ # If the fiber is current, and later is `true`, we need to schedule the fiber to be stopped later, as it's currently invoking `stop`:
211
238
  if later
212
239
  # If the fiber is the current fiber and we want to stop it later, schedule it:
213
240
  Fiber.scheduler.push(Stop::Later.new(self))
@@ -218,6 +245,7 @@ module Async
218
245
  else
219
246
  # If the fiber is not curent, we can raise the exception directly:
220
247
  begin
248
+ # There is a chance that this will stop the fiber that originally called stop. If that happens, the exception handling in `#stopped` will rescue the exception and re-raise it later.
221
249
  Fiber.scheduler.raise(@fiber, Stop)
222
250
  rescue FiberError
223
251
  # In some cases, this can cause a FiberError (it might be resumed already), so we schedule it to be stopped later:
@@ -244,7 +272,7 @@ module Async
244
272
  end
245
273
 
246
274
  def current?
247
- self.equal?(Thread.current[:async_task])
275
+ Fiber.current.equal?(@fiber)
248
276
  end
249
277
 
250
278
  private
@@ -289,11 +317,23 @@ module Async
289
317
  end
290
318
 
291
319
  def stopped!
292
- # Console.logger.info(self, self.annotation) {"Task was stopped with #{@children&.size.inspect} children!"}
320
+ # Console.logger.info(self, status:) {"Task #{self} was stopped with #{@children&.size.inspect} children!"}
293
321
  @status = :stopped
294
322
 
295
- # We are not running, but children might be so we should stop them:
296
- stop_children(true)
323
+ stopped = false
324
+
325
+ begin
326
+ # We are bnot running, but children might be so we should stop them:
327
+ stop_children(true)
328
+ rescue Stop
329
+ stopped = true
330
+ # If we are stopping children, and one of them tries to stop the current task, we should ignore it. We will be stopped later.
331
+ retry
332
+ end
333
+
334
+ if stopped
335
+ raise Stop, "Stopping current task!"
336
+ end
297
337
  end
298
338
 
299
339
  def stop!
@@ -303,7 +343,7 @@ module Async
303
343
  end
304
344
 
305
345
  def schedule(&block)
306
- @fiber = Fiber.new do
346
+ @fiber = Fiber.new(annotation: self.annotation) do
307
347
  set!
308
348
 
309
349
  begin
data/lib/async/version.rb CHANGED
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2017-2022, by Samuel Williams.
5
5
 
6
6
  module Async
7
- VERSION = "2.5.1"
7
+ VERSION = "2.6.0"
8
8
  end
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.5.1
4
+ version: 2.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -56,7 +56,7 @@ cert_chain:
56
56
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
57
57
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
58
58
  -----END CERTIFICATE-----
59
- date: 2023-05-15 00:00:00.000000000 Z
59
+ date: 2023-06-07 00:00:00.000000000 Z
60
60
  dependencies:
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: console
@@ -100,6 +100,20 @@ dependencies:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
102
  version: '4.1'
103
+ - !ruby/object:Gem::Dependency
104
+ name: fiber-annotation
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
103
117
  - !ruby/object:Gem::Dependency
104
118
  name: bake-test
105
119
  requirement: !ruby/object:Gem::Requirement
@@ -247,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
247
261
  - !ruby/object:Gem::Version
248
262
  version: '0'
249
263
  requirements: []
250
- rubygems_version: 3.5.0.dev
264
+ rubygems_version: 3.4.7
251
265
  signing_key:
252
266
  specification_version: 4
253
267
  summary: A concurrency framework for Ruby.
metadata.gz.sig CHANGED
Binary file