ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
state.c
Go to the documentation of this file.
1
6#include <ascii-chat/network/consensus/state.h>
7#include <ascii-chat/common.h>
8#include <ascii-chat/util/time.h>
9#include <ascii-chat/asciichat_errno.h>
10#include <string.h>
11
15typedef struct {
16 uint8_t host_id[16];
17 uint8_t backup_id[16];
18 uint64_t timestamp_ns;
20
24typedef struct consensus_state {
25 consensus_state_machine_t current_state;
27 uint8_t my_id[16];
29 /* Collected metrics storage */
30 participant_metrics_t *metrics;
34 /* Election result */
37
38/* Static helper: compute score from metrics (lower is better) */
39static double compute_metric_score(const participant_metrics_t *m) {
40 /* Simple score: prioritize low RTT, then high bandwidth
41 * Score = RTT_ns / 1000 + (max_bandwidth - upload_kbps) / 1000
42 * Lower RTT = lower score
43 * Higher bandwidth = lower score
44 */
45 double rtt_score = m->rtt_ns / 1000.0; // Convert ns to µs for reasonable scale
46 double bandwidth_penalty = (1000000.0 - m->upload_kbps) / 1000.0;
47 return rtt_score + bandwidth_penalty;
48}
49
50asciichat_error_t consensus_state_create(const uint8_t my_id[16], const consensus_topology_t *topology,
51 consensus_state_t **out_state) {
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
59 state->current_state = CONSENSUS_STATE_IDLE;
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}
73
75 if (state) {
76 if (state->metrics) {
77 SAFE_FREE(state->metrics);
78 }
79 SAFE_FREE(state);
80 }
81}
82
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
96 state->current_state = CONSENSUS_STATE_COLLECTING;
97 return ASCIICHAT_OK;
98}
99
100asciichat_error_t consensus_state_add_metrics(consensus_state_t *state, const participant_metrics_t *metrics) {
101 if (!state || !metrics) {
102 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
103 }
104
105 if (state->current_state != CONSENSUS_STATE_COLLECTING) {
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}
124
126 if (!state) {
127 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
128 }
129
130 if (state->current_state != CONSENSUS_STATE_COLLECTING) {
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 */
136 state->current_state = CONSENSUS_STATE_ELECTION_START;
137 } else {
138 state->current_state = CONSENSUS_STATE_IDLE;
139 }
140
141 return ASCIICHAT_OK;
142}
143
145 if (!state) {
146 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
147 }
148
149 if (state->current_state != CONSENSUS_STATE_ELECTION_START) {
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
193 state->current_state = CONSENSUS_STATE_ELECTION_COMPLETE;
194 return ASCIICHAT_OK;
195}
196
198 if (!state) {
199 return SET_ERRNO(ERROR_INVALID_PARAM, "State is NULL");
200 }
201
202 if (state->current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
203 return SET_ERRNO(ERROR_INVALID_STATE, "Invalid transition from state %d", state->current_state);
204 }
205
206 state->current_state = CONSENSUS_STATE_IDLE;
207 return ASCIICHAT_OK;
208}
209
210consensus_state_machine_t consensus_state_get_current_state(const consensus_state_t *state) {
211 if (!state) {
212 return CONSENSUS_STATE_FAILED;
213 }
214 return state->current_state;
215}
216
217asciichat_error_t consensus_state_get_elected_host(const consensus_state_t *state, uint8_t out_host_id[16]) {
218 if (!state || !out_host_id) {
219 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
220 }
221
222 if (state->current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
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}
229
230asciichat_error_t consensus_state_get_elected_backup(const consensus_state_t *state, uint8_t out_backup_id[16]) {
231 if (!state || !out_backup_id) {
232 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
233 }
234
235 if (state->current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
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}
242
244 if (!state) {
245 return false;
246 }
248}
249
251 if (!state) {
252 return -1;
253 }
254 return state->metrics_count;
255}
256
257asciichat_error_t consensus_state_get_metric_at(const consensus_state_t *state, int index,
258 participant_metrics_t *out_metrics) {
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 */
268 if (state->current_state != CONSENSUS_STATE_COLLECTING &&
269 state->current_state != CONSENSUS_STATE_COLLECTION_COMPLETE &&
270 state->current_state != CONSENSUS_STATE_ELECTION_START && state->current_state != CONSENSUS_STATE_ELECTING &&
271 state->current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
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}
asciichat_error_t consensus_state_start_collection(consensus_state_t *state)
Definition state.c:83
asciichat_error_t consensus_state_compute_election(consensus_state_t *state)
Definition state.c:144
asciichat_error_t consensus_state_get_metric_at(const consensus_state_t *state, int index, participant_metrics_t *out_metrics)
Definition state.c:257
bool consensus_state_is_leader(const consensus_state_t *state)
Definition state.c:243
asciichat_error_t consensus_state_get_elected_backup(const consensus_state_t *state, uint8_t out_backup_id[16])
Definition state.c:230
void consensus_state_destroy(consensus_state_t *state)
Definition state.c:74
asciichat_error_t consensus_state_get_elected_host(const consensus_state_t *state, uint8_t out_host_id[16])
Definition state.c:217
asciichat_error_t consensus_state_reset_to_idle(consensus_state_t *state)
Definition state.c:197
asciichat_error_t consensus_state_collection_complete(consensus_state_t *state)
Definition state.c:125
struct consensus_state consensus_state_t
State machine instance.
int consensus_state_get_metrics_count(const consensus_state_t *state)
Definition state.c:250
asciichat_error_t consensus_state_create(const uint8_t my_id[16], const consensus_topology_t *topology, consensus_state_t **out_state)
Definition state.c:50
consensus_state_machine_t consensus_state_get_current_state(const consensus_state_t *state)
Definition state.c:210
asciichat_error_t consensus_state_add_metrics(consensus_state_t *state, const participant_metrics_t *metrics)
Definition state.c:100
State machine instance.
Definition state.c:24
uint8_t my_id[16]
Definition state.c:27
int metrics_capacity
Definition state.c:32
int metrics_count
Definition state.c:31
election_result_t election_result
Definition state.c:35
participant_metrics_t * metrics
Definition state.c:30
consensus_topology_t * topology
Definition state.c:26
consensus_state_machine_t current_state
Definition state.c:25
Ring topology for consensus participants.
Definition topology.c:17
Election result storage.
Definition state.c:15
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
bool consensus_topology_am_leader(const consensus_topology_t *topology)
Definition topology.c:76
uint64_t time_get_realtime_ns(void)
Definition util/time.c:59