Skip to content

Commit 21f9d55

Browse files
committed
Add ASYNC_SCHEDULER_DEBUG feature for diagnosing hangs.
1 parent 0a79692 commit 21f9d55

File tree

2 files changed

+56
-1
lines changed

2 files changed

+56
-1
lines changed

lib/async/scheduler.rb

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
module Async
1717
# Handles scheduling of fibers. Implements the fiber scheduler interface.
1818
class Scheduler < Node
19+
# Whether to enable debug output.
20+
DEBUG = ENV.fetch("ASYNC_SCHEDULER_DEBUG", false) == "true"
21+
1922
# Raised when an operation is attempted on a closed scheduler.
2023
class ClosedError < RuntimeError
2124
# Create a new error.
@@ -388,14 +391,21 @@ def stop
388391
end
389392
rescue Interrupt => interrupt
390393
Thread.handle_interrupt(::SignalException => :never) do
394+
if DEBUG
395+
$stderr.puts "Scheduler interrupted: #{interrupt.inspect}"
396+
self.print_hierarchy($stderr)
397+
end
398+
391399
self.stop
392400
end
393401

394402
retry
395403
end
396404

397405
# 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.
398-
Kernel.raise(interrupt) if interrupt
406+
if interrupt
407+
Kernel.raise(interrupt)
408+
end
399409
end
400410

401411
# Run the reactor until all tasks are finished. Proxies arguments to {#async} immediately before entering the loop, if a block is provided.

releases.md

+45
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,51 @@
22

33
## Unreleased
44

5+
### Async::Scheduler Debug
6+
7+
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 doesn't provide enough information to diagnose the problem. I've added a new environment variable, `ASYNC_SCHEDULER_DEBUG=true` which will print out the current state of the scheduler when you press Ctrl-C.
8+
9+
```
10+
> ASYNC_SCHEDULER_DEBUG=true bundle exec ruby ./test.rb
11+
^CScheduler interrupted: Interrupt
12+
#<Async::Reactor:0x0000000000000910 1 children (running)>
13+
#<Async::Task:0x0000000000000924 /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:187:in `transfer' (running)>
14+
→ /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:187:in `transfer'
15+
/home/samuel/Developer/socketry/async/lib/async/scheduler.rb:187:in `block'
16+
/home/samuel/Developer/socketry/async/lib/async/scheduler.rb:209:in `kernel_sleep'
17+
./test.rb:4:in `sleep'
18+
./test.rb:4:in `sleepy'
19+
./test.rb:10:in `sleepy'
20+
./test.rb:10:in `sleepy'
21+
./test.rb:10:in `sleepy'
22+
./test.rb:14:in `block in <main>'
23+
/home/samuel/Developer/socketry/async/lib/async/task.rb:197:in `block in run'
24+
/home/samuel/Developer/socketry/async/lib/async/task.rb:420:in `block in schedule'
25+
#<Async::Task:0x0000000000000938 /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:187:in `transfer' (running)>
26+
→ /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:187:in `transfer'
27+
/home/samuel/Developer/socketry/async/lib/async/scheduler.rb:187:in `block'
28+
/home/samuel/Developer/socketry/async/lib/async/scheduler.rb:209:in `kernel_sleep'
29+
./test.rb:4:in `sleep'
30+
./test.rb:4:in `sleepy'
31+
./test.rb:10:in `sleepy'
32+
./test.rb:10:in `sleepy'
33+
./test.rb:7:in `block in sleepy'
34+
/home/samuel/Developer/socketry/async/lib/async/task.rb:197:in `block in run'
35+
/home/samuel/Developer/socketry/async/lib/async/task.rb:420:in `block in schedule'
36+
/home/samuel/Developer/socketry/async/lib/async/scheduler.rb:319:in `select': Interrupt
37+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:319:in `run_once!'
38+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:355:in `run_once'
39+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:417:in `block in run'
40+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:388:in `block in run_loop'
41+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:385:in `handle_interrupt'
42+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:385:in `run_loop'
43+
from /home/samuel/Developer/socketry/async/lib/async/scheduler.rb:416:in `run'
44+
from /home/samuel/Developer/socketry/async/lib/kernel/async.rb:34:in `Async'
45+
from ./test.rb:13:in `<main>'
46+
```
47+
48+
This gives better visibility into what the scheduler is doing, and should help diagnose issues.
49+
550
### Console Shims
651

752
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.

0 commit comments

Comments
 (0)