async-signals 0.2.0 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d11c9736e5b39139e1875619f776b0ffa1153c6782e3aaed48b0703ec50865ad
4
- data.tar.gz: c5eca9092f34e538c9b76bc3feebdc005ca9a7fe8ac1283f77aac16602b245e3
3
+ metadata.gz: 892b6d001f6a23c4190ecb71cd652d63aea1a7331932a68f3b0f057f8cdf762e
4
+ data.tar.gz: fd8a403c5abfe0a943ce0ec4c684ea5b6ba390f6c76006ed6772d0e12532d903
5
5
  SHA512:
6
- metadata.gz: 832500ae1ef771269fa4d05eb6322332ab70e1d9003594001d2ef18831bcd8c4430c65005ca087b167efde19ba765ef5f273f7add5ec639f9b840769dcbf1488
7
- data.tar.gz: 937153c1ddf69d8211150f428d5f674f6992cd88526b4b82e1a4a0e26395d238e427a96e378cc69b46b3415a323d087354df9a8c932f881378f22a0fa260389f
6
+ metadata.gz: cc9458635104d6f45cdcde03512d1e5e3dcaf09f21fa345bad891f678e2a5f6d80177c5dd7f84bc11e0a2d2b4b0c4582bba5cea9871bae6aafefaf320b05b124
7
+ data.tar.gz: f885189f0ae71ef8c529bf17ae23c9ad080c22641716fd30e4c54c11bc0659786c030536b6448472ac9fb66667ed0736e180e0e746bfe3972cd9ad26589a8022
checksums.yaml.gz.sig CHANGED
Binary file
@@ -55,6 +55,14 @@ end
55
55
 
56
56
  When the block exits, the handler set is removed and any previous signal trap is restored.
57
57
 
58
+ Handlers may also accept the context that installed the handler set. This is useful when a signal should interrupt the component that installed the handlers, regardless of which thread dispatches the signal trap.
59
+
60
+ ```ruby
61
+ handlers.trap(:INT) do |signal, context|
62
+ context.raise(Interrupt)
63
+ end
64
+ ```
65
+
58
66
  ### Multiple Consumers
59
67
 
60
68
  Multiple parts of an application can listen for the same signal. This is useful when a service, supervisor, and application component each need to observe shutdown signals without taking ownership of the process-wide trap.
@@ -169,6 +177,8 @@ Avoid calling `Signal.trap` for the same signals while `async-signals` handlers
169
177
 
170
178
  Keep signal handlers thread safe. Ruby implementations may dispatch signal traps from an implementation-specific thread, so handlers should avoid mutating shared state directly. Prefer doing minimal work in the handler and forwarding the event to a thread-safe mechanism such as `Thread::Queue`.
171
179
 
180
+ Handler exceptions propagate from dispatch. If multiple handler sets observe the same signal and one handler raises, later handlers may not run.
181
+
172
182
  ## Troubleshooting
173
183
 
174
184
  If a handler is not invoked, check that the handler set is installed at the time the signal is delivered. Handler sets are only active inside the `Async::Signals.install` block, or until the returned registration is closed.
@@ -55,6 +55,14 @@ end
55
55
 
56
56
  When the block exits, the handler set is removed and any previous signal trap is restored.
57
57
 
58
+ Handlers may also accept the context that installed the handler set. This is useful when a signal should interrupt the component that installed the handlers, regardless of which thread dispatches the signal trap.
59
+
60
+ ```ruby
61
+ handlers.trap(:INT) do |signal, context|
62
+ context.raise(Interrupt)
63
+ end
64
+ ```
65
+
58
66
  ### Multiple Consumers
59
67
 
60
68
  Multiple parts of an application can listen for the same signal. This is useful when a service, supervisor, and application component each need to observe shutdown signals without taking ownership of the process-wide trap.
@@ -169,6 +177,8 @@ Avoid calling `Signal.trap` for the same signals while `async-signals` handlers
169
177
 
170
178
  Keep signal handlers thread safe. Ruby implementations may dispatch signal traps from an implementation-specific thread, so handlers should avoid mutating shared state directly. Prefer doing minimal work in the handler and forwarding the event to a thread-safe mechanism such as `Thread::Queue`.
171
179
 
180
+ Handler exceptions propagate from dispatch. If multiple handler sets observe the same signal and one handler raises, later handlers may not run.
181
+
172
182
  ## Troubleshooting
173
183
 
