ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
state.h File Reference

Ring consensus state machine. More...

Go to the source code of this file.

Typedefs

typedef struct consensus_state consensus_state_t
 Opaque consensus state machine handle.
 

Enumerations

enum  consensus_state_machine_t {
  CONSENSUS_STATE_IDLE = 0 , CONSENSUS_STATE_COLLECTION_START = 1 , CONSENSUS_STATE_COLLECTING = 2 , CONSENSUS_STATE_COLLECTION_COMPLETE = 3 ,
  CONSENSUS_STATE_ELECTION_START = 4 , CONSENSUS_STATE_ELECTING = 5 , CONSENSUS_STATE_ELECTION_COMPLETE = 6 , CONSENSUS_STATE_FAILED = 7
}
 State machine lifecycle states. More...
 

Functions

asciichat_error_t consensus_state_create (const uint8_t my_id[16], const consensus_topology_t *topology, consensus_state_t **out_state)
 Initialize state machine with topology.
 
void consensus_state_destroy (consensus_state_t *state)
 Destroy state machine and free resources.
 
asciichat_error_t consensus_state_start_collection (consensus_state_t *state)
 Transition to COLLECTION_START state.
 
asciichat_error_t consensus_state_add_metrics (consensus_state_t *state, const participant_metrics_t *metrics)
 Add collected metrics from a participant.
 
asciichat_error_t consensus_state_collection_complete (consensus_state_t *state)
 Transition to COLLECTION_COMPLETE state.
 
asciichat_error_t consensus_state_compute_election (consensus_state_t *state)
 Run election algorithm and transition to ELECTION_COMPLETE.
 
asciichat_error_t consensus_state_reset_to_idle (consensus_state_t *state)
 Transition back to IDLE from ELECTION_COMPLETE.
 
consensus_state_machine_t consensus_state_get_current_state (const consensus_state_t *state)
 Get current state machine state.
 
asciichat_error_t consensus_state_get_elected_host (const consensus_state_t *state, uint8_t out_host_id[16])
 Get elected host ID from last election.
 
asciichat_error_t consensus_state_get_elected_backup (const consensus_state_t *state, uint8_t out_backup_id[16])
 Get elected backup host ID from last election.
 
bool consensus_state_is_leader (const consensus_state_t *state)
 Check if we are the elected leader for this round.
 
int consensus_state_get_metrics_count (const consensus_state_t *state)
 Get count of metrics collected in current round.
 
asciichat_error_t consensus_state_get_metric_at (const consensus_state_t *state, int index, participant_metrics_t *out_metrics)
 Get collected metrics at index.
 

Detailed Description

Ring consensus state machine.

Definition in file state.h.

Typedef Documentation

◆ consensus_state_t

Opaque consensus state machine handle.

Definition at line 32 of file state.h.

Enumeration Type Documentation

◆ consensus_state_machine_t

State machine lifecycle states.

Enumerator
CONSENSUS_STATE_IDLE 

Waiting for round to start

CONSENSUS_STATE_COLLECTION_START 

Round started, prepare collection

CONSENSUS_STATE_COLLECTING 

Collecting metrics

CONSENSUS_STATE_COLLECTION_COMPLETE 

All metrics received

CONSENSUS_STATE_ELECTION_START 

Preparing election (leader only)

CONSENSUS_STATE_ELECTING 

Running election algorithm

CONSENSUS_STATE_ELECTION_COMPLETE 

Election result computed

CONSENSUS_STATE_FAILED 

Error state

Definition at line 18 of file state.h.

