async 2.14.2 → 2.15.1

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: 12cf95e2d70376d5634d24377bb7d3d3974f14f20227fc33450fb103b17f5783
4
- data.tar.gz: e0c89541d8d039ec1672b68ef1ae90a86a00c065f6143769734bcbf7cc01d7de
3
+ metadata.gz: d69239cbc022fd8b50e28bac6356f037ae0ff221605f9309312b1b8566b61ac1
4
+ data.tar.gz: ba47d3b3f2d50e4999eb219338ab9e3d619882c9b3de3b000c07a6f2dca89d21
5
5
  SHA512:
6
- metadata.gz: cec85cadb861dd14ed374930963e94785e066b8584537e2ef0cc28d8a804372bcf7895f9836d436bd72e20d3f4e0027820ea005dff60120f3d0b66192571e82a
7
- data.tar.gz: f11f62065e7fb9072f06b26514e98c33b6aac2988a485ebaf814792a68abe18711f9842187fcfa66282c8ce0290a1064c575a6d1f8cc5a18269f85f82886fc04
6
+ metadata.gz: 6ddd53973722c47e13f92c93149cdc5a111af55af22a9d810aaddbe29fe6b996937737b929556dec48c22deab204d8113574892bb6f2095e991aea62a9188529
7
+ data.tar.gz: 2c2c81505e97ebe5fe28fde6abe3eeed7641f5f6e4ead801432c31b60e85d378a6b823bcd97903d11cbfdd3f937f40e80ee288f0e6fb2c961884358c0d30c905
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2022, by Samuel Williams.
4
+ # Copyright, 2017-2024, by Samuel Williams.
5
5
  # Copyright, 2017, by Kent Gruber.
6
6
 
7
7
  require 'fiber'
data/lib/async/idler.rb CHANGED
@@ -41,7 +41,8 @@ module Async
41
41
  backoff = nil
42
42
 
43
43
  while true
44
- load = scheduler.load
44
+ load = scheduler.load
45
+
45
46
  break if load < @maximum_load
46
47
 
47
48
  if backoff
data/lib/async/list.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
5
 
6
6
  module Async
7
7
  # A general doublely linked list. This is used internally by {Async::Barrier} and {Async::Condition} to manage child tasks.
data/lib/async/node.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2023, by Samuel Williams.
4
+ # Copyright, 2017-2024, by Samuel Williams.
5
5
  # Copyright, 2017, by Kent Gruber.
6
6
  # Copyright, 2022, by Shannon Skipper.
7
7
 
@@ -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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'condition'
7
7
 
data/lib/async/queue.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2018-2022, by Samuel Williams.
4
+ # Copyright, 2018-2024, by Samuel Williams.
5
5
  # Copyright, 2019, by Ryan Musgrave.
6
6
  # Copyright, 2020-2022, by Bruno Sutic.
7
7
 
data/lib/async/reactor.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2022, by Samuel Williams.
4
+ # Copyright, 2017-2024, by Samuel Williams.
5
5
  # Copyright, 2017, by Kent Gruber.
6
6
  # Copyright, 2018, by Sokolov Yura.
7
7
 
@@ -71,47 +71,46 @@ module Async
71
71
  return @busy_time / total_time
72
72
  end
73
73
  end
74
-
74
+
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
- # Terminate the scheduler. We deliberately ignore interrupts here, as this code can be called from an interrupt, and we don't want to be interrupted while cleaning up.
87
+ # Terminate all child tasks.
88
88
  def terminate
89
- Thread.handle_interrupt(::Interrupt => :never) do
90
- super
89
+ # If that doesn't work, take more serious action:
90
+ @children&.each do |child|
91
+ child.terminate
91
92
  end
93
+
94
+ return @children.nil?
92
95
  end
93
96
 
94
97
  # Terminate all child tasks and close the scheduler.
95
98
  # @public Since `stable-v1`.
96
99
  def close
97
- # It's critical to stop all tasks. Otherwise they might be holding on to resources which are never closed/released correctly.
98
- until self.terminate
99
- self.run_once!
100
+ self.run_loop do
101
+ until self.terminate
102
+ self.run_once!
103
+ end
100
104
  end
101
105
 
102
106
  Kernel.raise "Closing scheduler with blocked operations!" if @blocked > 0
103
-
104
- # We depend on GVL for consistency:
105
- # @guard.synchronize do
106
-
107
+ ensure
107
108
  # We want `@selector = nil` to be a visible side effect from this point forward, specifically in `#interrupt` and `#unblock`. If the selector is closed, then we don't want to push any fibers to it.
108
109
  selector = @selector
109
110
  @selector = nil
110
111
 
111
112
  selector&.close
112
113
 
113
- # end
114
-
115
114
  consume
116
115
  end
117
116
 
@@ -292,28 +291,13 @@ module Async
292
291
  return @selector.process_wait(Fiber.current, pid, flags)
293
292
  end
294
293
 
