async 2.14.2 → 2.23.1

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.
@@ -1,9 +1,9 @@
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
- require_relative 'condition'
6
+ require_relative "condition"
7
7
 
8
8
  module Async
9
9
  # A synchronization primitive that allows one task to wait for another task to resolve a value.
@@ -31,6 +31,11 @@ module Async
31
31
  condition.signal(value)
32
32
  end
33
33
 
34
+ # Alias for {#resolve}.
35
+ def value=(value)
36
+ self.resolve(value)
37
+ end
38
+
34
39
  # Whether the value has been resolved.
35
40
  #
36
41
  # @returns [Boolean] Whether the value has been resolved.
@@ -41,14 +46,14 @@ module Async
41
46
  # Wait for the value to be resolved.
42
47
  #
43
48
  # @returns [Object] The resolved value.
44
- def value
49
+ def wait
45
50
  @condition&.wait
46
51
  return @value
47
52
  end
48
53
 
49
- # Alias for {#value}.
50
- def wait
51
- self.value
54
+ # Alias for {#wait}.
55
+ def value
56
+ self.wait
52
57
  end
53
58
  end
54
59
  end
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.23.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}.
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require "etc"
7
+
8
+ module Async
9
+ # A simple work pool that offloads work to a background thread.
10
+ #
11
+ # @private
12
+ class WorkerPool
13
+ # Used to augment the scheduler to add support for blocking operations.
14
+ module BlockingOperationWait
15
+ # Wait for the given work to be executed.
16
+ #
17
+ # @public Since *Async v2.19* and *Ruby v3.4*.
18
+ # @asynchronous May be non-blocking.
19
+ #
20
+ # @parameter work [Proc] The work to execute on a background thread.
21
+ # @returns [Object] The result of the work.
22
+ def blocking_operation_wait(work)
23
+ @worker_pool.call(work)
24
+ end
25
+ end
26
+
27
+ # Execute the given work in a background thread.
28
+ class Promise
29
+ # Create a new promise.
30
+ #
31
+ # @parameter work [Proc] The work to be done.
32
+ def initialize(work)
33
+ @work = work
34
+ @state = :pending
35
+ @value = nil
36
+ @guard = ::Mutex.new
37
+ @condition = ::ConditionVariable.new
38
+ @thread = nil
39
+ end
40
+
41
+ # Execute the work and resolve the promise.
42
+ def call
43
+ work = nil
44
+
45
+ @guard.synchronize do
46
+ @thread = ::Thread.current
47
+
48
+ return unless work = @work
49
+ end
50
+
51
+ resolve(work.call)
52
+ rescue Exception => error
53
+ reject(error)
54
+ end
55
+
56
+ private def resolve(value)
57
+ @guard.synchronize do
58
+ @work = nil
59
+ @thread = nil
60
+ @value = value
61
+ @state = :resolved
62
+ @condition.broadcast
63
+ end
64
+ end
65
+
66
+ private def reject(error)
67
+ @guard.synchronize do
68
+ @work = nil
69
+ @thread = nil
70
+ @value = error
71
+ @state = :failed
72
+ @condition.broadcast
73
+ end
74
+ end
75
+
76
+ # Cancel the work and raise an exception in the background thread.
77
+ def cancel
78
+ return unless @work
79
+
80
+ @guard.synchronize do
81
+ @work = nil
82
+ @state = :cancelled
83
+ @thread&.raise(Interrupt)
84
+ end
85
+ end
86
+
87
+ # Wait for the work to be done.
88
+ #
89
+ # @returns [Object] The result of the work.
90
+ def wait
91
+ @guard.synchronize do
92
+ while @state == :pending
93
+ @condition.wait(@guard)
94
+ end
95
+
96
+ if @state == :failed
97
+ raise @value
98
+ else
99
+ return @value
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # A background worker thread.
106
+ class Worker
107
+ # Create a new worker.
108
+ def initialize
109
+ @work = ::Thread::Queue.new
110
+ @thread = ::Thread.new(&method(:run))
111
+ end
112
+
113
+ # Execute work until the queue is closed.
114
+ def run
115
+ while work = @work.pop
116
+ work.call
117
+ end
118
+ end
119
+
120
+ # Close the worker thread.
121
+ def close
122
+ if thread = @thread
123
+ @thread = nil
124
+ thread.kill
125
+ end
126
+ end
127
+
128
+ # Call the work and notify the scheduler when it is done.
129
+ def call(work)
130
+ promise = Promise.new(work)
131
+
132
+ @work.push(promise)
133
+
134
+ begin
135
+ return promise.wait
136
+ ensure
137
+ promise.cancel
138
+ end
139
+ end
140
+ end
141
+
142
+ # Create a new work pool.
143
+ #
144
+ # @parameter size [Integer] The number of threads to use.
145
+ def initialize(size: Etc.nprocessors)
146
+ @ready = ::Thread::Queue.new
147
+
148
+ size.times do
149
+ @ready.push(Worker.new)
150
+ end
151
+ end
152
+
153
+ # Close the work pool. Kills all outstanding work.
154
+ def close
155
+ if ready = @ready
156
+ @ready = nil
157
+ ready.close
158
+
159
+ while worker = ready.pop
160
+ worker.close
161
+ end
162
+ end
163
+ end
164
+
165
+ # Offload work to a thread.
166
+ #
167
+ # @parameter work [Proc] The work to be done.
168
+ def call(work)
169
+ if ready = @ready
170
+ worker = ready.pop
171
+
172
+ begin
173
+ worker.call(work)
174
+ ensure
175
+ ready.push(worker)
176
+ end
177
+ else
178
+ raise RuntimeError, "No worker available!"
179
+ end
180
+ end
181
+ end
182
+ end
data/lib/async/wrapper.rb CHANGED
@@ -1,9 +1,11 @@
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
+ warn "Async::Wrapper is deprecated and will be removed on 2025-03-31. Please use native interfaces instead.", uplevel: 1, category: :deprecated
8
+
7
9
  module Async
