ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
client.h File Reference

Per-client state management and lifecycle orchestration. More...

Go to the source code of this file.

Data Structures

struct  client_manager_t
 Global client manager structure for server-side client coordination. More...

Typedefs

typedef struct server_context_t server_context_t

Functions

client_info_t * add_transport_client (server_context_t *server_ctx, acip_transport_t *transport, const char *client_ip)
int start_transport_client_threads (server_context_t *server_ctx, const char *client_id)
int remove_client (server_context_t *server_ctx, const char *client_id)
client_info_t * find_client_by_id (const char *client_id)
 Fast O(1) client lookup by ID using hash table.
void cleanup_client_media_buffers (client_info_t *client)
void cleanup_client_packet_queues (client_info_t *client)
void initialize_client_info (client_info_t *client)

Variables

client_manager_t g_client_manager
 Hash table entry for client packet dispatch.
rwlock_t g_client_manager_rwlock
 Reader-writer lock protecting the global client manager.
bool g_client_manager_rwlock_initialized

Detailed Description

Per-client state management and lifecycle orchestration.

This header provides server-specific client management functions. The client_info_t structure and network logging macros are defined in lib/network/client.h.

Definition in file src/server/client.h.

Typedef Documentation

◆ server_context_t

typedef struct server_context_t server_context_t

Definition at line 17 of file src/server/client.h.

Function Documentation

◆ add_transport_client()

client_info_t * add_transport_client ( server_context_t * server_ctx,
acip_transport_t * transport,
const char * client_ip )

Definition at line 412 of file src/server/client.c.

