async 2.19.0 → 2.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/async/barrier.rb +2 -2
- data/lib/async/clock.rb +1 -1
- data/lib/async/condition.rb +1 -1
- data/lib/async/console.rb +5 -0
- data/lib/async/idler.rb +2 -1
- data/lib/async/notification.rb +1 -1
- data/lib/async/queue.rb +2 -2
- data/lib/async/scheduler.rb +112 -23
- data/lib/async/semaphore.rb +1 -1
- data/lib/async/task.rb +19 -4
- data/lib/async/version.rb +1 -1
- data/lib/async/wrapper.rb +2 -0
- data/lib/kernel/async.rb +1 -1
- data/lib/kernel/sync.rb +1 -1
- data/lib/metrics/provider/async/task.rb +17 -0
- data/lib/metrics/provider/async.rb +6 -0
- data/lib/traces/provider/async/barrier.rb +17 -0
- data/lib/traces/provider/async/task.rb +29 -0
- data/lib/traces/provider/async.rb +7 -0
- data/readme.md +4 -0
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +8 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a5bc2e233137c26a571123a0edc0cd22fe32278f780fe801c166a2c7968dc83
|
4
|
+
data.tar.gz: 2c2aab55da8cfe72e2806a6bb1e4a18e3b20d7162ff929bcb91de019eb9c6da3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9f3490b820e58aa4e6a3a95051bc36b699903f617918832f557b4a3369c7101a9c36cf878b34ab274caa1379591c30fef0f56cac79dbf1da482b16472c9cde1d
|
7
|
+
data.tar.gz: 8c911bdb5a50dadc299ee4d7ed0cd6d16371b84fe622fb1542190fc5a586f914dca4a5396de481f826c8606d547df831357f783f3ee67da3de18e49dd5ad7dd6
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/barrier.rb
CHANGED
@@ -9,11 +9,11 @@ require_relative "task"
|
|
9
9
|
module Async
|
10
10
|
# A general purpose synchronisation primitive, which allows one task to wait for a number of other tasks to complete. It can be used in conjunction with {Semaphore}.
|
11
11
|
#
|
12
|
-
# @public Since
|
12
|
+
# @public Since *Async v1*.
|
13
13
|
class Barrier
|
14
14
|
# Initialize the barrier.
|
15
15
|
# @parameter parent [Task | Semaphore | Nil] The parent for holding any children tasks.
|
16
|
-
# @public Since
|
16
|
+
# @public Since *Async v1*.
|
17
17
|
def initialize(parent: nil)
|
18
18
|
@tasks = List.new
|
19
19
|
|
data/lib/async/clock.rb
CHANGED
data/lib/async/condition.rb
CHANGED
@@ -9,7 +9,7 @@ require_relative "list"
|
|
9
9
|
|
10
10
|
module Async
|
11
11
|
# A synchronization primitive, which allows fibers to wait until a particular condition is (edge) triggered.
|
12
|
-
# @public Since
|
12
|
+
# @public Since *Async v1*.
|
13
13
|
class Condition
|
14
14
|
# Create a new condition.
|
15
15
|
def initialize
|
data/lib/async/console.rb
CHANGED
@@ -12,12 +12,15 @@ module Async
|
|
12
12
|
#
|
13
13
|
# This is an experimental feature.
|
14
14
|
module Console
|
15
|
+
# Log a message at the debug level. The shim is silent.
|
15
16
|
def self.debug(...)
|
16
17
|
end
|
17
18
|
|
19
|
+
# Log a message at the info level. The shim is silent.
|
18
20
|
def self.info(...)
|
19
21
|
end
|
20
22
|
|
23
|
+
# Log a message at the warn level. The shim redirects to `Kernel#warn`.
|
21
24
|
def self.warn(*arguments, exception: nil, **options)
|
22
25
|
if exception
|
23
26
|
super(*arguments, exception.full_message, **options)
|
@@ -26,10 +29,12 @@ module Async
|
|
26
29
|
end
|
27
30
|
end
|
28
31
|
|
32
|
+
# Log a message at the error level. The shim redirects to `Kernel#warn`.
|
29
33
|
def self.error(...)
|
30
34
|
self.warn(...)
|
31
35
|
end
|
32
36
|
|
37
|
+
# Log a message at the fatal level. The shim redirects to `Kernel#warn`.
|
33
38
|
def self.fatal(...)
|
34
39
|
self.warn(...)
|
35
40
|
end
|
data/lib/async/idler.rb
CHANGED
@@ -7,7 +7,8 @@ module Async
|
|
7
7
|
# A load balancing mechanism that can be used process work when the system is idle.
|
8
8
|
class Idler
|
9
9
|
# Create a new idler.
|
10
|
-
#
|
10
|
+
#
|
11
|
+
# @public Since *Async v2*.
|
11
12
|
#
|
12
13
|
# @parameter maximum_load [Numeric] The maximum load before we start shedding work.
|
13
14
|
# @parameter backoff [Numeric] The initial backoff time, used for delaying work.
|
data/lib/async/notification.rb
CHANGED
@@ -7,7 +7,7 @@ require_relative "condition"
|
|
7
7
|
|
8
8
|
module Async
|
9
9
|
# A synchronization primitive, which allows fibers to wait until a notification is received. Does not block the task which signals the notification. Waiting tasks are resumed on next iteration of the reactor.
|
10
|
-
# @public Since
|
10
|
+
# @public Since *Async v1*.
|
11
11
|
class Notification < Condition
|
12
12
|
# Signal to a given task that it should resume operations.
|
13
13
|
def signal(value = nil, task: Task.current)
|
data/lib/async/queue.rb
CHANGED
@@ -12,7 +12,7 @@ module Async
|
|
12
12
|
#
|
13
13
|
# It has a compatible interface with {Notification} and {Condition}, except that it's multi-value.
|
14
14
|
#
|
15
|
-
# @public Since
|
15
|
+
# @public Since *Async v1*.
|
16
16
|
class Queue
|
17
17
|
# Create a new queue.
|
18
18
|
#
|
@@ -99,7 +99,7 @@ module Async
|
|
99
99
|
end
|
100
100
|
|
101
101
|
# A queue which limits the number of items that can be enqueued.
|
102
|
-
# @public Since
|
102
|
+
# @public Since *Async v1*.
|
103
103
|
class LimitedQueue < Queue
|
104
104
|
# Create a new limited queue.
|
105
105
|
#
|
data/lib/async/scheduler.rb
CHANGED
@@ -27,14 +27,14 @@ module Async
|
|
27
27
|
end
|
28
28
|
|
29
29
|
# Whether the fiber scheduler is supported.
|
30
|
-
# @public Since
|
30
|
+
# @public Since *Async v1*.
|
31
31
|
def self.supported?
|
32
32
|
true
|
33
33
|
end
|
34
34
|
|
35
35
|
# Create a new scheduler.
|
36
36
|
#
|
37
|
-
# @public Since
|
37
|
+
# @public Since *Async v1*.
|
38
38
|
# @parameter parent [Node | Nil] The parent node to use for task hierarchy.
|
39
39
|
# @parameter selector [IO::Event::Selector] The selector to use for event handling.
|
40
40
|
def initialize(parent = nil, selector: nil)
|
@@ -52,6 +52,7 @@ module Async
|
|
52
52
|
end
|
53
53
|
|
54
54
|
# Compute the scheduler load according to the busy and idle times that are updated by the run loop.
|
55
|
+
#
|
55
56
|
# @returns [Float] The load of the scheduler. 0.0 means no load, 1.0 means fully loaded or over-loaded.
|
56
57
|
def load
|
57
58
|
total_time = @busy_time + @idle_time
|
@@ -95,7 +96,7 @@ module Async
|
|
95
96
|
end
|
96
97
|
|
97
98
|
# Terminate all child tasks and close the scheduler.
|
98
|
-
# @public Since
|
99
|
+
# @public Since *Async v1*.
|
99
100
|
def close
|
100
101
|
self.run_loop do
|
101
102
|
until self.terminate
|
@@ -115,7 +116,7 @@ module Async
|
|
115
116
|
end
|
116
117
|
|
117
118
|
# @returns [Boolean] Whether the scheduler has been closed.
|
118
|
-
# @public Since
|
119
|
+
# @public Since *Async v1*.
|
119
120
|
def closed?
|
120
121
|
@selector.nil?
|
121
122
|
end
|
@@ -167,6 +168,8 @@ module Async
|
|
167
168
|
end
|
168
169
|
|
169
170
|
# Invoked when a fiber tries to perform a blocking operation which cannot continue. A corresponding call {unblock} must be performed to allow this fiber to continue.
|
171
|
+
#
|
172
|
+
|
170
173
|
# @asynchronous May only be called on same thread as fiber scheduler.
|
171
174
|
def block(blocker, timeout)
|
172
175
|
# $stderr.puts "block(#{blocker}, #{Fiber.current}, #{timeout})"
|
@@ -190,7 +193,13 @@ module Async
|
|
190
193
|
timer&.cancel!
|
191
194
|
end
|
192
195
|
|
196
|
+
# Unblock a fiber that was previously blocked.
|
197
|
+
#
|
198
|
+
# @public Since *Async v2* and *Ruby v3.1*.
|
193
199
|
# @asynchronous May be called from any thread.
|
200
|
+
#
|
201
|
+
# @parameter blocker [Object] The object that was blocking the fiber.
|
202
|
+
# @parameter fiber [Fiber] The fiber to unblock.
|
194
203
|
def unblock(blocker, fiber)
|
195
204
|
# $stderr.puts "unblock(#{blocker}, #{fiber})"
|
196
205
|
|
@@ -201,7 +210,12 @@ module Async
|
|
201
210
|
end
|
202
211
|
end
|
203
212
|
|
204
|
-
#
|
213
|
+
# Sleep for the specified duration.
|
214
|
+
#
|
215
|
+
# @public Since *Async v2* and *Ruby v3.1*.
|
216
|
+
# @asynchronous May be non-blocking.
|
217
|
+
#
|
218
|
+
# @parameter duration [Numeric | Nil] The time in seconds to sleep, or if nil, indefinitely.
|
205
219
|
def kernel_sleep(duration = nil)
|
206
220
|
if duration
|
207
221
|
self.block(nil, duration)
|
@@ -210,7 +224,12 @@ module Async
|
|
210
224
|
end
|
211
225
|
end
|
212
226
|
|
213
|
-
#
|
227
|
+
# Resolve the address of the given hostname.
|
228
|
+
#
|
229
|
+
# @public Since *Async v2*.
|
230
|
+
# @asynchronous May be non-blocking.
|
231
|
+
#
|
232
|
+
# @parameter hostname [String] The hostname to resolve.
|
214
233
|
def address_resolve(hostname)
|
215
234
|
# On some platforms, hostnames may contain a device-specific suffix (e.g. %en0). We need to strip this before resolving.
|
216
235
|
# See <https://github.com/socketry/async/issues/180> for more details.
|
@@ -218,7 +237,6 @@ module Async
|
|
218
237
|
::Resolv.getaddresses(hostname)
|
219
238
|
end
|
220
239
|
|
221
|
-
|
222
240
|
if IO.method_defined?(:timeout)
|
223
241
|
private def get_timeout(io)
|
224
242
|
io.timeout
|
@@ -229,7 +247,14 @@ module Async
|
|
229
247
|
end
|
230
248
|
end
|
231
249
|
|
232
|
-
#
|
250
|
+
# Wait for the specified IO to become ready for the specified events.
|
251
|
+
#
|
252
|
+
# @public Since *Async v2*.
|
253
|
+
# @asynchronous May be non-blocking.
|
254
|
+
#
|
255
|
+
# @parameter io [IO] The IO object to wait on.
|
256
|
+
# @parameter events [Integer] The events to wait for, e.g. `IO::READABLE`, `IO::WRITABLE`, etc.
|
257
|
+
# @parameter timeout [Float | Nil] The maximum time to wait, or if nil, indefinitely.
|
233
258
|
def io_wait(io, events, timeout = nil)
|
234
259
|
fiber = Fiber.current
|
235
260
|
|
@@ -251,6 +276,15 @@ module Async
|
|
251
276
|
end
|
252
277
|
|
253
278
|
if ::IO::Event::Support.buffer?
|
279
|
+
# Read from the specified IO into the buffer.
|
280
|
+
#
|
281
|
+
# @public Since *Async v2* and Ruby with `IO::Buffer` support.
|
282
|
+
# @asynchronous May be non-blocking.
|
283
|
+
#
|
284
|
+
# @parameter io [IO] The IO object to read from.
|
285
|
+
# @parameter buffer [IO::Buffer] The buffer to read into.
|
286
|
+
# @parameter length [Integer] The minimum number of bytes to read.
|
287
|
+
# @parameter offset [Integer] The offset within the buffer to read into.
|
254
288
|
def io_read(io, buffer, length, offset = 0)
|
255
289
|
fiber = Fiber.current
|
256
290
|
|
@@ -266,6 +300,15 @@ module Async
|
|
266
300
|
end
|
267
301
|
|
268
302
|
if RUBY_ENGINE != "ruby" || RUBY_VERSION >= "3.3.1"
|
303
|
+
# Write the specified buffer to the IO.
|
304
|
+
#
|
305
|
+
# @public Since *Async v2* and *Ruby v3.3.1* with `IO::Buffer` support.
|
306
|
+
# @asynchronous May be non-blocking.
|
307
|
+
#
|
308
|
+
# @parameter io [IO] The IO object to write to.
|
309
|
+
# @parameter buffer [IO::Buffer] The buffer to write from.
|
310
|
+
# @parameter length [Integer] The minimum number of bytes to write.
|
311
|
+
# @parameter offset [Integer] The offset within the buffer to write from.
|
269
312
|
def io_write(io, buffer, length, offset = 0)
|
270
313
|
fiber = Fiber.current
|
271
314
|
|
@@ -283,6 +326,10 @@ module Async
|
|
283
326
|
end
|
284
327
|
|
285
328
|
# Wait for the specified process ID to exit.
|
329
|
+
#
|
330
|
+
# @public Since *Async v2*.
|
331
|
+
# @asynchronous May be non-blocking.
|
332
|
+
#
|
286
333
|
# @parameter pid [Integer] The process ID to wait for.
|
287
334
|
# @parameter flags [Integer] A bit-mask of flags suitable for `Process::Status.wait`.
|
288
335
|
# @returns [Process::Status] A process status instance.
|
@@ -291,6 +338,25 @@ module Async
|
|
291
338
|
return @selector.process_wait(Fiber.current, pid, flags)
|
292
339
|
end
|
293
340
|
|
341
|
+
# Wait for the given work to be executed.
|
342
|
+
#
|
343
|
+
# @public Since *Async v2.19* and *Ruby v3.4*.
|
344
|
+
# @asynchronous May be non-blocking.
|
345
|
+
#
|
346
|
+
# @parameter work [Proc] The work to execute on a background thread.
|
347
|
+
# @returns [Object] The result of the work.
|
348
|
+
def blocking_operation_wait(work)
|
349
|
+
thread = Thread.new(&work)
|
350
|
+
|
351
|
+
result = thread.join
|
352
|
+
|
353
|
+
thread = nil
|
354
|
+
|
355
|
+
return result
|
356
|
+
ensure
|
357
|
+
thread&.kill
|
358
|
+
end
|
359
|
+
|
294
360
|
# Run one iteration of the event loop.
|
295
361
|
#
|
296
362
|
# 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.
|
@@ -335,7 +401,10 @@ module Async
|
|
335
401
|
end
|
336
402
|
|
337
403
|
# Run one iteration of the event loop.
|
338
|
-
#
|
404
|
+
#
|
405
|
+
# @public Since *Async v1*.
|
406
|
+
# @asynchronous Must be invoked from blocking (root) fiber.
|
407
|
+
#
|
339
408
|
# @parameter timeout [Float | Nil] The maximum timeout, or if nil, indefinite.
|
340
409
|
# @returns [Boolean] Whether there is more work to do.
|
341
410
|
def run_once(timeout = nil)
|
@@ -354,6 +423,7 @@ module Async
|
|
354
423
|
end
|
355
424
|
|
356
425
|
# Checks and clears the interrupted state of the scheduler.
|
426
|
+
#
|
357
427
|
# @returns [Boolean] Whether the reactor has been interrupted.
|
358
428
|
private def interrupted?
|
359
429
|
if @interrupted
|
@@ -368,7 +438,9 @@ module Async
|
|
368
438
|
return false
|
369
439
|
end
|
370
440
|
|
371
|
-
# Stop all children, including transient children
|
441
|
+
# Stop all children, including transient children.
|
442
|
+
#
|
443
|
+
# @public Since *Async v1*.
|
372
444
|
def stop
|
373
445
|
@children&.each do |child|
|
374
446
|
child.stop
|
@@ -387,6 +459,7 @@ module Async
|
|
387
459
|
end
|
388
460
|
end
|
389
461
|
rescue Interrupt => interrupt
|
462
|
+
# If an interrupt did occur during an iteration of the event loop, we need to handle it. More specifically, `self.stop` is not safe to interrupt without potentially corrupting the task tree.
|
390
463
|
Thread.handle_interrupt(::SignalException => :never) do
|
391
464
|
Console.debug(self) do |buffer|
|
392
465
|
buffer.puts "Scheduler interrupted: #{interrupt.inspect}"
|
@@ -406,6 +479,13 @@ module Async
|
|
406
479
|
end
|
407
480
|
|
408
481
|
# Run the reactor until all tasks are finished. Proxies arguments to {#async} immediately before entering the loop, if a block is provided.
|
482
|
+
#
|
483
|
+
# Forwards all parameters to {#async} if a block is given.
|
484
|
+
#
|
485
|
+
# @public Since *Async v1*.
|
486
|
+
#
|
487
|
+
# @yields {|task| ...} The top level task, if a block is given.
|
488
|
+
# @returns [Task] The initial task that was scheduled into the reactor.
|
409
489
|
def run(...)
|
410
490
|
Kernel.raise ClosedError if @selector.nil?
|
411
491
|
|
@@ -418,30 +498,23 @@ module Async
|
|
418
498
|
return initial_task
|
419
499
|
end
|
420
500
|
|
421
|
-
# Start an asynchronous task within the specified reactor. The task will be
|
422
|
-
# executed until the first blocking call, at which point it will yield and
|
423
|
-
# and this method will return.
|
501
|
+
# Start an asynchronous task within the specified reactor. The task will be executed until the first blocking call, at which point it will yield and and this method will return.
|
424
502
|
#
|
425
|
-
#
|
503
|
+
# @public Since *Async v1*.
|
504
|
+
# @asynchronous May context switch immediately to new task.
|
505
|
+
# @deprecated Use {#run} or {Task#async} instead.
|
426
506
|
#
|
427
507
|
# @yields {|task| ...} Executed within the task.
|
428
508
|
# @returns [Task] The task that was scheduled into the reactor.
|
429
|
-
# @deprecated With no replacement.
|
430
509
|
def async(*arguments, **options, &block)
|
510
|
+
# warn "Async::Scheduler#async is deprecated. Use `run` or `Task#async` instead.", uplevel: 1, category: :deprecated
|
511
|
+
|
431
512
|
Kernel.raise ClosedError if @selector.nil?
|
432
513
|
|
433
514
|
task = Task.new(Task.current? || self, **options, &block)
|
434
515
|
|
435
|
-
# I want to take a moment to explain the logic of this.
|
436
|
-
# When calling an async block, we deterministically execute it until the
|
437
|
-
# first blocking operation. We don't *have* to do this - we could schedule
|
438
|
-
# it for later execution, but it's useful to:
|
439
|
-
# - Fail at the point of the method call where possible.
|
440
|
-
# - Execute determinstically where possible.
|
441
|
-
# - Avoid scheduler overhead if no blocking operation is performed.
|
442
516
|
task.run(*arguments)
|
443
517
|
|
444
|
-
# Console.debug "Initial execution of task #{fiber} complete (#{result} -> #{fiber.alive?})..."
|
445
518
|
return task
|
446
519
|
end
|
447
520
|
|
@@ -450,7 +523,14 @@ module Async
|
|
450
523
|
end
|
451
524
|
|
452
525
|
# Invoke the block, but after the specified timeout, raise {TimeoutError} in any currenly blocking operation. If the block runs to completion before the timeout occurs or there are no non-blocking operations after the timeout expires, the code will complete without any exception.
|
526
|
+
#
|
527
|
+
# @public Since *Async v1*.
|
528
|
+
# @asynchronous May raise an exception at any interruption point (e.g. blocking operations).
|
529
|
+
#
|
453
530
|
# @parameter duration [Numeric] The time in seconds, in which the task should complete.
|
531
|
+
# @parameter exception [Class] The exception class to raise.
|
532
|
+
# @parameter message [String] The message to pass to the exception.
|
533
|
+
# @yields {|duration| ...} The block to execute with a timeout.
|
454
534
|
def with_timeout(duration, exception = TimeoutError, message = "execution expired", &block)
|
455
535
|
fiber = Fiber.current
|
456
536
|
|
@@ -465,6 +545,15 @@ module Async
|
|
465
545
|
timer&.cancel!
|
466
546
|
end
|
467
547
|
|
548
|
+
# Invoke the block, but after the specified timeout, raise the specified exception with the given message. If the block runs to completion before the timeout occurs or there are no non-blocking operations after the timeout expires, the code will complete without any exception.
|
549
|
+
#
|
550
|
+
# @public Since *Async v1* and *Ruby v3.1*. May be invoked from `Timeout.timeout`.
|
551
|
+
# @asynchronous May raise an exception at any interruption point (e.g. blocking operations).
|
552
|
+
#
|
553
|
+
# @parameter duration [Numeric] The time in seconds, in which the task should complete.
|
554
|
+
# @parameter exception [Class] The exception class to raise.
|
555
|
+
# @parameter message [String] The message to pass to the exception.
|
556
|
+
# @yields {|duration| ...} The block to execute with a timeout.
|
468
557
|
def timeout_after(duration, exception, message, &block)
|
469
558
|
with_timeout(duration, exception, message) do |timer|
|
470
559
|
yield duration
|
data/lib/async/semaphore.rb
CHANGED
@@ -7,7 +7,7 @@ require_relative "list"
|
|
7
7
|
|
8
8
|
module Async
|
9
9
|
# A synchronization primitive, which limits access to a given resource.
|
10
|
-
# @public Since
|
10
|
+
# @public Since *Async v1*.
|
11
11
|
class Semaphore
|
12
12
|
# @parameter limit [Integer] The maximum number of times the semaphore can be acquired before it blocks.
|
13
13
|
# @parameter parent [Task | Semaphore | Nil] The parent for holding any children tasks.
|
data/lib/async/task.rb
CHANGED
@@ -40,7 +40,7 @@ module Async
|
|
40
40
|
end
|
41
41
|
|
42
42
|
# Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`.
|
43
|
-
# @public Since
|
43
|
+
# @public Since *Async v1*.
|
44
44
|
class TimeoutError < StandardError
|
45
45
|
# Create a new timeout error.
|
46
46
|
#
|
@@ -50,7 +50,7 @@ module Async
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
# @public Since
|
53
|
+
# @public Since *Async v1*.
|
54
54
|
class Task < Node
|
55
55
|
# Raised when a child task is created within a task that has finished execution.
|
56
56
|
class FinishedError < RuntimeError
|
@@ -198,7 +198,7 @@ module Async
|
|
198
198
|
rescue => error
|
199
199
|
# 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.
|
200
200
|
if @finished.nil?
|
201
|
-
|
201
|
+
warn(self, "Task may have ended with unhandled exception.", exception: error)
|
202
202
|
end
|
203
203
|
|
204
204
|
raise
|
@@ -210,6 +210,10 @@ module Async
|
|
210
210
|
|
211
211
|
# Run an asynchronous task as a child of the current task.
|
212
212
|
#
|
213
|
+
# @public Since *Async v1*.
|
214
|
+
# @asynchronous May context switch immediately to the new task.
|
215
|
+
#
|
216
|
+
# @yields {|task| ...} in the context of the new task.
|
213
217
|
# @raises [FinishedError] If the task has already finished.
|
214
218
|
# @returns [Task] The child task.
|
215
219
|
def async(*arguments, **options, &block)
|
@@ -217,6 +221,13 @@ module Async
|
|
217
221
|
|
218
222
|
task = Task.new(self, **options, &block)
|
219
223
|
|
224
|
+
# When calling an async block, we deterministically execute it until the first blocking operation. We don't *have* to do this - we could schedule it for later execution, but it's useful to:
|
225
|
+
#
|
226
|
+
# - Fail at the point of the method call where possible.
|
227
|
+
# - Execute determinstically where possible.
|
228
|
+
# - Avoid scheduler overhead if no blocking operation is performed.
|
229
|
+
#
|
230
|
+
# There are different strategies (greedy vs non-greedy). We are currently using a greedy strategy.
|
220
231
|
task.run(*arguments)
|
221
232
|
|
222
233
|
return task
|
@@ -302,7 +313,7 @@ module Async
|
|
302
313
|
# If stop is invoked a second time, it will be immediately executed.
|
303
314
|
#
|
304
315
|
# @yields {} The block of code to execute.
|
305
|
-
# @public Since
|
316
|
+
# @public Since *Async v1*.
|
306
317
|
def defer_stop
|
307
318
|
# Tri-state variable for controlling stop:
|
308
319
|
# - nil: defer_stop has not been called.
|
@@ -360,6 +371,10 @@ module Async
|
|
360
371
|
|
361
372
|
private
|
362
373
|
|
374
|
+
def warn(...)
|
375
|
+
Console.warn(...)
|
376
|
+
end
|
377
|
+
|
363
378
|
# Finish the current task, moving any children to the parent.
|
364
379
|
def finish!
|
365
380
|
# Don't hold references to the fiber or block after the task has finished:
|
data/lib/async/version.rb
CHANGED
data/lib/async/wrapper.rb
CHANGED
@@ -4,6 +4,8 @@
|
|
4
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/kernel/async.rb
CHANGED
@@ -19,7 +19,7 @@ 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
|
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?
|
data/lib/kernel/sync.rb
CHANGED
@@ -14,7 +14,7 @@ module Kernel
|
|
14
14
|
# @yields {|task| ...} The block that will execute asynchronously.
|
15
15
|
# @parameter task [Async::Task] The task that is executing the given block.
|
16
16
|
#
|
17
|
-
# @public Since
|
17
|
+
# @public Since *Async v1*.
|
18
18
|
# @asynchronous Will block until given block completes executing.
|
19
19
|
def Sync(annotation: nil, &block)
|
20
20
|
if task = ::Async::Task.current?
|
@@ -0,0 +1,17 @@
|
|
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
|
+
|
12
|
+
def schedule(&block)
|
13
|
+
ASYNC_TASK_SCHEDULED.emit(1)
|
14
|
+
|
15
|
+
super(&block)
|
16
|
+
end
|
17
|
+
end
|
@@ -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,29 @@
|
|
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
|
+
unless self.transient?
|
12
|
+
trace_context = Traces.trace_context
|
13
|
+
end
|
14
|
+
|
15
|
+
super do
|
16
|
+
Traces.trace_context = trace_context
|
17
|
+
|
18
|
+
if annotation = self.annotation
|
19
|
+
attributes = {
|
20
|
+
"annotation" => annotation
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
Traces.trace("async.task", attributes: attributes) do
|
25
|
+
yield
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/readme.md
CHANGED
@@ -35,6 +35,10 @@ Please see the [project documentation](https://socketry.github.io/async/) for mo
|
|
35
35
|
|
36
36
|
Please see the [project releases](https://socketry.github.io/async/releases/index) for all releases.
|
37
37
|
|
38
|
+
### v2.20.0
|
39
|
+
|
40
|
+
- [Traces and Metrics Providers](https://socketry.github.io/async/releases/index#traces-and-metrics-providers)
|
41
|
+
|
38
42
|
### v2.19.0
|
39
43
|
|
40
44
|
- [Async::Scheduler Debugging](https://socketry.github.io/async/releases/index#async::scheduler-debugging)
|
data/releases.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Releases
|
2
2
|
|
3
|
+
## v2.20.0
|
4
|
+
|
5
|
+
### Traces and Metrics Providers
|
6
|
+
|
7
|
+
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.
|
8
|
+
|
9
|
+
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.
|
10
|
+
|
3
11
|
## v2.19.0
|
4
12
|
|
5
13
|
### Async::Scheduler Debugging
|
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.
|
4
|
+
version: 2.21.0
|
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-11-
|
66
|
+
date: 2024-11-21 00:00:00.000000000 Z
|
67
67
|
dependencies:
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
69
|
name: console
|
@@ -144,6 +144,11 @@ files:
|
|
144
144
|
- lib/async/wrapper.rb
|
145
145
|
- lib/kernel/async.rb
|
146
146
|
- lib/kernel/sync.rb
|
147
|
+
- lib/metrics/provider/async.rb
|
148
|
+
- lib/metrics/provider/async/task.rb
|
149
|
+
- lib/traces/provider/async.rb
|
150
|
+
- lib/traces/provider/async/barrier.rb
|
151
|
+
- lib/traces/provider/async/task.rb
|
147
152
|
- license.md
|
148
153
|
- readme.md
|
149
154
|
- releases.md
|
@@ -169,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
174
|
- !ruby/object:Gem::Version
|
170
175
|
version: '0'
|
171
176
|
requirements: []
|
172
|
-
rubygems_version: 3.5.
|
177
|
+
rubygems_version: 3.5.22
|
173
178
|
signing_key:
|
174
179
|
specification_version: 4
|
175
180
|
summary: A concurrency framework for Ruby.
|
metadata.gz.sig
CHANGED
Binary file
|