8
10
  # Represents an asynchronous IO within a reactor.
9
11
  # @deprecated With no replacement. Prefer native interfaces.
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/lib/kernel/async.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
 
6
6
  require_relative "../async/reactor"
7
7
 
@@ -19,11 +19,13 @@ module Kernel
19
19
  # @yields {|task| ...} The block that will execute asynchronously.
20
20
  # @parameter task [Async::Task] The task that is executing the given block.
21
21
  #
22
- # @public Since `stable-v1`.
22
+ # @public Since *Async v1*.
23
23
  # @asynchronous May block until given block completes executing.
24
24
  def Async(...)
25
25
  if current = ::Async::Task.current?
26
26
  return current.async(...)
27
+ elsif scheduler = Fiber.scheduler
28
+ ::Async::Task.run(scheduler, ...)
27
29
  else
28
30
  # This calls Fiber.set_scheduler(self):
29
31
  reactor = ::Async::Reactor.new
data/lib/kernel/sync.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2022, by Samuel Williams.
4
+ # Copyright, 2019-2024, by Samuel Williams.
5
5
  # Copyright, 2020, by Brian Morearty.
6
+ # Copyright, 2024, by Patrik Wenger.
6
7
 
7
8
  require_relative "../async/reactor"
8
9
 
@@ -13,17 +14,23 @@ module Kernel
13
14
  # @yields {|task| ...} The block that will execute asynchronously.
14
15
  # @parameter task [Async::Task] The task that is executing the given block.
15
16
  #
16
- # @public Since `stable-v1`.
17
+ # @public Since *Async v1*.
17
18
  # @asynchronous Will block until given block completes executing.
18
- def Sync(&block)
19
+ def Sync(annotation: nil, &block)
19
20
  if task = ::Async::Task.current?
20
- yield task
21
+ if annotation
22
+ task.annotate(annotation) {yield task}
23
+ else
24
+ yield task
25
+ end
26
+ elsif scheduler = Fiber.scheduler
27
+ ::Async::Task.run(scheduler, &block).wait
21
28
  else
22
29
  # This calls Fiber.set_scheduler(self):
23
30
  reactor = Async::Reactor.new
24
31
 
25
32
  begin