412 {
413 if (!server_ctx || !transport || !client_ip) {
414 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid parameters");
415 return NULL;
416 }
417
419
420 // Find empty slot - this is the authoritative check
421 int slot = -1;
422 int existing_count = 0;
423 for (int i = 0; i < MAX_CLIENTS; i++) {
424 if (slot == -1 && g_client_manager.clients[i].client_id[0] == '\0') {
425 slot = i; // Take first available slot
426 }
427 // Count only active clients
428 if (g_client_manager.clients[i].client_id[0] != '\0' && atomic_load_bool(&g_client_manager.clients[i].active)) {
429 existing_count++;
430 }
431 }
432
433 // Check if we've hit the configured max-clients limit (not the array size)
434 if (existing_count >= GET_OPTION(max_clients)) {
436 SET_ERRNO(ERROR_RESOURCE_EXHAUSTED, "Maximum client limit reached (%d/%d active clients)", existing_count,
437 GET_OPTION(max_clients));
438 log_error("Maximum client limit reached (%d/%d active clients)", existing_count, GET_OPTION(max_clients));
439 return NULL;
440 }
441
442 if (slot == -1) {
444 SET_ERRNO(ERROR_RESOURCE_EXHAUSTED, "No available client slots (all %d array slots are in use)", MAX_CLIENTS);
445 log_error("No available client slots (all %d array slots are in use)", MAX_CLIENTS);
446 return NULL;
447 }
448
449 // Update client_count to match actual count before adding new client
450 g_client_manager.client_count = existing_count;
451
452 // Generate unique client ID for transport client (noun only, without counter or port)
453 char new_client_id[MAX_CLIENT_ID_LEN];
454 if (generate_client_id(new_client_id, sizeof(new_client_id)) != 0) {
456 log_error("Failed to generate unique client ID for transport client");
457 return NULL;
458 }
459
460 // Initialize client
461 client_info_t *client = &g_client_manager.clients[slot];
462
463 server_client_initialize_transport_slot(server_ctx, client, transport, new_client_id, client_ip, slot);
464
465 if (server_client_allocate_transport_buffers(client, new_client_id) != ASCIICHAT_OK ||
466 server_client_initialize_transport_sync(client, new_client_id) != ASCIICHAT_OK) {
467 goto error_cleanup_transport;
468 }
469
470 g_client_manager.client_count = existing_count + 1; // We just added a client
471 log_debug("Client count updated: now %d clients (added transport client_id=%s to slot %d)",
472 g_client_manager.client_count, new_client_id, slot);
473
474 // Add client to uthash table for O(1) lookup
475 HASH_ADD_STR(g_client_manager.clients_by_id, client_id, client);
476 log_debug("Added transport client %s to uthash table", new_client_id);
477
478 // Release the write lock IMMEDIATELY after adding to hash table
479 // The client is fully initialized before other threads can look it up.
481
482 // Register client with session_host (for discovery mode support)
483 // transport clients use INVALID_SOCKET_VALUE since they don't have a TCP socket
484 if (server_ctx->session_host) {
485 client->session_client_id =
486 session_host_add_client(server_ctx->session_host, NULL, ACIP_TRANSPORT_BORROWED, client_ip, 0);
487 if (client->session_client_id == 0) {
488 log_warn("Failed to register transport client %s with session_host", new_client_id);
489 } else {
490 log_debug("transport client %s registered with session_host as %u", new_client_id, client->session_client_id);
491 }
492 }
493
494 // Broadcast server state to ALL clients AFTER the new client is fully set up
495 // This notifies all clients (including the new one) about the updated grid
497
498 return client;
499
500error_cleanup_transport:
501 // Clean up all partially allocated resources for transport client
502 // NOTE: This label is reached when allocation or initialization fails BEFORE
503 // the client is added to the hash table. Don't call remove_client() here.
504 cleanup_client_all_buffers(client);
505 memset(client, 0, sizeof(*client));
507 return NULL;
508}
#define SET_ERRNO(code,...)
@ ERROR_RESOURCE_EXHAUSTED
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
#define MAX_CLIENTS
Maximum possible clients (static array size) - actual runtime limit set by –max-clients (1-128).
#define MAX_CLIENT_ID_LEN
Maximum client ID length - format: "noun.N (transport:port)" e.g., "mountain.0 (tcp:48036)".
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_error(...)
Log an ERROR message.
Definition log/log.h:634
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define GET_OPTION(field)
Safely get a specific option field (lock-free read).
Definition options.h:1336
#define rwlock_wrunlock(lock)
Release a write lock (with debug tracking in debug builds).
Definition rwlock.h:357
#define rwlock_wrlock(lock)
Acquire a write lock (with debug tracking in debug builds).
Definition rwlock.h:319
struct client_info client_info_t
uint32_t session_host_add_client(session_host_t *host, acip_transport_t *transport, acip_transport_ownership_t ownership, const char *ip, int port)
Definition host.c:1129
int generate_client_id(char *buffer, size_t buffer_size)
Generate a unique client ID (noun only, no counter or port).
Definition nouns.c:780
rwlock_t g_client_manager_rwlock
Reader-writer lock protecting the global client manager.
client_manager_t g_client_manager
Hash table entry for client packet dispatch.
void broadcast_server_state_to_all_clients(void)
Notify all clients of state changes.
session_host_t * session_host
Session host for discovery mode support.

References ACIP_TRANSPORT_BORROWED, ASCIICHAT_OK, broadcast_server_state_to_all_clients(), ERROR_INVALID_PARAM, ERROR_RESOURCE_EXHAUSTED, g_client_manager, g_client_manager_rwlock, generate_client_id(), GET_OPTION, log_debug, log_error, log_warn, MAX_CLIENT_ID_LEN, MAX_CLIENTS, rwlock_wrlock, rwlock_wrunlock, client_info::session_client_id, server_context_t::session_host, session_host_add_client(), and SET_ERRNO.

◆ cleanup_client_media_buffers()

void cleanup_client_media_buffers ( client_info_t * client)

Definition at line 804 of file src/server/client.c.

