async 2.15.0 → 2.15.2

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: 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