Class: ConvertSdk::BackgroundTimer
- Inherits:
-
Object
- Object
- ConvertSdk::BackgroundTimer
- Defined in:
- lib/convert_sdk/background_timer.rb
Overview
The SDK's single background-thread primitive — the ONLY Thread.new site in
the gem (architecture Decision 6, thread/fork boundary). One class, two
future instances: the config-refresh timer (Story 2.7) and the queue-flush
timer (Story 4.2). It is never subclassed.
A timer wraps an interval, a tick block, and a Mutex'd lifecycle state
machine. #start lazily spawns a single loop thread (NFR4 — no threads
until first use); #stop signals it to exit and joins it; #mark_dead
clears the state WITHOUT joining (the fork re-arm hook — a thread reference
copied into a forked child is dead and joining it can hang), so the next
#start transparently re-arms a fresh thread.
The loop sleeps for interval on a Thread::ConditionVariable (interruptible
— #stop is responsive instead of waiting out a bare sleep), then runs the
block. Each tick is wrapped in rescue StandardError and logged (never
rescue Exception): a raising tick is logged and the loop continues — an
exception must never silently kill a timer thread (never-crash contract).
A nil or zero interval is the timer-off mode: #start is a guarded
no-op and no thread is ever created.
Instance Method Summary collapse
-
#alive? ⇒ Boolean
Whether the loop thread is currently running.
-
#initialize(interval:, log_manager:, name:) { ... } ⇒ BackgroundTimer
constructor
A new instance of BackgroundTimer.
-
#mark_dead ⇒ void
Fork re-arm hook: clear the lifecycle state WITHOUT joining the thread.
-
#start ⇒ void
Start the loop thread if not already running.
-
#stop ⇒ void
Signal the loop to exit and join the thread.
Constructor Details
#initialize(interval:, log_manager:, name:) { ... } ⇒ BackgroundTimer
Returns a new instance of BackgroundTimer.
33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/convert_sdk/background_timer.rb', line 33 def initialize(interval:, log_manager:, name:, &block) @interval = interval @log_manager = log_manager @name = name @block = block # Thread safety: @thread and @running are guarded by @state_mutex; the # condition variable wakes the loop's interruptible sleep on #stop. @state_mutex = Thread::Mutex.new @sleep_cv = Thread::ConditionVariable.new @thread = nil @running = false end |
Instance Method Details
#alive? ⇒ Boolean
Returns whether the loop thread is currently running.
93 94 95 |
# File 'lib/convert_sdk/background_timer.rb', line 93 def alive? @state_mutex.synchronize { @running && !@thread.nil? } end |
#mark_dead ⇒ void
This method returns an undefined value.
Fork re-arm hook: clear the lifecycle state WITHOUT joining the thread. The thread reference is stale in a forked child (fork copies only the calling thread), so joining it can hang. The next #start creates a fresh thread.
85 86 87 88 89 90 |
# File 'lib/convert_sdk/background_timer.rb', line 85 def mark_dead @state_mutex.synchronize do @running = false @thread = nil end end |
#start ⇒ void
This method returns an undefined value.
Start the loop thread if not already running. Idempotent: concurrent calls and repeat calls produce exactly one thread. Re-arms transparently after #mark_dead. A +nil+/zero interval is a no-op (timer-off mode).
50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/convert_sdk/background_timer.rb', line 50 def start @state_mutex.synchronize do # Timer-off guard: a nil/zero interval never starts (and narrows the # interval to a concrete positive Numeric for the loop's sleep). interval = @interval return if interval.nil? || interval <= 0 return if @running @running = true @thread = Thread.new { run_loop(interval.to_f) } @log_manager.debug("BackgroundTimer#start: started ##{@name} (interval=#{@interval}s)") end end |
#stop ⇒ void
This method returns an undefined value.
Signal the loop to exit and join the thread. Idempotent: a no-op when not running (including after #mark_dead).
67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/convert_sdk/background_timer.rb', line 67 def stop thread = nil #: Thread? @state_mutex.synchronize do return unless @running @running = false @sleep_cv.broadcast thread = @thread @thread = nil end thread&.join end |