ascii-chat 0.11.33
Video chat in your terminal
Loading...
Searching...
No Matches
client.c File Reference

Go to the source code of this file.

Macros

#define MAX_RECONNECT_DELAY   (5LL * NS_PER_SEC_INT)
 

Functions

tcp_client_t * tcp_client_create (void)
 Create and initialize TCP client.
 
void tcp_client_destroy (tcp_client_t **client_ptr)
 Destroy TCP client and free resources.
 
bool tcp_client_is_active (const tcp_client_t *client)
 Check if connection is currently active.
 
bool tcp_client_is_lost (const tcp_client_t *client)
 Check if connection was lost.
 
socket_t tcp_client_get_socket (const tcp_client_t *client)
 Get current socket descriptor.
 
uint32_t tcp_client_get_id (const tcp_client_t *client)
 Get client ID assigned by server.
 
void tcp_client_signal_lost (tcp_client_t *client)
 Signal that connection was lost (triggers reconnection)
 
void tcp_client_close (tcp_client_t *client)
 Close connection gracefully.
 
void tcp_client_shutdown (tcp_client_t *client)
 Shutdown connection forcefully (for signal handlers)
 
int tcp_client_send_packet (tcp_client_t *client, packet_type_t type, const void *data, size_t len)
 Send packet with thread-safe mutex protection.
 
int tcp_client_send_ping (tcp_client_t *client)
 Send ping packet.
 
int tcp_client_send_pong (tcp_client_t *client)
 Send pong packet.
 
int tcp_client_connect (tcp_client_t *client, const char *address, int port, int reconnect_attempt, bool first_connection, bool has_ever_connected)
 Establish TCP connection to server.
 

Macro Definition Documentation

◆ MAX_RECONNECT_DELAY

#define MAX_RECONNECT_DELAY   (5LL * NS_PER_SEC_INT)

Maximum delay between reconnection attempts (nanoseconds)

Definition at line 57 of file lib/network/tcp/client.c.

Function Documentation

◆ tcp_client_close()

void tcp_client_close ( tcp_client_t *  client)

Close connection gracefully.

Parameters
clientTCP client instance

Definition at line 216 of file lib/network/tcp/client.c.

216 {
217 if (!client)
218 return;
219
220 log_debug("Closing client connection");
221
222 // Mark connection as inactive
223 atomic_store_bool(&client->connection_active, false);
224
225 // Close socket
226 if (socket_is_valid(client->sockfd)) {
227 close_socket_safe(client->sockfd);
228 client->sockfd = INVALID_SOCKET_VALUE;
229 }
230
231 // Reset client ID
232 client->my_client_id = 0;
233}
void atomic_store_bool(atomic_t *a, bool value)
Atomically store a boolean value.
Definition atomic.c:177
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:548
bool socket_is_valid(socket_t sock)
Check if a socket handle is valid.
#define INVALID_SOCKET_VALUE
Invalid socket value (POSIX: -1)
Definition socket.h:278

References atomic_store_bool(), INVALID_SOCKET_VALUE, log_debug, and socket_is_valid().

◆ tcp_client_connect()

int tcp_client_connect ( tcp_client_t *  client,
const char *  address,
int  port,
int  reconnect_attempt,
bool  first_connection,
bool  has_ever_connected 
)

Establish TCP connection to server.

Performs full connection lifecycle:

  • DNS resolution with IPv4/IPv6 dual-stack support
  • Socket creation and connection with timeout
  • Crypto handshake (if enabled)
  • Initial capability exchange
  • Client ID assignment from local port
Parameters
clientTCP client instance
addressServer hostname or IP address
portServer port number
reconnect_attemptCurrent reconnection attempt (0 for first, 1+ for retries)
first_connectionTrue if this is the very first connection since program start
has_ever_connectedTrue if client has successfully connected at least once
Returns
0 on success, negative on error

Definition at line 325 of file lib/network/tcp/client.c.