174
184
  If a handler is not invoked, check that the handler set is installed at the time the signal is delivered. Handler sets are only active inside the `Async::Signals.install` block, or until the returned registration is closed.
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ module Async
7
+ module Signals
8
+ # Represents the execution context that installed a signal handler set.
9
+ class Context
10
+ # Initialize the context.
11
+ def initialize
12
+ # Capture both primitives so the public interface can evolve without
13
+ # changing the handler arguments.
14
+ @thread = ::Thread.current
15
+ @fiber = ::Fiber.current
16
+ end
17
+
18
+ # Raise an exception in the thread that installed the handler set.
19
+ # @parameter arguments [Array] The arguments to pass to {Thread#raise}.
20
+ def raise(*arguments)
21
+ @thread.raise(*arguments)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -6,6 +6,7 @@
6
6
  require "thread"
7
7
 
8
8
  require_relative "handlers"
9
+ require_relative "context"
9
10
 
10
11
  module Async
11
12
  module Signals
@@ -74,9 +75,11 @@ module Async
74
75
  end
75
76
 
76
77
  # The active callable signal handlers.
77
- # @returns [Array(Proc)] The active handlers.
78
+ # @returns [Array(Array(Proc, Context))] The active handlers and the contexts that installed them.
78
79
  def callbacks
79
- @handlers.values.freeze
80
+ @handlers.map do |registration, handler|
81
+ [handler, registration.context]
82
+ end.freeze
80
83
  end
81
84
  end
82
85
 
@@ -88,8 +91,12 @@ module Async
88
91
  def initialize(controller, handlers)
89
92
  @controller = controller
90
93
  @handlers = handlers
94
+ @context = Context.new
91
95
  end
92
96
 
97
+ # @attribute [Context] The context that installed this registration.
98
+ attr :context
99
+
93
100
  # Remove this registration from the controller.
94
101
  def close
95
102
  if handlers = @handlers
@@ -138,12 +145,8 @@ module Async
138
145
  def dispatch(signal)
139
146
  number = ::Signal.list.fetch(signal)
140
147
 
141
- @dispatch[signal]&.each do |handler|
142
- begin
143
- handler.call(number)
144
- rescue Exception => error
145
- warn "Async::Signals handler failed: #{error.class}: #{error.message}"
146
- end
148
+ @dispatch[signal]&.each do |handler, context|
149
+ handler.call(number, context)
147
150
  end
148
151
  end
149
152
 
@@ -16,6 +16,7 @@ module Async
16
16
 
17
17
  # Trap a signal while these handlers are installed.
18
18
  # @parameter signal [Symbol | String | Integer] The signal to trap.
19
+ # @yields {|signal, context| ...} The signal number and the context that installed the handler set.
19
20
  def trap(signal, &block)
20
21
  @signals[normalize(signal)] = block
21
22
  end
@@ -7,6 +7,6 @@
7
7
  module Async
8
8
  # @namespace
9
9
  module Signals
10
- VERSION = "0.2.0"
10
+ VERSION = "0.3.0"
11
11
  end
12
12
  end
data/lib/async/signals.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  # Copyright, 2026, by Samuel Williams.
5
5
 
6
6
  require_relative "signals/version"
7
+ require_relative "signals/context"
7
8
  require_relative "signals/handlers"
8
9
  require_relative "signals/controller"
9
10
  require_relative "signals/ignore"
data/readme.md CHANGED
@@ -24,6 +24,10 @@ Please see the [project documentation](https://socketry.github.io/async-signals/
24
24
 
25
25
  Please see the [project releases](https://socketry.github.io/async-signals/releases/index) for all releases.
26
26
 
27
+ ### v0.3.0
28
+
29
+ - Pass the installing context as the second signal handler argument and allow handler exceptions to propagate.
30
+
27
31
  ### v0.2.0
28
32
 
29
33
  - Add `Async::Signals.default` and `Async::Signals::Ignore` for selecting process signal handling based on the current thread.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.3.0
4
+
5
+ - Pass the installing context as the second signal handler argument and allow handler exceptions to propagate.
6
+
3
7
  ## v0.2.0
4
8
 
5
9
  - Add `Async::Signals.default` and `Async::Signals::Ignore` for selecting process signal handling based on the current thread.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: async-signals
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -47,6 +47,7 @@ files:
47
47
  - guides/getting-started/readme.md
48
48
  - guides/links.yaml
49
49
  - lib/async/signals.rb
50
+ - lib/async/signals/context.rb
50
51
  - lib/async/signals/controller.rb
51
52
  - lib/async/signals/handlers.rb
52
53
  - lib/async/signals/ignore.rb
metadata.gz.sig CHANGED
Binary file