26
- return reactor.run(finished: ::Async::Condition.new, &block).wait
33
+ return reactor.run(annotation: annotation, finished: ::Async::Condition.new, &block).wait
27
34
  ensure
28
35
  Fiber.set_scheduler(nil)
29
36
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative "../../../async/task"
7
+ require "metrics/provider"
8
+
9
+ Metrics::Provider(Async::Task) do
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.")
12
+
13
+ def schedule(&block)
14
+ ASYNC_TASK_SCHEDULED.emit(1)
15
+
16
+ super(&block)
17
+ ensure
18
+ ASYNC_TASK_FINISHED.emit(1)
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative "async/task"
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022, by Samuel Williams.
5
+
6
+ require_relative "../../../async/barrier"
7
+ require "traces/provider"
8
+
9
+ Traces::Provider(Async::Barrier) do
10
+ def wait
11
+ attributes = {
12
+ "size" => self.size
13
+ }
14
+
15
+ Traces.trace("async.barrier.wait", attributes: attributes) {super}
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2022, by Samuel Williams.
5
+
6
+ require_relative "../../../async/task"
7
+ require "traces/provider"
8
+
9
+ Traces::Provider(Async::Task) do
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
+
16
+ unless self.transient?
17
+ trace_context = Traces.trace_context
18
+ end
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:
27
+ super do
28
+ Traces.trace_context = trace_context
29
+
30
+ if annotation = self.annotation
31
+ attributes["annotation"] = annotation
32
+ end
33
+
34
+ Traces.trace("async.task", attributes: attributes) do
35
+ # Yes, this is correct, we already called super above:
36
+ yield
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2024, by Samuel Williams.
5
+
6
+ require_relative "async/task"
7
+ require_relative "async/barrier"
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
@@ -23,7 +23,7 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
23
23
 