326 {
327 (void)first_connection; // Currently unused
328 (void)has_ever_connected; // Currently unused
329
330 if (!client || !address || port <= 0) {
331 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client, address, or port");
332 }
333
334 // Close any existing connection
335 if (socket_is_valid(client->sockfd)) {
336 close_socket_safe(client->sockfd);
337 client->sockfd = INVALID_SOCKET_VALUE;
338 }
339
340 // Apply reconnection delay if this is a retry
341 if (reconnect_attempt > 0) {
342 uint64_t delay_ns = get_reconnect_delay(reconnect_attempt);
343 platform_sleep_ns(delay_ns);
344 }
345
346 // Resolve server address using getaddrinfo() for IPv4/IPv6 support
347 // Special handling for localhost: ensure we try both IPv6 (::1) and IPv4 (127.0.0.1)
348 bool is_localhost = (strcmp(address, "localhost") == 0 || is_localhost_ipv4(address) || is_localhost_ipv6(address));
349
350 struct addrinfo hints, *res = NULL, *addr_iter;
351 memset(&hints, 0, sizeof(hints));
352 hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
353 hints.ai_socktype = SOCK_STREAM;
354 if (is_localhost) {
355 hints.ai_flags = AI_NUMERICSERV; // Optimize for localhost
356 }
357
358 char port_str[16];
359 SAFE_SNPRINTF(port_str, sizeof(port_str), "%d", port);
360
361 // For localhost, try IPv6 loopback (::1) first, then fall back to IPv4
362 if (is_localhost) {
363 log_debug("Localhost detected - trying IPv6 loopback [::1]:%s first...", port_str);
364 hints.ai_family = AF_INET6;
365 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
366
367 int ipv6_result = getaddrinfo("::1", port_str, &hints, &res);
368 if (ipv6_result == 0 && res != NULL) {
369 // Try IPv6 loopback connection
370 client->sockfd = socket_create("client_ipv6", res->ai_family, res->ai_socktype, res->ai_protocol);
371 if (client->sockfd != INVALID_SOCKET_VALUE) {
372 log_debug("Trying IPv6 loopback connection to [::1]:%s...", port_str);
373 if (connect_with_timeout(client->sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
374 log_debug("Connection successful using IPv6 loopback");
375 SAFE_STRNCPY(client->server_ip, "::1", sizeof(client->server_ip));
376 freeaddrinfo(res);
377 res = NULL; // Prevent double-free at connection_success label
378 goto connection_success;
379 }
380 close_socket_safe(client->sockfd);
381 client->sockfd = INVALID_SOCKET_VALUE;
382 }
383 freeaddrinfo(res);
384 res = NULL;
385 }
386
387 // IPv6 failed, try IPv4 loopback (127.0.0.1)
388 log_debug("IPv6 failed, trying IPv4 loopback 127.0.0.1:%s...", port_str);
389 hints.ai_family = AF_INET;
390
391 int ipv4_result = getaddrinfo("127.0.0.1", port_str, &hints, &res);
392 if (ipv4_result == 0 && res != NULL) {
393 client->sockfd = socket_create("client_ipv4", res->ai_family, res->ai_socktype, res->ai_protocol);
394 if (client->sockfd != INVALID_SOCKET_VALUE) {
395 log_debug("Trying IPv4 loopback connection to 127.0.0.1:%s...", port_str);
396 if (connect_with_timeout(client->sockfd, res->ai_addr, res->ai_addrlen, CONNECT_TIMEOUT)) {
397 log_debug("Connection successful using IPv4 loopback");
398 SAFE_STRNCPY(client->server_ip, "127.0.0.1", sizeof(client->server_ip));
399 freeaddrinfo(res);
400 res = NULL;
401 goto connection_success;
402 }
403 close_socket_safe(client->sockfd);
404 client->sockfd = INVALID_SOCKET_VALUE;
405 }
406 freeaddrinfo(res);
407 res = NULL;
408 }
409
410 // Both IPv6 and IPv4 loopback failed for localhost
411 log_warn("Could not connect to localhost using either IPv6 or IPv4 loopback");
412 return -1;
413 }
414
415 // For non-localhost addresses, use standard resolution
416 log_debug("Resolving server address '%s' port %s...", address, port_str);
417 hints.ai_family = AF_UNSPEC;
418 hints.ai_flags = 0;
419 int getaddr_result = getaddrinfo(address, port_str, &hints, &res);
420 if (getaddr_result != 0) {
421 log_error("Failed to resolve server address '%s': %s", address, gai_strerror(getaddr_result));
422 return -1;
423 }
424
425 // Try each address returned by getaddrinfo() - prefer IPv6, fall back to IPv4
426 for (int address_family = AF_INET6; address_family >= AF_INET; address_family -= (AF_INET6 - AF_INET)) {
427 for (addr_iter = res; addr_iter != NULL; addr_iter = addr_iter->ai_next) {
428 if (addr_iter->ai_family != address_family) {
429 continue;
430 }
431
432 const char *socket_name = (addr_iter->ai_family == AF_INET6) ? "client_server_ipv6" : "client_server_ipv4";
433 client->sockfd = socket_create(socket_name, addr_iter->ai_family, addr_iter->ai_socktype, addr_iter->ai_protocol);
434 if (client->sockfd == INVALID_SOCKET_VALUE) {
435 continue;
436 }
437
438 if (addr_iter->ai_family == AF_INET) {
439 log_debug("Trying IPv4 connection...");
440 } else if (addr_iter->ai_family == AF_INET6) {
441 log_debug("Trying IPv6 connection...");
442 }
443
444 if (connect_with_timeout(client->sockfd, addr_iter->ai_addr, addr_iter->ai_addrlen, CONNECT_TIMEOUT)) {
445 log_debug("Connection successful using %s", addr_iter->ai_family == AF_INET ? "IPv4"
446 : addr_iter->ai_family == AF_INET6 ? "IPv6"
447 : "unknown protocol");
448
449 // Extract server IP address for known_hosts
450 if (format_ip_address(addr_iter->ai_family, addr_iter->ai_addr, client->server_ip, sizeof(client->server_ip)) ==
451 ASCIICHAT_OK) {
452 log_debug("Resolved server IP: %s", client->server_ip);
453 } else {
454 log_warn("Failed to format server IP address");
455 }
456
457 goto connection_success;
458 }
459
460 close_socket_safe(client->sockfd);
461 client->sockfd = INVALID_SOCKET_VALUE;
462 }
463 }
464
465connection_success:
466
467 if (res) {
468 freeaddrinfo(res);
469 }
470
471 // If we exhausted all addresses without success, fail
472 if (client->sockfd == INVALID_SOCKET_VALUE) {
473 log_warn("Could not connect to server %s:%d (tried all addresses)", address, port);
474 return -1;
475 }
476
477 // Extract local port for client ID
478 struct sockaddr_storage local_addr = {0};
479 socklen_t addr_len = sizeof(local_addr);
480 if (getsockname(client->sockfd, (struct sockaddr *)&local_addr, &addr_len) == -1) {
481 log_error("Failed to get local socket address: %s", network_error_string());
482 close_socket_safe(client->sockfd);
483 client->sockfd = INVALID_SOCKET_VALUE;
484 return -1;
485 }
486
487 // Extract port from either IPv4 or IPv6 address
488 int local_port = 0;
489 if (((struct sockaddr *)&local_addr)->sa_family == AF_INET) {
490 local_port = NET_TO_HOST_U16(((struct sockaddr_in *)&local_addr)->sin_port);
491 } else if (((struct sockaddr *)&local_addr)->sa_family == AF_INET6) {
492 local_port = NET_TO_HOST_U16(((struct sockaddr_in6 *)&local_addr)->sin6_port);
493 }
494 client->my_client_id = (uint32_t)local_port;
495
496 // Update registration name now that we have a client ID
497 {
498 char client_name[64];
499 SAFE_SNPRINTF(client_name, sizeof(client_name), "tcp_client_%u", client->my_client_id);
500 named_update_name((uintptr_t)client, client_name);
501 }
502
503 // Mark connection as active
504 atomic_store_bool(&client->connection_active, true);
505 atomic_store_bool(&client->connection_lost, false);
506 atomic_store_bool(&client->should_reconnect, false);
507
508 // Initialize crypto (application must set crypto_initialized flag)
509 // This is done outside this function by calling client_crypto_init()
510
511 // Configure socket options
512 if (socket_set_keepalive(client->sockfd, true) < 0) {
513 log_warn("Failed to set socket keepalive: %s", network_error_string());
514 }
515
516 asciichat_error_t sock_config_result = socket_configure_buffers(client->sockfd);
517 if (sock_config_result != ASCIICHAT_OK) {
518 log_warn("Failed to configure socket: %s", network_error_string());
519 }
520
521 log_debug("Connection established successfully to %s:%d (client_id=%u)", address, port, client->my_client_id);
522 return 0;
523}
#define NET_TO_HOST_U16(val)
Definition endian.h:111
unsigned int uint32_t
Definition common.h:58
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:414
#define SAFE_SNPRINTF(buffer, buffer_size,...)
Definition common.h:492
unsigned long long uint64_t
Definition common.h:59
const char * named_update_name(uintptr_t key, const char *new_base_name)
Update the registered name for a resource with a new base name.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it, returning the error code.
asciichat_error_t
Error and exit codes - unified status values (0-255)
Definition error_codes.h:49
@ ASCIICHAT_OK
Definition error_codes.h:51
@ ERROR_INVALID_PARAM
#define log_warn(...)
Log a WARN message.
Definition log/log.h:574
#define log_error(...)
Log an ERROR message.
Definition log/log.h:587
#define CONNECT_TIMEOUT
Connection timeout in seconds (3 seconds)
bool connect_with_timeout(socket_t sockfd, const struct sockaddr *addr, socklen_t addrlen, int timeout_seconds)
Connect with timeout.
const char * network_error_string()
Get human-readable error string for network errors.
asciichat_error_t socket_configure_buffers(socket_t sockfd)
Configure socket buffers and TCP_NODELAY for optimal performance.
void platform_sleep_ns(uint64_t ns)
Platform-safe sleep function with nanosecond precision.
int socket_set_keepalive(socket_t sock, bool keepalive)
Set SO_KEEPALIVE socket option.
socket_t socket_create(const char *name, int domain, int type, int protocol)
Create a new named socket.
int is_localhost_ipv6(const char *ip)
Check if IPv6 address is localhost.
Definition ip.c:1320
int is_localhost_ipv4(const char *ip)
Check if IPv4 address is localhost.
Definition ip.c:1299
asciichat_error_t format_ip_address(int family, const struct sockaddr *addr, char *output, size_t output_size)
Format IP address from socket address structure.
Definition ip.c:196

References ASCIICHAT_OK, atomic_store_bool(), CONNECT_TIMEOUT, connect_with_timeout(), ERROR_INVALID_PARAM, format_ip_address(), INVALID_SOCKET_VALUE, is_localhost_ipv4(), is_localhost_ipv6(), log_debug, log_error, log_warn, named_update_name(), NET_TO_HOST_U16, network_error_string(), platform_sleep_ns(), SAFE_SNPRINTF, SAFE_STRNCPY, SET_ERRNO, socket_configure_buffers(), socket_create(), socket_is_valid(), and socket_set_keepalive().

Referenced by connection_attempt_tcp().

◆ tcp_client_create()

tcp_client_t * tcp_client_create ( void  )

Create and initialize TCP client.

Create and initialize a TCP client instance.

Allocates tcp_client_t and initializes all fields to safe defaults.

Definition at line 96 of file lib/network/tcp/client.c.

96 {
98 if (!client) {
99 log_error("Failed to allocate tcp_client_t");
100 return NULL;
101 }
102
103 memset(client, 0, sizeof(*client));
104
105 client->sockfd = INVALID_SOCKET_VALUE;
106 atomic_store_bool(&client->connection_active, false);
107 atomic_store_bool(&client->connection_lost, false);
108 atomic_store_bool(&client->should_reconnect, false);
109 client->my_client_id = 0;
110 memset(client->server_ip, 0, sizeof(client->server_ip));
111 client->encryption_enabled = false;
112
113 if (mutex_init(&client->send_mutex, "client_send") != 0) {
114 log_error("Failed to initialize send mutex");
115 SAFE_FREE(client);
116 return NULL;
117 }
118
119 NAMED_REGISTER_CLIENT(client, "client", NULL);
120
121 // Register atomic fields for debug tracking - parent_key handles hierarchical naming
122 NAMED_REGISTER_ATOMIC(&client->connection_active, "is_connection_active", (uintptr_t)(const void *)(client));
123 NAMED_REGISTER_ATOMIC(&client->connection_lost, "has_connection_been_lost", (uintptr_t)(const void *)(client));
124 NAMED_REGISTER_ATOMIC(&client->should_reconnect, "should_attempt_reconnection", (uintptr_t)(const void *)(client));
125
126 log_debug("TCP client created successfully");
127 return client;
128}
void tcp_client_t
#define SAFE_FREE(ptr)
Definition common.h:376
#define SAFE_MALLOC(size, cast)
Definition common.h:264
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.
#define NAMED_REGISTER_CLIENT(client, name, parent_ptr)
Register a client connection with automatic format specifier.
int mutex_init(mutex_t *mutex, const char *name)
Initialize a mutex with a name.
Definition threading.c:16

References atomic_store_bool(), INVALID_SOCKET_VALUE, log_debug, log_error, mutex_init(), NAMED_REGISTER_ATOMIC, NAMED_REGISTER_CLIENT, SAFE_FREE, and SAFE_MALLOC.

Referenced by connection_attempt_tcp().

◆ tcp_client_destroy()

void tcp_client_destroy ( tcp_client_t **  client_ptr)

Destroy TCP client and free resources.

Destroy TCP client and free all resources.

Must be called AFTER all threads have been joined.

Definition at line 135 of file lib/network/tcp/client.c.

135 {
136 if (!client_ptr || !*client_ptr) {
137 return;
138 }
139
140 tcp_client_t *client = *client_ptr;
141
142 if (socket_is_valid(client->sockfd)) {
143 close_socket_safe(client->sockfd);
144 client->sockfd = INVALID_SOCKET_VALUE;
145 }
146
147 // Unregister atomic fields
148 NAMED_UNREGISTER(&client->connection_active);
149 NAMED_UNREGISTER(&client->connection_lost);
150 NAMED_UNREGISTER(&client->should_reconnect);
151
152 NAMED_UNREGISTER(client);
153 mutex_destroy(&client->send_mutex);
154 SAFE_FREE(*client_ptr);
155
156 log_debug("TCP client destroyed");
157}
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.
int mutex_destroy(mutex_t *mutex)
Destroy a mutex.
Definition threading.c:22

References INVALID_SOCKET_VALUE, log_debug, mutex_destroy(), NAMED_UNREGISTER, SAFE_FREE, and socket_is_valid().

Referenced by connection_attempt_tcp().

◆ tcp_client_get_id()

uint32_t tcp_client_get_id ( const tcp_client_t *  client)

Get client ID assigned by server.

Parameters
clientTCP client instance
Returns
Client ID (from local port) or 0 if not connected

Definition at line 191 of file lib/network/tcp/client.c.

191 {
192 return client ? client->my_client_id : 0;
193}

◆ tcp_client_get_socket()

socket_t tcp_client_get_socket ( const tcp_client_t *  client)

Get current socket descriptor.

Parameters
clientTCP client instance
Returns
Socket descriptor or INVALID_SOCKET_VALUE if not connected

Definition at line 184 of file lib/network/tcp/client.c.

184 {
185 return client ? client->sockfd : INVALID_SOCKET_VALUE;
186}

References INVALID_SOCKET_VALUE.

Referenced by connection_attempt_tcp().

◆ tcp_client_is_active()

bool tcp_client_is_active ( const tcp_client_t *  client)

Check if connection is currently active.

Parameters
clientTCP client instance
Returns
true if connection is active, false otherwise

Definition at line 166 of file lib/network/tcp/client.c.

166 {
167 if (!client)
168 return false;
169 return atomic_load_bool(&client->connection_active);
170}
bool atomic_load_bool(atomic_t *a)
Atomically load a boolean value.
Definition atomic.c:169

References atomic_load_bool().

◆ tcp_client_is_lost()

bool tcp_client_is_lost ( const tcp_client_t *  client)

Check if connection was lost.

Parameters
clientTCP client instance
Returns
true if connection loss was detected, false otherwise

Definition at line 175 of file lib/network/tcp/client.c.

175 {
176 if (!client)
177 return false;
178 return atomic_load_bool(&client->connection_lost);
179}

References atomic_load_bool().

◆ tcp_client_send_packet()

int tcp_client_send_packet ( tcp_client_t *  client,
packet_type_t  type,
const void *  data,
size_t  len 
)

Send packet with thread-safe mutex protection.

All packet transmission goes through this function to ensure packets aren't interleaved on the wire.

Definition at line 260 of file lib/network/tcp/client.c.

260 {
261 if (!client) {
262 return SET_ERRNO(ERROR_INVALID_PARAM, "NULL client");
263 }
264
265 if (!atomic_load_bool(&client->connection_active)) {
266 return SET_ERRNO(ERROR_NETWORK, "Connection not active");
267 }
268
269 // Acquire send mutex for thread-safe transmission
270 mutex_lock(&client->send_mutex);
271
272 // Send packet without encryption (crypto is handled at app_client layer)
273 asciichat_error_t result = (asciichat_error_t)send_packet(client->sockfd, type, data, len);
274
275 mutex_unlock(&client->send_mutex);
276
277 if (result != ASCIICHAT_OK) {
278 log_debug("Failed to send packet type %d: %s", type, asciichat_error_string(result));
279 return -1;
280 }
281
282 return 0;
283}
@ ERROR_NETWORK
Definition error_codes.h:77
int send_packet(socket_t sockfd, packet_type_t type, const void *data, size_t len)
Send a basic packet without encryption.
Definition packet.c:784
#define mutex_lock(mutex)
Lock a mutex (with debug tracking in debug builds)
#define mutex_unlock(mutex)
Unlock a mutex (with debug tracking in debug builds)

References ASCIICHAT_OK, atomic_load_bool(), ERROR_INVALID_PARAM, ERROR_NETWORK, log_debug, mutex_lock, mutex_unlock, send_packet(), and SET_ERRNO.

Referenced by tcp_client_send_ping(), and tcp_client_send_pong().

◆ tcp_client_send_ping()

int tcp_client_send_ping ( tcp_client_t *  client)

Send ping packet.

Parameters
clientTCP client instance
Returns
0 on success, negative on error

Definition at line 288 of file lib/network/tcp/client.c.

288 {
289 if (!client)
290 return -1;
291 return tcp_client_send_packet(client, PACKET_TYPE_PING, NULL, 0);
292}
@ PACKET_TYPE_PING
Keepalive ping packet.
Definition packet.h:383
int tcp_client_send_packet(tcp_client_t *client, packet_type_t type, const void *data, size_t len)
Send packet with thread-safe mutex protection.

References PACKET_TYPE_PING, and tcp_client_send_packet().

◆ tcp_client_send_pong()

int tcp_client_send_pong ( tcp_client_t *  client)

Send pong packet.

Parameters
clientTCP client instance
Returns
0 on success, negative on error

Definition at line 297 of file lib/network/tcp/client.c.

297 {
298 if (!client)
299 return -1;
300 return tcp_client_send_packet(client, PACKET_TYPE_PONG, NULL, 0);
301}
@ PACKET_TYPE_PONG
Keepalive pong response.
Definition packet.h:385

References PACKET_TYPE_PONG, and tcp_client_send_packet().

◆ tcp_client_shutdown()

void tcp_client_shutdown ( tcp_client_t *  client)

Shutdown connection forcefully (for signal handlers)

Parameters
clientTCP client instance

Definition at line 238 of file lib/network/tcp/client.c.

238 {
239 if (!client)
240 return;
241
242 atomic_store_bool(&client->connection_active, false);
243
244 // Shutdown socket for reading/writing to interrupt blocking calls
245 if (socket_is_valid(client->sockfd)) {
246 socket_shutdown(client->sockfd, SHUT_RDWR);
247 }
248}
int socket_shutdown(socket_t sock, int how)
Shutdown socket I/O.

References atomic_store_bool(), socket_is_valid(), and socket_shutdown().

◆ tcp_client_signal_lost()

void tcp_client_signal_lost ( tcp_client_t *  client)

Signal that connection was lost (triggers reconnection)

Parameters
clientTCP client instance

Definition at line 202 of file lib/network/tcp/client.c.

202 {
203 if (!client)
204 return;
205
206 if (!atomic_load_bool(&client->connection_lost)) {
207 atomic_store_bool(&client->connection_lost, true);
208 atomic_store_bool(&client->connection_active, false);
209 log_info("Connection lost signaled");
210 }
211}
#define log_info(...)
Log an INFO message.
Definition log/log.h:561

References atomic_load_bool(), atomic_store_bool(), and log_info.