From 34c53bc3d396934df94431f7b76972180b1b0a8c Mon Sep 17 00:00:00 2001 From: Trumeet Date: Sat, 23 Jul 2022 11:32:53 -0700 Subject: feat(acronc): guard the connection using a mutex Signed-off-by: Trumeet --- client/libacron/README.md | 3 ++ client/libacron/acronc/main.c | 85 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) diff --git a/client/libacron/README.md b/client/libacron/README.md index 09e36dc..d52a4c3 100644 --- a/client/libacron/README.md +++ b/client/libacron/README.md @@ -261,6 +261,9 @@ it is the caller's responsibility to guard the whole connection with a mutex. li provide that because this may limit the flexibility. After locking, it is safe to call the connection-related functions from any threads. +Example `acronc/main.c` has a naive cross-platform example of how to guard the connection using +a mutex. + ## Roadmap * Make unit tests diff --git a/client/libacron/acronc/main.c b/client/libacron/acronc/main.c index 43802ad..dfd1668 100644 --- a/client/libacron/acronc/main.c +++ b/client/libacron/acronc/main.c @@ -15,6 +15,65 @@ #endif +#ifdef __STDC_NO_THREADS__ +# ifdef WIN32 +#define THREAD_WIN32 +#include +static HANDLE mtx_conn; + +int lock(void) { \ +DWORD _dwlck = WaitForSingleObject(mtx_conn, 0); \ +if (_dwlck == WAIT_FAILED) { _dwlck = GetLastError(); } \ +if (_dwlck) { fprintf(stderr, "Cannot lock the mutex: %d.\n", _dwlck); return _dwlck; } \ +return 0; \ +} + +int unlock(void) { \ +if (!ReleaseMutex(mtx_conn)) { \ +int r = GetLastError(); \ +fprintf(stderr, "Cannot release the mutex: %d.\n", r); \ +return r; \ +} \ +return 0; \ +} + +# else +#error "Either C11 threading or Win32 API is required for concurrency." +# endif +#else + +#define THREAD_C11 + +#include + +static mtx_t mtx_conn; + +int lock(void) { + \ +if (mtx_lock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot lock the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +int unlock(void) { + \ +if (mtx_unlock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot release the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +#endif + static const char *world_name(const enum ac_world world) { switch (world) { case overworld: @@ -168,6 +227,19 @@ int main(int argc, char **argv) { if ((r = net_init())) { return r; } +#if defined(THREAD_WIN32) + if (!(mtx_conn = CreateMutex(NULL, TRUE, NULL))) { + r = GetLastError(); + fprintf(stderr, "Cannot create mutex: %d.\n", r); + return r; + } +#elif defined(THREAD_C11) + if (mtx_init(&mtx_conn, mtx_plain) != thrd_success) { + fprintf(stderr, "Cannot create mutex.\n"); + return 1; + } +#endif + #ifdef WIN32 fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); #else @@ -235,12 +307,15 @@ int main(int argc, char **argv) { goto end; } } + if ((r = lock())) { goto end; } if ((r = ac_receive(connection, buffer, bytes, &obj))) { + if ((r = unlock())) { goto end; } goto end; } if (!ready) { /* Wait until ready. */ if ((r = ac_get_state(connection, &state))) { + if ((r = unlock())) { goto end; } goto end; } switch (state) { @@ -250,12 +325,15 @@ int main(int argc, char **argv) { printf("Connection is established.\n"); ready = true; say(connection); + if ((r = unlock())) { goto end; } continue; default: fprintf(stderr, "Unexpected state.\n"); + if ((r = unlock())) { goto end; } goto end; } } + if ((r = unlock())) { goto end; } if (!obj) { continue; @@ -277,9 +355,16 @@ int main(int argc, char **argv) { nr); } } + if ((r = lock())) { goto end; } ac_disconnect(connection); + if ((r = unlock())) { goto end; } net_close(&sock); ac_free(); net_free(); +#if defined(THREAD_WIN32) + CloseHandle(mtx_conn); +#elif defined(THREAD_C11) + mtx_destroy(&mtx_conn); +#endif return r; } \ No newline at end of file -- cgit v1.2.3