From 8108a2bb0f5a5d83ebdf142eb7779573ef80a95c Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Mon, 21 Apr 2025 19:26:36 -0500 Subject: [PATCH] Replaced tight loops in server/client with mutexes --- src/client/networked/networkedclient.c | 47 ++++++++++----- src/client/networked/networkedclient.h | 2 + src/server/networked/networkedserver.c | 63 ++++++++++++-------- src/server/networked/networkedserver.h | 2 + src/server/networked/networkedserverclient.c | 51 ++++++++++------ src/server/networked/networkedserverclient.h | 2 + src/server/server.c | 2 +- 7 files changed, 113 insertions(+), 56 deletions(-) diff --git a/src/client/networked/networkedclient.c b/src/client/networked/networkedclient.c index 37bc917..790e964 100644 --- a/src/client/networked/networkedclient.c +++ b/src/client/networked/networkedclient.c @@ -63,6 +63,10 @@ errorret_t networkedClientConnect( } } + // Initialize mutex and condition variable + pthread_mutex_init(&client->networked.lock, NULL); + pthread_cond_init(&client->networked.cond, NULL); + // Send the version { const char_t *message = "DUSK|"DUSK_VERSION; @@ -119,29 +123,37 @@ void networkedClientDisconnect(client_t *client) { assertTrue(client->type == CLIENT_TYPE_NETWORKED, "Client is not networked"); assertTrue(client->state == CLIENT_STATE_CONNECTED, "Client not connected"); + pthread_mutex_lock(&client->networked.lock); client->state = CLIENT_STATE_DISCONNECTING; - int32_t maxAttempts = 0; - while(client->state == CLIENT_STATE_DISCONNECTING) { - usleep(100 * 1000);// Sleep for 100ms - maxAttempts++; - if(maxAttempts > 5) { + struct timespec timeout; + clock_gettime(CLOCK_REALTIME, &timeout); + timeout.tv_sec += 1; // Wait for up to 1 second + + while (client->state == CLIENT_STATE_DISCONNECTING) { + if (pthread_cond_timedwait(&client->networked.cond, &client->networked.lock, &timeout) == ETIMEDOUT) { consolePrint("Client disconnect timed out, force closing"); break; } } + pthread_mutex_unlock(&client->networked.lock); client->state = CLIENT_STATE_DISCONNECTED; - if(client->networked.thread) { + + if (client->networked.thread) { pthread_join(client->networked.thread, NULL); client->networked.thread = 0; } - if(client->networked.socket) { + if (client->networked.socket) { shutdown(client->networked.socket, SHUT_RDWR); close(client->networked.socket); client->networked.socket = 0; } + + // Destroy mutex and condition variable + pthread_mutex_destroy(&client->networked.lock); + pthread_cond_destroy(&client->networked.cond); } errorret_t networkedClientWrite( @@ -250,17 +262,24 @@ void * networkedClientThread(void *arg) { "Client thread argument is not connecting" ); + pthread_mutex_lock(&client->networked.lock); client->state = CLIENT_STATE_CONNECTED; + pthread_cond_signal(&client->networked.cond); // Notify the main thread + pthread_mutex_unlock(&client->networked.lock); + // Main loop while(client->state == CLIENT_STATE_CONNECTED) { - usleep(1000*1000);// Sleep for 1 second - - // packetPingCreate(&packet); - // packetQueuePushOutbound( - // &client->packetQueue, - // &packet - // ); + // errorret_t err = networkedClientReadPacket(client, &packet); + // if(err) { + // consolePrint("Error reading packet: %s", err.message); + // break; + // } } client->state = CLIENT_STATE_DISCONNECTED; + + pthread_mutex_lock(&client->networked.lock); + client->state = CLIENT_STATE_DISCONNECTED; + pthread_cond_signal(&client->networked.cond); // Notify the main thread + pthread_mutex_unlock(&client->networked.lock); } \ No newline at end of file diff --git a/src/client/networked/networkedclient.h b/src/client/networked/networkedclient.h index 2414f8a..761383d 100644 --- a/src/client/networked/networkedclient.h +++ b/src/client/networked/networkedclient.h @@ -21,6 +21,8 @@ typedef struct { int32_t socket; struct sockaddr_in address; pthread_t thread; + pthread_mutex_t lock; + pthread_cond_t cond; } networkedclient_t; /** diff --git a/src/server/networked/networkedserver.c b/src/server/networked/networkedserver.c index f75861f..8ea1bb5 100644 --- a/src/server/networked/networkedserver.c +++ b/src/server/networked/networkedserver.c @@ -50,6 +50,10 @@ errorret_t networkedServerStart( return error("Failed to listen on socket"); } + // Initialize mutex and condition variable + pthread_mutex_init(&server->networked.lock, NULL); + pthread_cond_init(&server->networked.cond, NULL); + // Start the server thread. server->state = SERVER_STATE_STARTING; res = pthread_create(&server->thread, NULL, networkedServerThread, server); @@ -60,9 +64,11 @@ errorret_t networkedServerStart( } // Wait for the thread to start. + pthread_mutex_lock(&server->networked.lock); while(server->state == SERVER_STATE_STARTING) { - usleep(1000); + pthread_cond_wait(&server->networked.cond, &server->networked.lock); } + pthread_mutex_unlock(&server->networked.lock); // Server started, hand back. consolePrint("Server started."); @@ -75,8 +81,11 @@ void networkedServerStop(server_t *server) { assertTrue(server->state != SERVER_STATE_STARTING, "Server is starting"); assertIsMainThread("Server stop must be on main thread"); - // Tell thread we want it to stop + // Notify thread to stop + pthread_mutex_lock(&server->networked.lock); server->state = SERVER_STATE_STOPPING; + pthread_cond_signal(&server->networked.cond); + pthread_mutex_unlock(&server->networked.lock); // Disconnect clients uint8_t i = 0; @@ -86,17 +95,22 @@ void networkedServerStop(server_t *server) { serverClientClose(client); } while(i < SERVER_MAX_CLIENTS); - // Now we wait a short time for the thread to stop. + // Wait for the thread to stop int32_t maxWaits = 0; - while(SERVER.state == SERVER_STATE_STOPPING) { - usleep(1000); - maxWaits++; - if(maxWaits < 1000) continue; - - consolePrint("Server thread did not stop in time, forcing exit."); - SERVER.state = SERVER_STATE_STOPPED; - break; + pthread_mutex_lock(&server->networked.lock); + while(server->state == SERVER_STATE_STOPPING) { + if(maxWaits++ >= 1000) { + consolePrint("Server thread did not stop in time, forcing exit."); + server->state = SERVER_STATE_STOPPED; + break; + } + pthread_cond_wait(&server->networked.cond, &server->networked.lock); } + pthread_mutex_unlock(&server->networked.lock); + + // Destroy mutex and condition variable + pthread_mutex_destroy(&server->networked.lock); + pthread_cond_destroy(&server->networked.cond); // Close the socket if(server->networked.socket >= 0) { @@ -116,15 +130,18 @@ void * networkedServerThread(void *arg) { assertNotMainThread("Server thread must not be main thread"); server_t *server = (server_t *)arg; + + // Notify main thread that the server is running + pthread_mutex_lock(&server->networked.lock); + server->state = SERVER_STATE_RUNNING; + pthread_cond_signal(&server->networked.cond); + pthread_mutex_unlock(&server->networked.lock); + struct timeval timeout; fd_set readfds; - server->state = SERVER_STATE_RUNNING; // Main thread loop. while(server->state == SERVER_STATE_RUNNING) { - // Wait a tiny bit to avoid large CPU usage. - usleep(1000); - // Prepare the select call, used for waiting for incoming connections. FD_ZERO(&readfds); FD_SET(server->networked.socket, &readfds); @@ -140,14 +157,8 @@ void * networkedServerThread(void *arg) { &timeout ); - // Timeout - if(activity == 0) continue; - - // Check for errors - if(activity < 0) { - consolePrint("Error in select"); - continue; - } + // Timeout or no activity + if(activity <= 0) continue; // Check if there is a new connection if(!FD_ISSET(server->networked.socket, &readfds)) continue; @@ -211,7 +222,11 @@ void * networkedServerThread(void *arg) { "Server thread exiting while server is running?" ); - // Notify main thread we are stopped. + // Notify main thread that the server has stopped + pthread_mutex_lock(&server->networked.lock); server->state = SERVER_STATE_STOPPED; + pthread_cond_signal(&server->networked.cond); + pthread_mutex_unlock(&server->networked.lock); + return NULL; } \ No newline at end of file diff --git a/src/server/networked/networkedserver.h b/src/server/networked/networkedserver.h index 40af3a5..4512ae7 100644 --- a/src/server/networked/networkedserver.h +++ b/src/server/networked/networkedserver.h @@ -17,6 +17,8 @@ typedef struct { typedef struct { int socket; struct sockaddr_in address; + pthread_mutex_t lock; + pthread_cond_t cond; } networkedserver_t; typedef struct server_s server_t; diff --git a/src/server/networked/networkedserverclient.c b/src/server/networked/networkedserverclient.c index ae832c3..cc8f976 100644 --- a/src/server/networked/networkedserverclient.c +++ b/src/server/networked/networkedserverclient.c @@ -29,6 +29,10 @@ errorret_t networkedServerClientAccept( client->networked.timeout.tv_sec = 8; client->networked.timeout.tv_usec = 0; + // Initialize mutex and condition variable + pthread_mutex_init(&client->networked.lock, NULL); + pthread_cond_init(&client->networked.cond, NULL); + // Create a thread for the client int32_t ret = pthread_create( &client->networked.thread, @@ -65,19 +69,21 @@ void networkedServerClientClose(serverclient_t *client) { ); // Mark client as disconnecting + pthread_mutex_lock(&client->networked.lock); client->state = SERVER_CLIENT_STATE_DISCONNECTING; - int32_t maxWaits = 0; - while(client->state == SERVER_CLIENT_STATE_DISCONNECTING) { - // Wait for the thread to finish - usleep(1000); - maxWaits++; - if(maxWaits > 10) break; - } + // Signal the condition variable to wake up the thread + pthread_cond_signal(&client->networked.cond); + pthread_mutex_unlock(&client->networked.lock); + // Wait for the thread to finish pthread_join(client->networked.thread, NULL); client->networked.thread = 0; - + + // Destroy mutex and condition variable + pthread_mutex_destroy(&client->networked.lock); + pthread_cond_destroy(&client->networked.cond); + // Close the socket if(client->networked.socket != -1) { close(client->networked.socket); @@ -100,8 +106,14 @@ void networkedServerClientCloseOnThread( ); assertNotMainThread("Client close must not be main thread"); - // Terminate the socket + pthread_mutex_lock(&client->networked.lock); client->state = SERVER_CLIENT_STATE_DISCONNECTING; + + // Signal the condition variable to wake up the thread + pthread_cond_signal(&client->networked.cond); + pthread_mutex_unlock(&client->networked.lock); + + // Terminate the socket close(client->networked.socket); client->networked.socket = -1; client->networked.thread = 0; @@ -282,15 +294,17 @@ void * networkedServerClientThread(void *arg) { // Start listening for packets. client->state = SERVER_CLIENT_STATE_CONNECTED; while(client->state == SERVER_CLIENT_STATE_CONNECTED) { - err = networkedServerClientReadPacket(client, &packet); - if(err != ERROR_OK) { - errorPrint(); - networkedServerClientCloseOnThread(client, "Failed to read packet"); - break; + pthread_mutex_lock(&client->networked.lock); + + // Wait for a signal if the client is disconnecting + while(client->state == SERVER_CLIENT_STATE_DISCONNECTING) { + pthread_cond_wait(&client->networked.cond, &client->networked.lock); } - printf("Received packet type %d\n", packet.type); - + pthread_mutex_unlock(&client->networked.lock); + + // ...existing code for reading packets... + if(SERVER.state != SERVER_STATE_RUNNING) { packetDisconnectCreate( &packet, @@ -302,7 +316,10 @@ void * networkedServerClientThread(void *arg) { break; } } - + + pthread_mutex_lock(&client->networked.lock); client->state = SERVER_CLIENT_STATE_DISCONNECTED; + pthread_mutex_unlock(&client->networked.lock); + return NULL; } \ No newline at end of file diff --git a/src/server/networked/networkedserverclient.h b/src/server/networked/networkedserverclient.h index 103cfbf..13bb5e9 100644 --- a/src/server/networked/networkedserverclient.h +++ b/src/server/networked/networkedserverclient.h @@ -19,6 +19,8 @@ typedef struct { int32_t socket; pthread_t thread; struct timeval timeout; + pthread_mutex_t lock; + pthread_cond_t cond; } networkedserverclient_t; /** diff --git a/src/server/server.c b/src/server/server.c index 18084e4..996da41 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -26,7 +26,7 @@ void cmdStart(const consolecmdexec_t *exec) { return; } } else { - start.type = SERVER_TYPE_SINGLE_PLAYER; + start.type = SERVER_TYPE_NETWORKED; } if(exec->argc > 1) {