Class: PatientHttp::LifecycleManager

Inherits:
Object
  • Object
show all
Includes:
TimeHelper
Defined in:
lib/patient_http/lifecycle_manager.rb

Overview

Manages the lifecycle state of the Processor.

Handles state transitions and provides predicates for checking the current state. Thread-safe state management using Concurrent::AtomicReference.

Constant Summary collapse

STATES =

Valid processor states

%i[stopped starting running draining stopping].freeze
POLL_INTERVAL =

Polling interval during wait operations

0.01

Instance Method Summary collapse

Methods included from TimeHelper

#monotonic_time, #wall_clock_time

Constructor Details

#initializevoid

Initialize the lifecycle manager.



20
21
22
23
24
25
# File 'lib/patient_http/lifecycle_manager.rb', line 20

def initialize
  @state = Concurrent::AtomicReference.new(:stopped)
  @shutdown_barrier = Concurrent::Event.new
  @reactor_ready = Concurrent::Event.new
  @lock = Mutex.new
end

Instance Method Details

#drain!Boolean

Transition to draining state.

Returns:

  • (Boolean)

    true if transition was successful



105
106
107
108
109
110
111
112
113
# File 'lib/patient_http/lifecycle_manager.rb', line 105

def drain!
  @lock.synchronize do
    return false unless running?

    @state.set(:draining)
  end

  true
end

#draining?Boolean

Check if processor is draining.

Returns:

  • (Boolean)

    true if draining



58
59
60
# File 'lib/patient_http/lifecycle_manager.rb', line 58

def draining?
  state == :draining
end

#reactor_ready!void

This method returns an undefined value.

Signal that the reactor is ready.



144
145
146
# File 'lib/patient_http/lifecycle_manager.rb', line 144

def reactor_ready!
  @reactor_ready.set
end

#running!Boolean

Transition to running state.

The transition only occurs from the starting state so that a reactor that already failed and transitioned to stopped is not overwritten.

Returns:

  • (Boolean)

    true if transition was successful



92
93
94
95
96
97
98
99
100
# File 'lib/patient_http/lifecycle_manager.rb', line 92

def running!
  @lock.synchronize do
    return false unless starting?

    @state.set(:running)
  end

  true
end

#running?Boolean

Check if processor is running.

Returns:

  • (Boolean)

    true if running



44
45
46
# File 'lib/patient_http/lifecycle_manager.rb', line 44

def running?
  state == :running
end

#shutdown_signaled?Boolean

Check if shutdown has been signaled.

Returns:

  • (Boolean)

    true if shutdown is signaled



159
160
161
# File 'lib/patient_http/lifecycle_manager.rb', line 159

def shutdown_signaled?
  @shutdown_barrier.set?
end

#start!Boolean

Transition to starting state. The processor can only be started from the stopped state; in particular, starting a draining processor would spawn a second reactor alongside the one still finishing its drain.

Returns:

  • (Boolean)

    true if transition was successful



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/patient_http/lifecycle_manager.rb', line 74

def start!
  @lock.synchronize do
    return false unless stopped?

    @state.set(:starting)
    @shutdown_barrier.reset
    @reactor_ready.reset
  end

  true
end

#starting?Boolean

Check if processor is starting.

Returns:

  • (Boolean)

    true if starting



37
38
39
# File 'lib/patient_http/lifecycle_manager.rb', line 37

def starting?
  state == :starting
end

#stateSymbol

Get the current state.

Returns:

  • (Symbol)

    the current state



30
31
32
# File 'lib/patient_http/lifecycle_manager.rb', line 30

def state
  @state.get
end

#stop!Boolean

Transition to stopping state.

Returns:

  • (Boolean)

    true if transition was successful



118
119
120
121
122
123
124
125
126
127
# File 'lib/patient_http/lifecycle_manager.rb', line 118

def stop!
  @lock.synchronize do
    return false if stopped? || stopping? || starting?

    @state.set(:stopping)
    @shutdown_barrier.set
  end

  true
end

#stopped!void

This method returns an undefined value.

Transition to stopped state.

Also signals the reactor_ready event to unblock any thread waiting in #wait_for_reactor in case the reactor failed before it could signal readiness.



136
137
138
139
# File 'lib/patient_http/lifecycle_manager.rb', line 136

def stopped!
  @state.set(:stopped)
  @reactor_ready.set
end

#stopped?Boolean

Check if processor is stopped.

Returns:

  • (Boolean)

    true if stopped



51
52
53
# File 'lib/patient_http/lifecycle_manager.rb', line 51

def stopped?
  state == :stopped
end

#stopping?Boolean

Check if processor is stopping.

Returns:

  • (Boolean)

    true if stopping



65
66
67
# File 'lib/patient_http/lifecycle_manager.rb', line 65

def stopping?
  state == :stopping
end

#wait_for_condition(timeout: 1) { ... } ⇒ Boolean

Wait for a condition to be met.

Parameters:

  • timeout (Numeric) (defaults to: 1)

    maximum time to wait in seconds

Yields:

  • Block that checks the condition.

Returns:

  • (Boolean)

    true if the condition is met, false if timeout reached



176
177
178
179
180
181
182
183
184
# File 'lib/patient_http/lifecycle_manager.rb', line 176

def wait_for_condition(timeout: 1)
  deadline = monotonic_time + timeout
  while monotonic_time <= deadline
    return true if yield

    sleep(POLL_INTERVAL)
  end
  false
end

#wait_for_reactor(timeout: nil) ⇒ Boolean

Wait for the reactor to be ready.

Parameters:

  • timeout (Numeric, nil) (defaults to: nil)

    maximum time to wait in seconds (nil waits forever)

Returns:

  • (Boolean)

    true if the reactor is ready, false if the timeout was reached



152
153
154
# File 'lib/patient_http/lifecycle_manager.rb', line 152

def wait_for_reactor(timeout: nil)
  @reactor_ready.wait(timeout)
end

#wait_for_running(timeout: 5) ⇒ Boolean

Wait for running state.

Parameters:

  • timeout (Numeric) (defaults to: 5)

    maximum time to wait in seconds

Returns:

  • (Boolean)

    true if running, false if timeout reached



167
168
169
# File 'lib/patient_http/lifecycle_manager.rb', line 167

def wait_for_running(timeout: 5)
  wait_for_condition(timeout: timeout) { running? }
end