18 {
consensus_state_machine_t
State machine lifecycle states.
Definition state.h:18
@ CONSENSUS_STATE_COLLECTION_COMPLETE
Definition state.h:22
@ CONSENSUS_STATE_COLLECTION_START
Definition state.h:20
@ CONSENSUS_STATE_ELECTION_COMPLETE
Definition state.h:25
@ CONSENSUS_STATE_FAILED
Definition state.h:26
@ CONSENSUS_STATE_ELECTING
Definition state.h:24
@ CONSENSUS_STATE_ELECTION_START
Definition state.h:23
@ CONSENSUS_STATE_IDLE
Definition state.h:19
@ CONSENSUS_STATE_COLLECTING
Definition state.h:21

Function Documentation

◆ consensus_state_add_metrics()

asciichat_error_t consensus_state_add_metrics ( consensus_state_t *  state,
const participant_metrics_t *  metrics 
)

Add collected metrics from a participant.

Valid in: COLLECTING state Stores metrics in dynamic array (grows as needed, initial capacity 10)

Parameters
stateState machine handle
metricsParticipant metrics to store
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if not in COLLECTING state

Definition at line 100 of file state.c.

100 {
101 if (!state || !metrics) {
102 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
103 }
104
106 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid state for adding metrics: %d", state->current_state);
107 }
108
109 /* Grow array if needed */
110 if (state->metrics_count >= state->metrics_capacity) {
111 int new_capacity = state->metrics_capacity * 2;
112 participant_metrics_t *new_metrics =
113 SAFE_REALLOC(state->metrics, new_capacity * sizeof(participant_metrics_t), participant_metrics_t *);
114 state->metrics = new_metrics;
115 state->metrics_capacity = new_capacity;
116 }
117
118 /* Copy metrics into array */
119 memcpy(&state->metrics[state->metrics_count], metrics, sizeof(participant_metrics_t));
120 state->metrics_count++;
121
122 return ASCIICHAT_OK;
123}
#define SAFE_REALLOC(ptr, size, cast)
Definition common.h:284
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
@ ERROR_INVALID_STATE
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
int metrics_capacity
Definition state.c:32
int metrics_count
Definition state.c:31
participant_metrics_t * metrics
Definition state.c:30
consensus_state_machine_t current_state
Definition state.c:25

References ASCIICHAT_OK, CONSENSUS_STATE_COLLECTING, consensus_state::current_state, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, consensus_state::metrics, consensus_state::metrics_capacity, consensus_state::metrics_count, SAFE_REALLOC, and SET_ERRNO.

Referenced by consensus_coordinator_on_stats_update().

◆ consensus_state_collection_complete()

asciichat_error_t consensus_state_collection_complete ( consensus_state_t *  state)

Transition to COLLECTION_COMPLETE state.

Valid from: COLLECTING Transitions to: ELECTION_START (if leader) or IDLE (if not leader)

Parameters
stateState machine handle
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if transition invalid

Definition at line 125 of file state.c.

125 {
126 if (!state) {
127 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
128 }
129
131 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid transition from state %d", state->current_state);
132 }
133
134 /* Transition: if we're leader, go to ELECTION_START; otherwise go to IDLE */
137 } else {
139 }
140
141 return ASCIICHAT_OK;
142}
consensus_topology_t * topology
Definition state.c:26
bool consensus_topology_am_leader(const consensus_topology_t *topology)
Check if I am the ring leader (last position)
Definition topology.c:76

References ASCIICHAT_OK, CONSENSUS_STATE_COLLECTING, CONSENSUS_STATE_ELECTION_START, CONSENSUS_STATE_IDLE, consensus_topology_am_leader(), consensus_state::current_state, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, SET_ERRNO, and consensus_state::topology.

◆ consensus_state_compute_election()

asciichat_error_t consensus_state_compute_election ( consensus_state_t *  state)

Run election algorithm and transition to ELECTION_COMPLETE.

Valid from: ELECTION_START or ELECTION_COMPLETE Processes collected metrics to select best host and backup. Election selects host as participant with best score, backup as second-best. Score is computed from metrics: lower RTT + higher bandwidth = better score. Only leader should call this.

Parameters
stateState machine handle
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if not in ELECTION_START state

Definition at line 144 of file state.c.