24
24
  - [Asynchronous Tasks](https://socketry.github.io/async/guides/asynchronous-tasks/index) - This guide explains how asynchronous tasks work and how to use them.
25
25
 
26
- - [Event Loop](https://socketry.github.io/async/guides/event-loop/index) - This guide gives an overview of how the event loop is implemented.
26
+ - [Scheduler](https://socketry.github.io/async/guides/scheduler/index) - This guide gives an overview of how the scheduler is implemented.
27
27
 
28
28
  - [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
29
 
@@ -31,23 +31,39 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
31
31
 
32
32
  - [Debugging](https://socketry.github.io/async/guides/debugging/index) - This guide explains how to debug issues with programs that use Async.
33
33
 
34
- ## Contributing
34
+ ## Releases
35
35
 
36
- We welcome contributions to this project.
36
+ Please see the [project releases](https://socketry.github.io/async/releases/index) for all releases.
37
37
 
38
- 1. Fork it.
39
- 2. Create your feature branch (`git checkout -b my-new-feature`).
40
- 3. Commit your changes (`git commit -am 'Add some feature'`).
41
- 4. Push to the branch (`git push origin my-new-feature`).
42
- 5. Create new Pull Request.
38
+ ### v2.23.0
43
39
 
44
- ### Developer Certificate of Origin
40
+ - Rename `ASYNC_SCHEDULER_DEFAULT_WORKER_POOL` to `ASYNC_SCHEDULER_WORKER_POOL`.
41
+ - [Fiber Stall Profiler](https://socketry.github.io/async/releases/index#fiber-stall-profiler)
42
+
43
+ ### v2.21.1
44
+
45
+ - [Worker Pool](https://socketry.github.io/async/releases/index#worker-pool)
46
+
47
+ ### v2.20.0
48
+
49
+ - [Traces and Metrics Providers](https://socketry.github.io/async/releases/index#traces-and-metrics-providers)
50
+
51
+ ### v2.19.0
52
+
53
+ - [Async::Scheduler Debugging](https://socketry.github.io/async/releases/index#async::scheduler-debugging)
54
+ - [Console Shims](https://socketry.github.io/async/releases/index#console-shims)
55
+
56
+ ### v2.18.0
57
+
58
+ - Add support for `Sync(annotation:)`, so that you can annotate the block with a description of what it does, even if it doesn't create a new task.
45
59
 
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.
60
+ ### v2.17.0
47
61
 
48
- ### Contributor Covenant
62
+ - Introduce `Async::Queue#push` and `Async::Queue#pop` for compatibility with `::Queue`.
49
63
 
50
- This project is governed by the [Contributor Covenant](https://www.contributor-covenant.org/). All contributors and participants agree to abide by its terms.
64
+ ### v2.16.0
65
+
66
+ - [Better Handling of Async and Sync in Nested Fibers](https://socketry.github.io/async/releases/index#better-handling-of-async-and-sync-in-nested-fibers)
51
67
 
52
68
  ## See Also
53
69
 
@@ -57,3 +73,21 @@ This project is governed by the [Contributor Covenant](https://www.contributor-c
57
73
  - [falcon](https://github.com/socketry/falcon) — A rack compatible server built on top of `async-http`.
58
74
  - [rubydns](https://github.com/ioquatix/rubydns) — An easy to use Ruby DNS server.
59
75
  - [slack-ruby-bot](https://github.com/slack-ruby/slack-ruby-bot) — A client for making slack bots.
76
+
77
+ ## Contributing
78
+
79
+ We welcome contributions to this project.
80
+
81
+ 1. Fork it.
82
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
83
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
84
+ 4. Push to the branch (`git push origin my-new-feature`).
85
+ 5. Create new Pull Request.
86
+
87
+ ### Developer Certificate of Origin
88
+
89
+ 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.
90
+
91
+ ### Community Guidelines
92
+
93
+ 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.
data/releases.md ADDED
@@ -0,0 +1,121 @@
1
+ # Releases
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
+
27
+ ## v2.21.1
28
+
29
+ ### Worker Pool
30
+
31
+ Ruby 3.4 will feature a new fiber scheduler hook, `blocking_operation_wait` which allows the scheduler to redirect the work given to `rb_nogvl` to a worker pool.
32
+
33
+ The Async scheduler optionally supports this feature using a worker pool, by using the following environment variable:
34
+
35
+ ASYNC_SCHEDULER_WORKER_POOL=true
36
+
37
+ This will cause the scheduler to use a worker pool for general blocking operations, rather than blocking the event loop.
38
+
39
+ It should be noted that this isn't a net win, as the overhead of using a worker pool can be significant compared to the `rb_nogvl` work. As such, it is recommended to benchmark your application with and without the worker pool to determine if it is beneficial.
40
+
41
+ ## v2.20.0
42
+
43
+ ### Traces and Metrics Providers
44
+
45
+ Async now has [traces](https://github.com/socketry/traces) and [metrics](https://github.com/socketry/metrics) providers for various core classes. This allows you to emit traces and metrics to a suitable backend (including DataDog, New Relic, OpenTelemetry, etc.) for monitoring and debugging purposes.
46
+
47
+ To take advantage of this feature, you will need to introduce your own `config/traces.rb` and `config/metrics.rb`. Async's own repository includes these files for testing purposes, you could copy them into your own project and modify them as needed.
48
+
49
+ ## v2.19.0
50
+
51
+ ### Async::Scheduler Debugging
52
+
53
+ Occasionally on issues, I encounter people asking for help and I need more information. Pressing Ctrl-C to exit a hung program is common, but it usually doesn't provide enough information to diagnose the problem. Setting the `CONSOLE_LEVEL=debug` environment variable will now print additional information about the scheduler when you interrupt it, including a backtrace of the current tasks.
54
+
55
+ > CONSOLE_LEVEL=debug bundle exec ruby ./test.rb
56
+ ^C 0.0s debug: Async::Reactor [oid=0x974] [ec=0x988] [pid=9116] [2024-11-08 14:12:03 +1300]
57
+ | Scheduler interrupted: Interrupt
58
+ | #<Async::Reactor:0x0000000000000974 1 children (running)>
59
+ | #<Async::Task:0x000000000000099c /Users/samuel/Developer/socketry/async/lib/async/scheduler.rb:185:in `transfer' (running)>
60
+ | → /Users/samuel/Developer/socketry/async/lib/async/scheduler.rb:185:in `transfer'
61
+ | /Users/samuel/Developer/socketry/async/lib/async/scheduler.rb:185:in `block'
62
+ | /Users/samuel/Developer/socketry/async/lib/async/scheduler.rb:207:in `kernel_sleep'
63
+ | /Users/samuel/Developer/socketry/async/test.rb:7:in `sleep'
64
+ | /Users/samuel/Developer/socketry/async/test.rb:7:in `sleepy'
65
+ | /Users/samuel/Developer/socketry/async/test.rb:12:in `block in <top (required)>'
66
+ | /Users/samuel/Developer/socketry/async/lib/async/task.rb:197:in `block in run'
67
+ | /Users/samuel/Developer/socketry/async/lib/async/task.rb:420:in `block in schedule'
68
+ /Users/samuel/Developer/socketry/async/lib/async/scheduler.rb:317:in `select': Interrupt
69
+ ... (backtrace continues) ...
70
+
71
+ This gives better visibility into what the scheduler is doing, and should help diagnose issues.
72
+
73
+ ### Console Shims
74
+
75
+ The `async` gem depends on `console` gem, because my goal was to have good logging by default without thinking about it too much. However, some users prefer to avoid using the `console` gem for logging, so I've added an experimental set of shims which should allow you to bypass the `console` gem entirely.
76
+
77
+ ``` ruby
78
+ require 'async/console'
79
+ require 'async'
80
+
81
+ Async{raise "Boom"}
82
+ ```
83
+
84
+ Will now use `Kernel#warn` to print the task failure warning:
85
+
86
+ #<Async::Task:0x00000000000012d4 /home/samuel/Developer/socketry/async/lib/async/task.rb:104:in `backtrace' (running)>
87
+ Task may have ended with unhandled exception.
88
+ (irb):4:in `block in <top (required)>': Boom (RuntimeError)
89
+ from /home/samuel/Developer/socketry/async/lib/async/task.rb:197:in `block in run'
90
+ from /home/samuel/Developer/socketry/async/lib/async/task.rb:420:in `block in schedule'
91
+
92
+ ## v2.18.0
93
+
94
+ - Add support for `Sync(annotation:)`, so that you can annotate the block with a description of what it does, even if it doesn't create a new task.
95
+
96
+ ## v2.17.0
97
+
98
+ - Introduce `Async::Queue#push` and `Async::Queue#pop` for compatibility with `::Queue`.
99
+
100
+ ## v2.16.0
101
+
102
+ ### Better Handling of Async and Sync in Nested Fibers
103
+
104
+ Interleaving bare fibers within `Async` and `Sync` blocks should not cause problems, but it presents a number of issues in the current implementation. Tracking the parent-child relationship between tasks, when they are interleaved with bare fibers, is difficult. The current implementation assumes that if there is no parent task, then it should create a new reactor. This is not always the case, as the parent task might not be visible due to nested Fibers. As a result, `Async` will create a new reactor, trying to stop the existing one, causing major internal consistency issues.
105
+
106
+ I encountered this issue when trying to use `Async` within a streaming response in Rails. The `protocol-rack` [uses a normal fiber to wrap streaming responses](https://github.com/socketry/protocol-rack/blob/cb1ca44e9deadb9369bdb2ea03416556aa927c5c/lib/protocol/rack/body/streaming.rb#L24-L28), and if you try to use `Async` within it, it will create a new reactor, causing the server to lock up.
107
+
108
+ Ideally, `Async` and `Sync` helpers should work when any `Fiber.scheduler` is defined. Right now, it's unrealistic to expect `Async::Task` to work in any scheduler, but at the very least, the following should work:
109
+
110
+ ``` ruby
111
+ reactor = Async::Reactor.new # internally calls Fiber.set_scheduler
112
+
113
+ # This should run in the above reactor, rather than creating a new one.
114
+ Async do
115
+ puts "Hello World"
116
+ end
117
+ ```
118
+
119
+ In order to do this, bare `Async` and `Sync` blocks should use `Fiber.scheduler` as a parent if possible.
120
+
121
+ See <https://github.com/socketry/async/pull/340> for more details.
data.tar.gz.sig CHANGED
Binary file