295
- # Run one iteration of the event loop.
296
- # Does not handle interrupts.
297
- # @parameter timeout [Float | Nil] The maximum timeout, or if nil, indefinite.
298
- # @returns [Boolean] Whether there is more work to do.
299
- def run_once(timeout = nil)
300
- Kernel::raise "Running scheduler on non-blocking fiber!" unless Fiber.blocking?
301
-
302
- # If we are finished, we stop the task tree and exit:
303
- if self.finished?
304
- return false
305
- end
306
-
307
- return run_once!(timeout)
308
- end
309
-
310
294
  # Run one iteration of the event loop.
311
295
  #
312
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.
313
297
  #
314
298
  # @parameter timeout [Float | Nil] The maximum timeout, or if nil, indefinite.
315
299
  # @returns [Boolean] Whether there is more work to do.
316
- private def run_once!(timeout = 0)
300
+ private def run_once!(timeout = nil)
317
301
  start_time = Async::Clock.now
318
302
 
319
303
  interval = @timers.wait_interval
@@ -350,6 +334,25 @@ module Async
350
334
  return true
351
335
  end
352
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
+
353
356
  # Checks and clears the interrupted state of the scheduler.
354
357
  # @returns [Boolean] Whether the reactor has been interrupted.
355
358
  private def interrupted?
@@ -365,22 +368,22 @@ module Async
365
368
  return false
366
369
  end
367
370
 
368
- # Run the reactor until all tasks are finished. Proxies arguments to {#async} immediately before entering the loop, if a block is provided.
369
- def run(...)
370
- Kernel::raise ClosedError if @selector.nil?
371
-
372
- initial_task = self.async(...) if block_given?
371
+ # Stop all children, including transient children, ignoring any signals.
372
+ def stop
373
+ @children&.each do |child|
374
+ child.stop
375
+ end
376
+ end
377
+
378
+ private def run_loop(&block)
373
379
  interrupt = nil
374
380
 
375
381
  begin
376
382
  # In theory, we could use Exception here to be a little bit safer, but we've only shown the case for SignalException to be a problem, so let's not over-engineer this.
377
383
  Thread.handle_interrupt(::SignalException => :never) do
378
- while true
379
- # If we are interrupted, we need to exit:
380
- break if self.interrupted?
381
-
384
+ until self.interrupted?
382
385
  # If we are finished, we need to exit:
383
- break unless self.run_once
386
+ break unless yield
384
387
  end
385
388
  end
386
389
  rescue Interrupt => interrupt
@@ -392,11 +395,20 @@ module Async
392
395
  end
393
396
 
394
397
  # If the event loop was interrupted, and we finished exiting normally (due to the interrupt), we need to re-raise the interrupt so that the caller can handle it too.
395
- Kernel.raise interrupt if interrupt
398
+ Kernel.raise(interrupt) if interrupt
399
+ end
400
+
401
+ # Run the reactor until all tasks are finished. Proxies arguments to {#async} immediately before entering the loop, if a block is provided.
402
+ def run(...)
403
+ Kernel.raise ClosedError if @selector.nil?
404
+
405
+ initial_task = self.async(...) if block_given?
406
+
407
+ self.run_loop do
408
+ run_once
409
+ end
396
410
 
397
411
  return initial_task
398
- ensure
399
- Console.debug(self) {"Exiting run-loop because #{$! ? $! : 'finished'}."}
400
412
  end
401
413
 
402
414
  # Start an asynchronous task within the specified reactor. The task will be
@@ -409,7 +421,7 @@ module Async
409
421
  # @returns [Task] The task that was scheduled into the reactor.
410
422
  # @deprecated With no replacement.
411
423
  def async(*arguments, **options, &block)
412
- Kernel::raise ClosedError if @selector.nil?
424
+ Kernel.raise ClosedError if @selector.nil?
413
425
 
414
426
  task = Task.new(Task.current? || self, **options, &block)
415
427
 
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.
@@ -192,8 +192,8 @@ module Async
192
192
  # I'm not completely happy with this overhead, but the alternative is to not log anything which makes debugging extremely difficult. Maybe we can introduce a debug wrapper which adds extra logging.
193
193
  if @finished.nil?
194
194
  Console::Event::Failure.for(error).emit(self, "Task may have ended with unhandled exception.", severity: :warn)
195
- # else
196
- # Console::Event::Failure.for(error).emit(self, severity: :debug)
195
+ else
196
+ # Console::Event::Failure.for(error).emit(self, severity: :debug)
197
197
  end
198
198
 
199
199
  raise
@@ -262,6 +262,9 @@ module Async
262
262
 
263
263
  # If the fiber is alive, we need to stop it:
264
264
  if @fiber&.alive?
265
+ # As the task is now exiting, we want to ensure the event loop continues to execute until the task finishes.
266
+ self.transient = false
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
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2021-2022, by Samuel Williams.
4
+ # Copyright, 2021-2024, by Samuel Williams.
5
5
 
6
6
  require_relative 'condition'