144 {
145 if (!state) {
146 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
147 }
148
150 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid transition from state %d", state->current_state);
151 }
152
153 if (state->metrics_count < 2) {
154 return SET_ERRNO(ERROR_INVALID_PARAM, "Need at least 2 metrics for election, have %d", state->metrics_count);
155 }
156
157 /* Find best and second-best metrics */
158 int best_idx = 0;
159 int backup_idx = 1;
160
161 double best_score = compute_metric_score(&state->metrics[0]);
162 double backup_score = compute_metric_score(&state->metrics[1]);
163
164 /* Ensure best_idx has lower score */
165 if (best_score > backup_score) {
166 int tmp = best_idx;
167 best_idx = backup_idx;
168 backup_idx = tmp;
169 double tmp_score = best_score;
170 best_score = backup_score;
171 backup_score = tmp_score;
172 }
173
174 /* Find best and second-best */
175 for (int i = 2; i < state->metrics_count; i++) {
176 double score = compute_metric_score(&state->metrics[i]);
177 if (score < best_score) {
178 backup_idx = best_idx;
179 backup_score = best_score;
180 best_idx = i;
181 best_score = score;
182 } else if (score < backup_score) {
183 backup_idx = i;
184 backup_score = score;
185 }
186 }
187
188 /* Store election result */
189 memcpy(state->election_result.host_id, state->metrics[best_idx].participant_id, 16);
190 memcpy(state->election_result.backup_id, state->metrics[backup_idx].participant_id, 16);
192
194 return ASCIICHAT_OK;
195}
uint64_t time_get_realtime_ns(void)
Get current wall-clock (real) time in nanoseconds.
Definition util/time.c:119
election_result_t election_result
Definition state.c:35
uint8_t backup_id[16]
Definition state.c:17
uint8_t host_id[16]
Definition state.c:16
uint64_t timestamp_ns
Definition state.c:18

References ASCIICHAT_OK, election_result_t::backup_id, CONSENSUS_STATE_ELECTION_COMPLETE, CONSENSUS_STATE_ELECTION_START, consensus_state::current_state, consensus_state::election_result, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, election_result_t::host_id, consensus_state::metrics, consensus_state::metrics_count, SET_ERRNO, time_get_realtime_ns(), and election_result_t::timestamp_ns.

◆ consensus_state_create()

asciichat_error_t consensus_state_create ( const uint8_t  my_id[16],
const consensus_topology_t *  topology,
consensus_state_t **  out_state 
)

Initialize state machine with topology.

Creates a new state machine for managing consensus rounds. Initial state is CONSENSUS_STATE_IDLE.

Parameters
my_idMy 16-byte UUID
topologyTopology handle (not owned by state machine)
out_stateOutput state machine handle (caller must destroy)
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 50 of file state.c.

51 {
52 if (!my_id || !topology || !out_state) {
53 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid state creation parameters");
54 }
55
56 consensus_state_t *state = SAFE_MALLOC(sizeof(*state), consensus_state_t *);
57 memset(state, 0, sizeof(*state));
58
60 state->topology = (consensus_topology_t *)topology; /* Cast away const for storage */
61 memcpy(state->my_id, my_id, 16);
62
63 /* Initialize metrics array with initial capacity of 10 */
64 state->metrics_capacity = 10;
65 state->metrics_count = 0;
66 state->metrics = SAFE_MALLOC(state->metrics_capacity * sizeof(participant_metrics_t), participant_metrics_t *);
67
68 memset(&state->election_result, 0, sizeof(election_result_t));
69
70 *out_state = state;
71 return ASCIICHAT_OK;
72}
#define SAFE_MALLOC(size, cast)
Definition common.h:264
State machine instance.
Definition state.c:24
uint8_t my_id[16]
Definition state.c:27
Ring topology for consensus participants.
Definition topology.c:17
Election result storage.
Definition state.c:15

