Class: Phronomy::FSMSession
- Inherits:
-
Object
- Object
- Phronomy::FSMSession
- Defined in:
- lib/phronomy/fsm_session.rb
Overview
Event-driven execution wrapper for a single workflow run.
Created by WorkflowRunner and registered with EventLoop. All public methods are called from the EventLoop thread — FSMSession is NOT thread-safe and must not be accessed concurrently from multiple threads.
== Lifecycle
register(session) → EventLoop posts :start → session.start ↓ (auto-transition present) EventLoop posts :state_completed → session.handle ↓ (repeat) session posts :finished or :halted ↓ EventLoop pushes ctx to completion_queue → caller unblocks
== Async IO pattern (EventLoop mode only)
When a state has no auto-transition and is not a wait_state, but has an external event registered (e.g. +transition from: :fetching, on: :fetch_done+), the FSMSession stays registered in the EventLoop and waits for that event. The entry action is expected to spawn an IO thread that posts the event back:
entry :fetching, ->(ctx) { Thread.new { ctx.result = http.get(ctx.url) Phronomy::EventLoop.instance.post( Phronomy::Event.new(type: :fetch_done, target_id: ctx.thread_id, payload: nil) ) } } transition from: :fetching, on: :fetch_done, to: :process
Constant Summary collapse
- FINISH =
WorkflowRunner::FINISH
Instance Attribute Summary collapse
-
#id ⇒ String
readonly
Workflow thread_id (matches WorkflowContext#thread_id).
Instance Method Summary collapse
-
#handle(event) ⇒ Object
Processes an event dispatched from EventLoop.
-
#initialize(id:, context:, entry_point:, entry_actions:, auto_state_set:, declared_states:, wait_state_names:, external_events:, phase_machine_class:, recursion_limit:, resume_event: nil, resume_phase: nil) ⇒ FSMSession
constructor
A new instance of FSMSession.
-
#start ⇒ Object
Begins workflow execution.
Constructor Details
#initialize(id:, context:, entry_point:, entry_actions:, auto_state_set:, declared_states:, wait_state_names:, external_events:, phase_machine_class:, recursion_limit:, resume_event: nil, resume_phase: nil) ⇒ FSMSession
Returns a new instance of FSMSession.
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/phronomy/fsm_session.rb', line 54 def initialize(id:, context:, entry_point:, entry_actions:, auto_state_set:, declared_states:, wait_state_names:, external_events:, phase_machine_class:, recursion_limit:, resume_event: nil, resume_phase: nil) @id = id @ctx = context @entry_point = entry_point @entry_actions = entry_actions @auto_state_set = auto_state_set @declared_states = declared_states @wait_state_names = wait_state_names @external_events = external_events @phase_machine_class = phase_machine_class @recursion_limit = recursion_limit @resume_event = resume_event @resume_phase = resume_phase @step = 0 @done = false @current_state = nil @tracker = nil end |
Instance Attribute Details
#id ⇒ String (readonly)
Returns workflow thread_id (matches WorkflowContext#thread_id).
40 41 42 |
# File 'lib/phronomy/fsm_session.rb', line 40 def id @id end |
Instance Method Details
#handle(event) ⇒ Object
Processes an event dispatched from EventLoop. Called for :state_completed and all user-defined external events.
102 103 104 105 106 107 108 |
# File 'lib/phronomy/fsm_session.rb', line 102 def handle(event) return if @done fire_and_advance!(event.type) rescue => e finish_with_error(e) end |
#start ⇒ Object
Begins workflow execution. Called by EventLoop on :start event.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/phronomy/fsm_session.rb', line 76 def start if @resume_event # Resume from wait state: position tracker at the wait state, then fire the # external event. state_machines fires before_transition (exit) and # after_transition (entry) callbacks, so both actions execute here. @current_state = @resume_phase @tracker = build_tracker(@current_state) @tracker.context = @ctx fire_and_advance!(@resume_event) else # Fresh start: state_machines does not fire callbacks on initialization, # so we invoke the entry action for the initial state manually. @current_state = @entry_point @tracker = build_tracker(@current_state) @tracker.context = @ctx (@entry_actions[@current_state] || []).each { |c| c.call(@ctx) } advance_or_halt end rescue => e finish_with_error(e) end |