ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
keepalive.c
Go to the documentation of this file.
1
64#include "keepalive.h"
65#include "main.h"
66#include "../main.h" // Global exit API
67#include "server.h"
68#include "crypto.h"
69#include <ascii-chat/util/fps.h>
70#include <ascii-chat/util/time.h>
71
72#include <ascii-chat/common.h>
73#include <ascii-chat/platform/abstraction.h>
74#include <ascii-chat/thread_pool.h>
75
76#include <stdatomic.h>
77
78/* ============================================================================
79 * Keepalive Thread Management
80 * ============================================================================ */
81
90static bool g_ping_thread_created = false;
91
100static atomic_bool g_ping_thread_exited = false;
101
102/* ============================================================================
103 * Keepalive Configuration
104 * ============================================================================ */
105
107#define PING_INTERVAL_NS (1LL * NS_PER_SEC_INT)
108
110#define PING_SLEEP_INTERVAL_NS (1LL * NS_PER_SEC_INT)
111
112/* ============================================================================
113 * Ping Thread Implementation
114 * ============================================================================ */
115
141static void *ping_thread_func(void *arg) {
142 (void)arg;
143
144#ifdef DEBUG_THREADS
145 log_debug("Ping thread started");
146#endif
147
148 // FPS tracking for keepalive thread (ping sent every 3 seconds = ~0.33 Hz)
149 static fps_t fps_tracker = {0};
150 static bool fps_tracker_initialized = false;
151 if (!fps_tracker_initialized) {
152 fps_init_with_interval(&fps_tracker, 1, "KEEPALIVE",
153 10 * NS_PER_MS_INT); // 1 "frame" per 3 seconds, report every 10ms
154 fps_tracker_initialized = true;
155 }
156
157 // Startup grace period: Allow connection initialization to complete before checking state
158 // Prevents race condition where thread spawns before transport is fully configured
160
161 while (!should_exit() && !server_connection_is_lost()) {
162 // Check if connection is still active before sending
164 log_debug("Connection inactive, exiting ping thread");
165 break;
166 }
167
168 // Check if session rekeying should be triggered
170 log_debug("Rekey threshold reached, initiating session rekey");
172 log_error("Failed to initiate rekey");
173 // Don't break - continue with keepalive, rekey will be retried
174 }
175 }
176
177 // Send ping packet every PING_INTERVAL_SECONDS to keep connection alive
178 // Server timeout is 5 seconds, so 3-second pings provide safety margin
179 if (threaded_send_ping_packet() < 0) {
180 log_debug("Failed to send ping packet");
181 // Set connection lost flag so main loop knows to reconnect
183 break;
184 }
185
186 // Track ping for FPS reporting
187 uint64_t ping_start_ns = time_get_ns();
188 fps_frame_ns(&fps_tracker, ping_start_ns, "ping sent");
189
190 // Sleep with early wake capability for responsive shutdown
191 // Break sleep into intervals to check shutdown flags and rekey triggers
193 uint64_t now_ns = time_get_ns();
194 uint64_t elapsed_ns = time_elapsed_ns(ping_start_ns, now_ns);
195
196 if (elapsed_ns >= PING_INTERVAL_NS) {
197 break; // Time for next ping
198 }
199
200 // Sleep for one interval or until next ping, whichever is sooner
201 uint64_t remaining_ns = PING_INTERVAL_NS - elapsed_ns;
202 uint64_t sleep_ns = (remaining_ns > PING_SLEEP_INTERVAL_NS) ? PING_SLEEP_INTERVAL_NS : remaining_ns;
203 platform_sleep_ns(sleep_ns);
204 }
205 }
206
207#ifdef DEBUG_THREADS
208 log_debug("Ping thread stopped");
209#endif
210
211 atomic_store(&g_ping_thread_exited, true);
212
213 // Clean up thread-local error context before exit
215
216 return NULL;
217}
218
219/* ============================================================================
220 * Public Interface Functions
221 * ============================================================================ */
222
234 if (g_ping_thread_created) {
235 log_warn("Ping thread already created");
236 return 0;
237 }
238
239 // Start ping thread for keepalive
240 atomic_store(&g_ping_thread_exited, false);
241 if (thread_pool_spawn(g_client_worker_pool, ping_thread_func, NULL, 3, "keepalive_ping") != ASCIICHAT_OK) {
242 log_error("Failed to spawn ping thread in worker pool");
243 LOG_ERRNO_IF_SET("Ping thread creation failed");
244 return -1;
245 }
246
247 g_ping_thread_created = true;
248 return 0;
249}
250
260 if (!g_ping_thread_created) {
261 return;
262 }
263
264 // Don't call signal_exit() here - that's for global shutdown only!
265 // The ping thread monitors connection state and will exit when connection is lost
266
267 // Wait for thread to exit gracefully
268 int wait_count = 0;
269 while (wait_count < 20 && !atomic_load(&g_ping_thread_exited)) {
270 platform_sleep_ns(100 * NS_PER_MS_INT); // 100ms
271 wait_count++;
272 }
273
274 if (!atomic_load(&g_ping_thread_exited)) {
275 log_warn("Ping thread not responding - will be joined by thread pool");
276 }
277
278 // Thread will be joined by thread_pool_stop_all() in protocol_stop_connection()
279 g_ping_thread_created = false;
280
281 log_debug("Ping thread stopped and joined");
282}
283
292 return atomic_load(&g_ping_thread_exited);
293}
void asciichat_errno_destroy(void)
thread_pool_t * g_client_worker_pool
Global client worker thread pool.
bool should_exit(void)
Definition main.c:90
void fps_init_with_interval(fps_t *tracker, int expected_fps, const char *name, uint64_t report_interval_ns)
Definition fps.c:37
void fps_frame_ns(fps_t *tracker, uint64_t current_time_ns, const char *context)
Definition fps.c:52
bool server_connection_is_active()
Check if server connection is currently active.
int threaded_send_ping_packet(void)
Thread-safe ping packet transmission.
bool server_connection_is_lost()
Check if connection loss has been detected.
void server_connection_lost()
Signal that connection has been lost.
bool crypto_client_should_rekey(void)
Check if session rekeying should be triggered.
int crypto_client_initiate_rekey(void)
Initiate session rekeying (client-initiated)
bool keepalive_thread_exited()
Check if keepalive thread has exited.
Definition keepalive.c:291
int keepalive_start_thread()
Start keepalive/ping thread.
Definition keepalive.c:233
void keepalive_stop_thread()
Stop keepalive/ping thread.
Definition keepalive.c:259
#define PING_INTERVAL_NS
Definition keepalive.c:107
#define PING_SLEEP_INTERVAL_NS
Definition keepalive.c:110
ascii-chat Client Connection Keepalive Management Interface
void platform_sleep_ms(unsigned int ms)
Server cryptographic operations and per-client handshake management.
ascii-chat Server Mode Entry Point Header
asciichat_error_t thread_pool_spawn(thread_pool_t *pool, void *(*thread_func)(void *), void *thread_arg, int stop_id, const char *thread_name)
Definition thread_pool.c:70
uint64_t time_get_ns(void)
Definition util/time.c:48
uint64_t time_elapsed_ns(uint64_t start_ns, uint64_t end_ns)
Definition util/time.c:90