References ASCIICHAT_OK, CONSENSUS_STATE_IDLE, consensus_state::current_state, consensus_state::election_result, ERROR_INVALID_PARAM, consensus_state::metrics, consensus_state::metrics_capacity, consensus_state::metrics_count, consensus_state::my_id, SAFE_MALLOC, SET_ERRNO, and consensus_state::topology.

Referenced by consensus_coordinator_create(), and consensus_coordinator_on_ring_members().

◆ consensus_state_destroy()

void consensus_state_destroy ( consensus_state_t *  state)

Destroy state machine and free resources.

Parameters
stateState machine handle (safe to pass NULL)

Definition at line 74 of file state.c.

74 {
75 if (state) {
76 if (state->metrics) {
77 SAFE_FREE(state->metrics);
78 }
79 SAFE_FREE(state);
80 }
81}
#define SAFE_FREE(ptr)
Definition common.h:376

References consensus_state::metrics, and SAFE_FREE.

Referenced by consensus_coordinator_destroy(), and consensus_coordinator_on_ring_members().

◆ consensus_state_get_current_state()

consensus_state_machine_t consensus_state_get_current_state ( const consensus_state_t *  state)

Get current state machine state.

Parameters
stateState machine handle
Returns
Current state (FAILED if state is NULL)

Definition at line 210 of file state.c.

210 {
211 if (!state) {
213 }
214 return state->current_state;
215}

References CONSENSUS_STATE_FAILED, and consensus_state::current_state.

Referenced by consensus_coordinator_get_current_host(), consensus_coordinator_get_state(), consensus_coordinator_on_collection_start(), consensus_coordinator_on_election_result(), consensus_coordinator_on_ring_members(), consensus_coordinator_on_stats_update(), and consensus_coordinator_process().

◆ consensus_state_get_elected_backup()

asciichat_error_t consensus_state_get_elected_backup ( const consensus_state_t *  state,
uint8_t  out_backup_id[16] 
)

Get elected backup host ID from last election.

Valid from: ELECTION_COMPLETE state

Parameters
stateState machine handle
out_backup_idOutput 16-byte backup UUID (caller provides buffer)
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if election not complete

Definition at line 230 of file state.c.

230 {
231 if (!state || !out_backup_id) {
232 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
233 }
234
236 return SET_ERRNO(ERROR_INVALID_STATE, "Election not complete, current state: %d", state->current_state);
237 }
238
239 memcpy(out_backup_id, state->election_result.backup_id, 16);
240 return ASCIICHAT_OK;
241}

References ASCIICHAT_OK, election_result_t::backup_id, CONSENSUS_STATE_ELECTION_COMPLETE, consensus_state::current_state, consensus_state::election_result, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by consensus_coordinator_get_current_host().

◆ consensus_state_get_elected_host()

asciichat_error_t consensus_state_get_elected_host ( const consensus_state_t *  state,
uint8_t  out_host_id[16] 
)

Get elected host ID from last election.

Valid from: ELECTION_COMPLETE state

Parameters
stateState machine handle
out_host_idOutput 16-byte host UUID (caller provides buffer)
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if election not complete

Definition at line 217 of file state.c.

217 {
218 if (!state || !out_host_id) {
219 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
220 }
221
223 return SET_ERRNO(ERROR_INVALID_STATE, "Election not complete, current state: %d", state->current_state);
224 }
225
226 memcpy(out_host_id, state->election_result.host_id, 16);
227 return ASCIICHAT_OK;
228}

References ASCIICHAT_OK, CONSENSUS_STATE_ELECTION_COMPLETE, consensus_state::current_state, consensus_state::election_result, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, election_result_t::host_id, and SET_ERRNO.

Referenced by consensus_coordinator_get_current_host().

◆ consensus_state_get_metric_at()

asciichat_error_t consensus_state_get_metric_at ( const consensus_state_t *  state,
int  index,
participant_metrics_t *  out_metrics 
)

Get collected metrics at index.

Valid from: COLLECTING, COLLECTION_COMPLETE, or ELECTION states