804 {
805 if (!client) {
806 SET_ERRNO(ERROR_INVALID_PARAM, "Client is NULL");
807 return;
808 }
809
810 if (client->digital_rain) {
812 client->digital_rain = NULL;
813 }
814
815 if (client->incoming_video_buffer) {
817 client->incoming_video_buffer = NULL;
818 }
819
820 if (client->incoming_audio_buffer) {
822 client->incoming_audio_buffer = NULL;
823 }
824
825 // Clean up Opus decoder
826 if (client->opus_decoder) {
828 client->opus_decoder = NULL;
829 }
830
831 // Clean up video decoder (persistent per-client decoder)
832 if (client->video_decoder_ctx) {
833 avcodec_free_context((AVCodecContext **)&client->video_decoder_ctx);
834 client->video_decoder_ctx = NULL;
835 }
836 if (client->video_decode_frame) {
837 av_frame_free((AVFrame **)&client->video_decode_frame);
838 client->video_decode_frame = NULL;
839 }
840 if (client->video_rgb_frame) {
841 av_frame_free((AVFrame **)&client->video_rgb_frame);
842 client->video_rgb_frame = NULL;
843 }
844 if (client->video_sws_ctx) {
845 sws_freeContext(client->video_sws_ctx);
846 client->video_sws_ctx = NULL;
847 }
848}
void audio_ring_buffer_destroy(audio_ring_buffer_t *rb)
Destroy an audio ring buffer.
void opus_codec_destroy(opus_codec_t *codec)
Destroy an Opus codec instance.
Definition opus.c:280
void video_frame_buffer_destroy(video_frame_buffer_t *vfb)
Destroy frame buffer and free all resources.
Definition video_frame.c:98
void digital_rain_destroy(digital_rain_t *rain)
Destroy digital rain effect context.
video_frame_buffer_t * incoming_video_buffer
audio_ring_buffer_t * incoming_audio_buffer
State for one Opus encoding or decoding direction.
Definition opus.h:105

References audio_ring_buffer_destroy(), client_info::digital_rain, digital_rain_destroy(), ERROR_INVALID_PARAM, client_info::incoming_audio_buffer, client_info::incoming_video_buffer, opus_codec_destroy(), client_info::opus_decoder, SET_ERRNO, client_info::video_decode_frame, client_info::video_decoder_ctx, video_frame_buffer_destroy(), client_info::video_rgb_frame, and client_info::video_sws_ctx.

◆ cleanup_client_packet_queues()

void cleanup_client_packet_queues ( client_info_t * client)

Definition at line 850 of file src/server/client.c.

850 {
851 if (!client)
852 return;
853
854 // Async dispatch: clean up received packet queue
855
856 // Video now uses double buffer, cleaned up in cleanup_client_media_buffers
857}

◆ find_client_by_id()

client_info_t * find_client_by_id ( const char * client_id)

Fast O(1) client lookup by ID using hash table.

This is the primary method for locating clients throughout the server. It uses a hash table for constant-time lookups regardless of client count, making it suitable for high-performance operations like rendering and stats.

PERFORMANCE CHARACTERISTICS:

  • Time Complexity: O(1) average case, O(n) worst case (hash collision)
  • Space Complexity: O(1)
  • Thread Safety: Hash table is internally thread-safe for lookups

USAGE PATTERNS:

  • Called by render threads to find target clients for frame generation
  • Used by protocol handlers to locate clients for packet processing
  • Stats collection for per-client performance monitoring
Parameters
client_idUnique identifier for the client (0 is invalid)
Returns
Pointer to client_info_t if found, NULL if not found or invalid ID
Note
Does not require external locking - hash table provides thread safety
Returns direct pointer to client struct - caller should use snapshot pattern

Definition at line 272 of file src/server/client.c.

272 {
273 if (!client_id || client_id[0] == '\0') {
274 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid client ID");
275 return NULL;
276 }
277
278 // Protect uthash lookup with read lock to prevent concurrent access issues
280
281 client_info_t *result = NULL;
282 HASH_FIND_STR(g_client_manager.clients_by_id, client_id, result);
283
285
286 if (!result) {
287 log_warn("Client not found for ID %s", client_id);
288 }
289
290 return result;
291}
#define rwlock_rdlock(lock)
Acquire a read lock (with debug tracking in debug builds).
Definition rwlock.h:299
#define rwlock_rdunlock(lock)
Release a read lock (with debug tracking in debug builds).
Definition rwlock.h:338