7
7
 
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.14.2"
7
+ VERSION = "2.15.1"
8
8
  end
data/lib/async/waiter.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022, by Samuel Williams.
4
+ # Copyright, 2022-2024, by Samuel Williams.
5
+ # Copyright, 2024, by Patrik Wenger.
5
6
 
6
7
  module Async
7
8
  # A composable synchronization primitive, which allows one task to wait for a number of other tasks to complete. It can be used in conjunction with {Semaphore} and/or {Barrier}.
data/lib/async/wrapper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2022, by Samuel Williams.
4
+ # Copyright, 2017-2024, by Samuel Williams.
5
5
  # Copyright, 2017, by Kent Gruber.
6
6
 
7
7
  module Async
data/lib/async.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2017-2022, by Samuel Williams.
4
+ # Copyright, 2017-2024, by Samuel Williams.
5
5
  # Copyright, 2020, by Salim Semaoune.
6
6
 
7
7
  require_relative "async/version"
data/license.md CHANGED
@@ -11,7 +11,7 @@ Copyright, 2020-2023, by Olle Jonsson.
11
11
  Copyright, 2020, by Salim Semaoune.
12
12
  Copyright, 2020, by Brian Morearty.
13
13
  Copyright, 2020, by Stefan Wrobel.
14
- Copyright, 2020, by Patrik Wenger.
14
+ Copyright, 2020-2024, by Patrik Wenger.
15
15
  Copyright, 2020, by Ken Muryoi.
16
16
  Copyright, 2020, by Jun Jiang.
17
17
  Copyright, 2020-2022, by Bruno Sutic.
@@ -26,6 +26,7 @@ Copyright, 2023, by Math Ieu.
26
26
  Copyright, 2023, by Emil Tin.
27
27
  Copyright, 2023, by Gert Goet.
28
28
  Copyright, 2024, by Dimitar Peychinov.
29
+ Copyright, 2024, by Jamie McCarthy.
29
30
 
30
31
  Permission is hereby granted, free of charge, to any person obtaining a copy
31
32
  of this software and associated documentation files (the "Software"), to deal
data/readme.md CHANGED
@@ -43,11 +43,11 @@ We welcome contributions to this project.
43
43
 
44
44
  ### Developer Certificate of Origin
45
45
 
46
- This project uses the [Developer Certificate of Origin](https://developercertificate.org/). All contributors to this project must agree to this document to have their contributions accepted.
46
+ In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
47
47
 
48
- ### Contributor Covenant
48
+ ### Community Guidelines
49
49
 
50
- This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
50
+ This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
51
51
 
52
52
  ## See Also
53
53
 
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.14.2
4
+ version: 2.15.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -9,7 +9,9 @@ authors:
9
9
  - Jeremy Jung
10
10
  - Olle Jonsson
11
11
  - Devin Christensen
12
+ - Patrik Wenger
12
13
  - Emil Tin
14
+ - Jamie McCarthy
13
15
  - Kent Gruber
14
16
  - Brian Morearty
15
17
  - Colin Kelley
@@ -23,7 +25,6 @@ authors:
23
25
  - Masafumi Okura
24
26
  - Masayuki Yamamoto
25
27
  - Math Ieu
26
- - Patrik Wenger
27
28
  - Ryan Musgrave
28
29
  - Salim Semaoune
29
30
  - Shannon Skipper
@@ -62,7 +63,7 @@ cert_chain:
62
63
  Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
63
64
  voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
64
65
  -----END CERTIFICATE-----
65
- date: 2024-07-17 00:00:00.000000000 Z
66
+ date: 2024-08-07 00:00:00.000000000 Z
66
67
  dependencies:
67
68
  - !ruby/object:Gem::Dependency
68
69
  name: console
@@ -70,20 +71,14 @@ dependencies:
70
71
  requirements:
71
72
  - - "~>"
72
73
  - !ruby/object:Gem::Version
73
- version: '1.25'
74
- - - ">="
75
- - !ruby/object:Gem::Version
76
- version: 1.25.2
74
+ version: '1.26'
77
75
  type: :runtime
78
76
  prerelease: false
79
77
  version_requirements: !ruby/object:Gem::Requirement
80
78
  requirements:
81
79
  - - "~>"
82
80
  - !ruby/object:Gem::Version
83
- version: '1.25'
84
- - - ">="
85
- - !ruby/object:Gem::Version
86
- version: 1.25.2
81
+ version: '1.26'
87
82
  - !ruby/object:Gem::Dependency
88
83
  name: fiber-annotation
89
84
  requirement: !ruby/object:Gem::Requirement
@@ -172,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
167
  - !ruby/object:Gem::Version
173
168
  version: '0'
174
169
  requirements: []
175
- rubygems_version: 3.5.11
170
+ rubygems_version: 3.5.13
176
171
  signing_key:
177
172
  specification_version: 4
178
173
  summary: A concurrency framework for Ruby.
metadata.gz.sig CHANGED
Binary file