Class: KairosMcp::SignalHandle
- Inherits:
-
Object
- Object
- KairosMcp::SignalHandle
- Defined in:
- lib/kairos_mcp/signal_handle.rb
Overview
Async-signal-safe signal flag bundle for the Bootstrap layer (24/7 v0.4 §2.2). Carries three one-way signals (shutdown / reload / diagnostic) across the signal-trap boundary.
Async-signal safety:
-
‘shutdown` is a one-way latch — set in trap, never cleared.
-
‘reload` and `diagnostic` are edge-triggered. To avoid losing an edge when a signal arrives mid-consume, they are tracked with a ticket counter pattern (R2 Codex P1):
• trap handler: `@reload_seen += 1` (increment-only) • consumer: diff = seen − consumed; consumed += diffA signal firing between the consumer’s read of ‘seen` and its write to `consumed` is safely picked up on the next call because `consumed += diff` accumulates rather than overwriting.
-
MRI serializes trap handlers and runs them at safepoints — no two handlers execute concurrently, and ‘@x = 1` in a trap is atomic with respect to the main thread (the main thread is paused while the trap runs). On JRuby/TruffleRuby, additional memory fences may be needed; this class targets MRI only for the Bootstrap layer. Richer SkillSet coordinators (see daemon_runtime §2.7 SignalCoordinator) use pipes ConditionVariable and are runtime-portable.
Single-consumer invariant (R3 P3, 4.6):
-
‘consume_reload!` and `consume_diagnostic!` must be called from EXACTLY ONE thread. Two concurrent consumers can both read `@reload_seen` and `@reload_consumed`, then each accumulate the same diff, double-consuming an edge or mis-ordering the `+=`. The Bootstrap layer honors this by having only MainLoop#forward_flags (a dedicated bridge thread) call these. Writers (trap handlers) may be concurrent with the consumer; that case is covered by the ticket accumulation semantics above.
-
‘shutdown_requested` is a one-way latch: trap sets true, nobody ever clears it. This asymmetry is deliberate — shutdown does not need edge semantics because it is terminal.
Instance Method Summary collapse
- #consume_diagnostic! ⇒ Object
-
#consume_reload! ⇒ Object
Edge-safe consume: returns true if at least one signal arrived since the last consume.
- #diagnostic_requested? ⇒ Boolean
-
#initialize ⇒ SignalHandle
constructor
A new instance of SignalHandle.
- #reload_requested? ⇒ Boolean
- #request_diagnostic ⇒ Object
- #request_reload ⇒ Object
-
#request_shutdown ⇒ Object
— setters (called from Signal.trap context) —.
-
#shutdown_requested? ⇒ Boolean
— readers (non-consuming) —.
Constructor Details
#initialize ⇒ SignalHandle
Returns a new instance of SignalHandle.
40 41 42 43 44 45 46 |
# File 'lib/kairos_mcp/signal_handle.rb', line 40 def initialize @shutdown_requested = false @reload_seen = 0 @reload_consumed = 0 @diagnostic_seen = 0 @diagnostic_consumed = 0 end |
Instance Method Details
#consume_diagnostic! ⇒ Object
68 69 70 71 72 73 |
# File 'lib/kairos_mcp/signal_handle.rb', line 68 def consume_diagnostic! seen = @diagnostic_seen diff = seen - @diagnostic_consumed @diagnostic_consumed += diff diff.positive? end |
#consume_reload! ⇒ Object
Edge-safe consume: returns true if at least one signal arrived since the last consume. Accumulates rather than overwrites, so a trap firing mid-consume is never lost.
61 62 63 64 65 66 |
# File 'lib/kairos_mcp/signal_handle.rb', line 61 def consume_reload! seen = @reload_seen diff = seen - @reload_consumed @reload_consumed += diff diff.positive? end |
#diagnostic_requested? ⇒ Boolean
56 |
# File 'lib/kairos_mcp/signal_handle.rb', line 56 def diagnostic_requested?; @diagnostic_seen > @diagnostic_consumed; end |
#reload_requested? ⇒ Boolean
55 |
# File 'lib/kairos_mcp/signal_handle.rb', line 55 def reload_requested?; @reload_seen > @reload_consumed; end |
#request_diagnostic ⇒ Object
51 |
# File 'lib/kairos_mcp/signal_handle.rb', line 51 def request_diagnostic; @diagnostic_seen += 1; end |
#request_reload ⇒ Object
50 |
# File 'lib/kairos_mcp/signal_handle.rb', line 50 def request_reload; @reload_seen += 1; end |
#request_shutdown ⇒ Object
— setters (called from Signal.trap context) —
49 |
# File 'lib/kairos_mcp/signal_handle.rb', line 49 def request_shutdown; @shutdown_requested = true; end |
#shutdown_requested? ⇒ Boolean
— readers (non-consuming) —
54 |
# File 'lib/kairos_mcp/signal_handle.rb', line 54 def shutdown_requested?; @shutdown_requested; end |