References ERROR_INVALID_PARAM, g_client_manager, g_client_manager_rwlock, log_warn, rwlock_rdlock, rwlock_rdunlock, and SET_ERRNO.

Referenced by start_transport_client_threads().

◆ initialize_client_info()

void initialize_client_info ( client_info_t * client)

◆ remove_client()

int remove_client ( server_context_t * server_ctx,
const char * client_id )

Definition at line 708 of file src/server/client.c.

708 {
709 if (!server_ctx) {
710 SET_ERRNO(ERROR_INVALID_PARAM, "Cannot remove client %s: NULL server_ctx", client_id);
711 return -1;
712 }
713
714 server_client_removal_t removal = {0};
715 if (server_client_begin_removal(server_ctx, client_id, &removal) != ASCIICHAT_OK) {
716 return -1;
717 }
718 if (!removal.client) {
719 return 0; // Another thread is already removing this client.
720 }
721
723 return -1;
724 if (!server_client_finish_removal(client_id, &removal)) {
725 return 0;
726 }
727
729 return 0;
730}
asciichat_error_t stop_client_render_threads(client_info_t *client)
Stop and cleanup per-client rendering threads.

References ASCIICHAT_OK, broadcast_server_state_to_all_clients(), server_client_removal_t::client, ERROR_INVALID_PARAM, SET_ERRNO, and stop_client_render_threads().

◆ start_transport_client_threads()

int start_transport_client_threads ( server_context_t * server_ctx,
const char * client_id )

Definition at line 788 of file src/server/client.c.

788 {
789 if (!server_ctx) {
790 SET_ERRNO(ERROR_INVALID_PARAM, "Server context is NULL");
791 return -1;
792 }
793
794 client_info_t *client = find_client_by_id(client_id);
795 if (!client) {
796 SET_ERRNO(ERROR_NOT_FOUND, "Client %s not found", client_id);
797 return -1;
798 }
799
800 log_debug("Starting application workers for client %s...", client_id);
801 return start_client_threads(server_ctx, client);
802}
@ ERROR_NOT_FOUND
client_info_t * find_client_by_id(const char *client_id)
Fast O(1) client lookup by ID using hash table.

References ERROR_INVALID_PARAM, ERROR_NOT_FOUND, find_client_by_id(), log_debug, and SET_ERRNO.

Variable Documentation

◆ g_client_manager

client_manager_t g_client_manager
extern

Hash table entry for client packet dispatch.

Global client manager singleton - central coordination point

This is the primary data structure for managing all connected clients. It serves as the bridge between main.c's connection accept loop and the per-client threading architecture.

STRUCTURE COMPONENTS:

  • clients[]: Array backing storage for client_info_t structs
  • client_hashtable: O(1) lookup table for client_id -> client_info_t*
  • client_count: Current number of active clients
  • mutex: Legacy mutex (mostly replaced by rwlock)
  • next_client_id: Monotonic counter for unique client identification

THREAD SAFETY: Protected by g_client_manager_rwlock for concurrent access

Definition at line 193 of file src/server/client.c.

◆ g_client_manager_rwlock

rwlock_t g_client_manager_rwlock
extern

Reader-writer lock protecting the global client manager.

This lock enables high-performance concurrent access patterns:

  • Multiple threads can read client data simultaneously (stats, rendering)
  • Only one thread can modify client data at a time (add/remove operations)
  • Eliminates contention between read-heavy operations

USAGE PATTERN:

Definition at line 208 of file src/server/client.c.

208{0};

◆ g_client_manager_rwlock_initialized

bool g_client_manager_rwlock_initialized
extern

Track whether g_client_manager_rwlock was successfully initialized.

Definition at line 211 of file src/server/client.c.

Referenced by broadcast_server_state_to_all_clients().