Parameters
stateState machine handle
indexIndex of metric to retrieve (0-based)
out_metricsOutput metrics structure
Returns
ASCIICHAT_OK on success, error code otherwise

Definition at line 257 of file state.c.

258 {
259 if (!state || !out_metrics || index < 0) {
260 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
261 }
262
263 if (index >= state->metrics_count) {
264 return SET_ERRNO(ERROR_INVALID_PARAM, "Index %d out of bounds (count: %d)", index, state->metrics_count);
265 }
266
267 /* Valid to retrieve metrics in COLLECTING, COLLECTION_COMPLETE, or ELECTION states */
272 return SET_ERRNO(ERROR_INVALID_STATE, "Cannot retrieve metrics in state %d", state->current_state);
273 }
274
275 memcpy(out_metrics, &state->metrics[index], sizeof(participant_metrics_t));
276 return ASCIICHAT_OK;
277}

References ASCIICHAT_OK, CONSENSUS_STATE_COLLECTING, CONSENSUS_STATE_COLLECTION_COMPLETE, CONSENSUS_STATE_ELECTING, CONSENSUS_STATE_ELECTION_COMPLETE, CONSENSUS_STATE_ELECTION_START, consensus_state::current_state, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, consensus_state::metrics, consensus_state::metrics_count, and SET_ERRNO.

◆ consensus_state_get_metrics_count()

int consensus_state_get_metrics_count ( const consensus_state_t *  state)

Get count of metrics collected in current round.

Parameters
stateState machine handle
Returns
Number of metrics collected, or -1 if invalid

Definition at line 250 of file state.c.

250 {
251 if (!state) {
252 return -1;
253 }
254 return state->metrics_count;
255}

References consensus_state::metrics_count.

Referenced by consensus_coordinator_get_metrics_count().

◆ consensus_state_is_leader()

bool consensus_state_is_leader ( const consensus_state_t *  state)

Check if we are the elected leader for this round.

Parameters
stateState machine handle
Returns
true if we are the ring leader, false otherwise

Definition at line 243 of file state.c.

243 {
244 if (!state) {
245 return false;
246 }
248}

References consensus_topology_am_leader(), and consensus_state::topology.

◆ consensus_state_reset_to_idle()

asciichat_error_t consensus_state_reset_to_idle ( consensus_state_t *  state)

Transition back to IDLE from ELECTION_COMPLETE.

Valid from: ELECTION_COMPLETE Transitions to: IDLE

Parameters
stateState machine handle
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if transition invalid

Definition at line 197 of file state.c.

197 {
198 if (!state) {
199 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
200 }
201
203 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid transition from state %d", state->current_state);
204 }
205
207 return ASCIICHAT_OK;
208}

References ASCIICHAT_OK, CONSENSUS_STATE_ELECTION_COMPLETE, CONSENSUS_STATE_IDLE, consensus_state::current_state, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by consensus_coordinator_on_election_result().

◆ consensus_state_start_collection()

asciichat_error_t consensus_state_start_collection ( consensus_state_t *  state)

Transition to COLLECTION_START state.

Valid from: IDLE Transitions to: COLLECTING

Parameters
stateState machine handle
Returns
ASCIICHAT_OK on success, ERROR_INVALID_STATE if transition invalid

Definition at line 83 of file state.c.

83 {
84 if (!state) {
85 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
86 }
87
88 if (state->current_state != CONSENSUS_STATE_IDLE) {
89 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid transition from state %d", state->current_state);
90 }
91
92 /* Reset metrics from previous round */
93 state->metrics_count = 0;
94 memset(&state->election_result, 0, sizeof(election_result_t));
95
97 return ASCIICHAT_OK;
98}

References ASCIICHAT_OK, CONSENSUS_STATE_COLLECTING, CONSENSUS_STATE_IDLE, consensus_state::current_state, consensus_state::election_result, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, consensus_state::metrics_count, and SET_ERRNO.

Referenced by consensus_coordinator_on_collection_start().