async 2.15.0 → 2.15.2

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: a4792a06109491eb42dd7eeedc5da5cd0226912efb83883bb6027a2bc7343776
4
- data.tar.gz: 9fa9867100f4cbbe1a09bd07e0b9cd2db199bd2df35dd422ecc6684bb989260a
3
+ metadata.gz: be0c94dc887562681bc88ba244ed1bdd85ff41cd5707b22208c44fde725459d0
4
+ data.tar.gz: d91b6f27e96fb9c892fda055cdfb7bcc6564d89e9a02da3f9cf95c248d989360
5
5
  SHA512:
6
- metadata.gz: 9668ffea1217001a5288ada7f6283622e9280773738d96bbe89195cec6ca8f3887aa14de12d8154474d880604648e2757b5d32d0b1af95f29ad10170d636b53b
7
- data.tar.gz: 54a021b844db4e50827812a77b365624919dd7ee34bf9e49416a0283d0da7e39d5b2dbcb6879158b45e1bf6032fd871406c412d07b02f14c0046caee98c4a30f
6
+ metadata.gz: 1ec8529ee1df127e0527eb7346b9b606a3767c9ca51b812616d758e0ef2f83687829f9b70d517e4e0f18dd56b169e07fa0df9339fd96393e51388c2fb837eaaf
7
+ data.tar.gz: 777d6eb933f5907504a129f3320f36bc3f9c199b5054d24c2bbdcdebb3d3f598bb064282cd7e07b8e75760cd7872119a4aa6c2d73df43b33dc42f60f1358cfd2
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/async/node.rb CHANGED
@@ -34,6 +34,19 @@ module Async
34
34
  empty?
35
35
  end
36
36
 
37
+ # Adjust the number of transient children, assuming it has changed.
38
+ #
39
+ # Despite being public, this is not intended to be called directly. It is used internally by {Node#transient=}.
40
+ #
41
+ # @parameter transient [Boolean] Whether to increment or decrement the transient count.
42
+ def adjust_transient_count(transient)
43
+ if transient
44
+ @transient_count += 1
45
+ else
46
+ @transient_count -= 1
47
+ end
48
+ end
49
+
37
50
  private
38
51
 
39
52
  def added(node)
@@ -110,6 +123,19 @@ module Async
110
123
  @transient
111
124
  end
112
125
 
126
+ # Change the transient state of the node.
127
+ #
128
+ # A transient node is not considered when determining if a node is finished, and propagates up if the parent is consumed.
129
+ #
130
+ # @parameter value [Boolean] Whether the node is transient.
131
+ def transient=(value)
132
+ if @transient != value
133
+ @transient = value
134
+
135
+ @parent&.children&.adjust_transient_count(value)
136
+ end
137
+ end
138
+
113
139
  # Annotate the node with a description.
114
140
  #
115
141
  # @parameter annotation [String] The description to annotate the node with.
@@ -75,30 +75,33 @@ module Async
75
75
  # Invoked when the fiber scheduler is being closed.
76
76
  #
77
77
  # Executes the run loop until all tasks are finished, then closes the scheduler.
78
- def scheduler_close
78
+ def scheduler_close(error = $!)
79
79
  # If the execution context (thread) was handling an exception, we want to exit as quickly as possible:
80
- unless $!
80
+ unless error
81
81
  self.run
82
82
  end
83
83
  ensure
84
84
  self.close
85
85
  end
86
86
 
87
- private def shutdown!
88
- # It's critical to stop all tasks. Otherwise they might be holding on to resources which are never closed/released correctly.
89
- self.stop
90
-
91
- self.run_loop do
92
- unless @children.nil?
93
- run_once!
94
- end
87
+ # Terminate all child tasks.
88
+ def terminate
89
+ # If that doesn't work, take more serious action:
90
+ @children&.each do |child|
91
+ child.terminate
95
92
  end
93
+
94
+ return @children.nil?
96
95
  end
97
96
 
98
97
  # Terminate all child tasks and close the scheduler.
99
98
  # @public Since `stable-v1`.
100
99
  def close
101
- self.shutdown!
100
+ self.run_loop do
101
+ until self.terminate
102
+ self.run_once!
103
+ end
104
+ end
102
105
 
103
106
  Kernel.raise "Closing scheduler with blocked operations!" if @blocked > 0
104
107
  ensure
@@ -288,21 +291,6 @@ module Async
288
291
  return @selector.process_wait(Fiber.current, pid, flags)
289
292
  end
290
293
 
291
- # Run one iteration of the event loop.
292
- # Does not handle interrupts.
293
- # @parameter timeout [Float | Nil] The maximum timeout, or if nil, indefinite.
294
- # @returns [Boolean] Whether there is more work to do.
295
- def run_once(timeout = nil)
296
- Kernel.raise "Running scheduler on non-blocking fiber!" unless Fiber.blocking?
297
-
298
- # If we are finished, we stop the task tree and exit:
299
- if self.finished?
300
- return false
301
- end
302
-
303
- return run_once!(timeout)
304
- end
305
-
306
294
  # Run one iteration of the event loop.
307
295
  #
