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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f095de1e8131ef68c86aeb0176f159d529614e0cc4550e09942c740602e5684c
4
- data.tar.gz: 110037688f349d660617df61c86bd637c367a7f0bde68a1920aaac6c508b9de4
3
+ metadata.gz: 4a5bc2e233137c26a571123a0edc0cd22fe32278f780fe801c166a2c7968dc83
4
+ data.tar.gz: 2c2aab55da8cfe72e2806a6bb1e4a18e3b20d7162ff929bcb91de019eb9c6da3
5
5
  SHA512:
6
- metadata.gz: d287fb690ce18f7b75df003cc7f8a93752c347dc7c0dbcdfcd23fa42827368f228ffcb867482e69f94df87fb140b7bcf0da4ca666692504b1221052dd8c6f6ec
7
- data.tar.gz: 8689282009332c05d4e6a958d1e612c77316f2e6084979b2201f073d5afc8e2d05e778d814f35de1a5c83f33c29e08221ca124c4367811f1bc5d92820de61e6b
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 `stable-v1`.
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 `stable-v1`.
16
+ # @public Since *Async v1*.
17
17
  def initialize(parent: nil)
18
18
  @tasks = List.new
19
19
 
data/lib/async/clock.rb CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  module Async
7
7
  # A convenient wrapper around the internal monotonic clock.
8
- # @public Since `stable-v1`.
8
+ # @public Since *Async v1*.
9
9
  class Clock
10
10
  # Get the current elapsed monotonic time.
11
11
  def self.now
@@ -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 `stable-v1`.
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
- # @public Since `stable-v2`.
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.
@@ -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 `stable-v1`.
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 `stable-v1`.
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 `stable-v1`.
102
+ # @public Since *Async v1*.
103
103
  class LimitedQueue < Queue
104
104
  # Create a new limited queue.
105
105
  #
@@ -27,14 +27,14 @@ module Async
27
27
  end
28
28
 
29
29
  # Whether the fiber scheduler is supported.
30
- # @public Since `stable-v1`.
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 `stable-v1`.
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 `stable-v1`.
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 `stable-v1`.
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
- # @asynchronous May be non-blocking..
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
- # @asynchronous May be non-blocking..
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
- # @asynchronous May be non-blocking..
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
- # Does not handle interrupts.
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, ignoring any signals.
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
- # This is the main entry point for scheduling asynchronus tasks.
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
@@ -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 `stable-v1`.
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 `stable-v1`.
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 `stable-v1`.
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
- Console.warn(self, "Task may have ended with unhandled exception.", exception: error)
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 `stable-v1`.
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
@@ -4,5 +4,5 @@
4
4
  # Copyright, 2017-2024, by Samuel Williams.
5
5
 
6
6
  module Async
7
- VERSION = "2.19.0"
7
+ VERSION = "2.21.0"
8
8
  end
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 `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?
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 `stable-v1`.
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,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,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
@@ -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/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.19.0
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-08 00:00:00.000000000 Z
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.11
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