308
296
  # When terminating the event loop, we already know we are finished. So we don't need to check the task tree. This is a logical requirement because `run_once` ignores transient tasks. For example, a single top level transient task is not enough to keep the reactor running, but during termination we must still process it in order to terminate child tasks.
@@ -346,6 +334,25 @@ module Async
346
334
  return true
347
335
  end
348
336
 
337
+ # Run one iteration of the event loop.
338
+ # Does not handle interrupts.
339
+ # @parameter timeout [Float | Nil] The maximum timeout, or if nil, indefinite.
340
+ # @returns [Boolean] Whether there is more work to do.
341
+ def run_once(timeout = nil)
342
+ Kernel.raise "Running scheduler on non-blocking fiber!" unless Fiber.blocking?
343
+
344
+ if self.finished?
345
+ self.stop
346
+ end
347
+
348
+ # If we are finished, we stop the task tree and exit:
349
+ if @children.nil?
350
+ return false
351
+ end
352
+
353
+ return run_once!(timeout)
354
+ end
355
+
349
356
  # Checks and clears the interrupted state of the scheduler.
350
357
  # @returns [Boolean] Whether the reactor has been interrupted.
351
358
  private def interrupted?
@@ -363,10 +370,8 @@ module Async
363
370
 
364
371
  # Stop all children, including transient children, ignoring any signals.
365
372
  def stop
366
- Thread.handle_interrupt(::SignalException => :never) do
367
- @children&.each do |child|
368
- child.stop
369
- end
373
+ @children&.each do |child|
374
+ child.stop
370
375
  end
371
376
  end
372
377
 
@@ -382,7 +387,9 @@ module Async
382
387
  end
383
388
  end
384
389
  rescue Interrupt => interrupt
385
- self.stop
390
+ Thread.handle_interrupt(::SignalException => :never) do
391
+ self.stop
392
+ end
386
393
 
387
394
  retry
388
395
  end
@@ -398,9 +405,7 @@ module Async
398
405
  initial_task = self.async(...) if block_given?
399
406
 
400
407
  self.run_loop do
401
- unless self.finished?
402
- run_once!
403
- end
408
+ run_once
404
409
  end
405
410
 
406
411
  return initial_task
data/lib/async/task.rb CHANGED
@@ -176,7 +176,7 @@ module Async
176
176
 
177
177
  alias complete? completed?
178
178
 
179
- # @attribute [Symbol] The status of the execution of the fiber, one of `:initialized`, `:running`, `:complete`, `:stopped` or `:failed`.
179
+ # @attribute [Symbol] The status of the execution of the task, one of `:initialized`, `:running`, `:complete`, `:stopped` or `:failed`.
180
180
  attr :status
181
181
 
182
182
  # Begin the execution of the task.
@@ -253,15 +253,18 @@ module Async
253
253
  return stopped!
254
254
  end
255
255
 
256
- # If we are deferring stop...
257
- if @defer_stop == false
258
- # Don't stop now... but update the state so we know we need to stop later.
259
- @defer_stop = true
260
- return false
261
- end
262
-
263
256
  # If the fiber is alive, we need to stop it:
264
257
  if @fiber&.alive?
258
+ # As the task is now exiting, we want to ensure the event loop continues to execute until the task finishes.
259
+ self.transient = false
260
+
261
+ # If we are deferring stop...
262
+ if @defer_stop == false
263
+ # Don't stop now... but update the state so we know we need to stop later.
264
+ @defer_stop = true
265
+ return false
266
+ end
267
+
265
268
  if self.current?
266
269
  # 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`:
267
270
  if later
@@ -276,7 +279,7 @@ module Async
276
279
  begin
277
280
  # 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.
278
281
  Fiber.scheduler.raise(@fiber, Stop)
279
- rescue FiberError
282
+ rescue FiberError => error
280
283
  # In some cases, this can cause a FiberError (it might be resumed already), so we schedule it to be stopped later:
281
284
  Fiber.scheduler.push(Stop::Later.new(self))
282
285
  end
@@ -323,6 +326,11 @@ module Async
323
326
  end
324
327
  end
325
328
 
329
+ # @returns [Boolean] Whether stop has been deferred.
330
+ def stop_deferred?
331
+ @defer_stop
332
+ end
333
+
326
334
  # Lookup the {Task} for the current fiber. Raise `RuntimeError` if none is available.
327
335
  # @returns [Task]
328
336
  # @raises[RuntimeError] If task was not {set!} for the current fiber.
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.15.0"
7
+ VERSION = "2.15.2"
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.15.0
4
+ version: 2.15.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -63,7 +63,7 @@ cert_chain:
63
63
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
64
64
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
65
65
  -----END CERTIFICATE-----
66
- date: 2024-08-04 00:00:00.000000000 Z
66
+ date: 2024-08-07 00:00:00.000000000 Z
67
67
  dependencies:
68
68
  - !ruby/object:Gem::Dependency
69
69
  name: console
@@ -167,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
167
167
  - !ruby/object:Gem::Version
168
168
  version: '0'
169
169
  requirements: []
170
- rubygems_version: 3.5.13
170
+ rubygems_version: 3.5.11
171
171
  signing_key:
172
172
  specification_version: 4
173
173
  summary: A concurrency framework for Ruby.
metadata.gz.sig CHANGED
Binary file