From 62a8786fe264400f18b57de40ddd715c5cab79a8 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sat, 12 Sep 2026 08:26:43 +1000 Subject: [PATCH] [common/client] platform: add portable worker primitives --- client/src/audio.c | 81 ++++++++++----------- client/src/clipboard_files.c | 24 +------ client/tests/common_platform_test.c | 56 +++++++++++++++ client/transports/LGMP/audio.c | 23 +----- client/transports/LGMP/clipboard.c | 25 +------ common/include/common/random.h | 29 ++++++++ common/include/common/semaphore.h | 33 +++++++++ common/src/platform/linux/CMakeLists.txt | 2 + common/src/platform/linux/random.c | 43 +++++++++++ common/src/platform/linux/semaphore.c | 80 +++++++++++++++++++++ common/src/platform/windows/CMakeLists.txt | 3 + common/src/platform/windows/random.c | 43 +++++++++++ common/src/platform/windows/semaphore.c | 83 ++++++++++++++++++++++ 13 files changed, 417 insertions(+), 108 deletions(-) create mode 100644 common/include/common/random.h create mode 100644 common/include/common/semaphore.h create mode 100644 common/src/platform/linux/random.c create mode 100644 common/src/platform/linux/semaphore.c create mode 100644 common/src/platform/windows/random.c create mode 100644 common/src/platform/windows/semaphore.c diff --git a/client/src/audio.c b/client/src/audio.c index d87a730d..8c0ff68a 100644 --- a/client/src/audio.c +++ b/client/src/audio.c @@ -26,18 +26,17 @@ #include "common/debug.h" #include "common/event.h" #include "common/locking.h" +#include "common/semaphore.h" #include "common/thread.h" #include "common/util.h" #include "common/ringbuffer.h" #include "dynamic/audiodev.h" -#include #include #include #include #include -#include #include #include #include @@ -303,16 +302,16 @@ typedef struct LG_Lock deviceLock; struct { - sem_t wake; - bool wakeInitialized; - atomic_bool wakePending; - LGThread * thread; - atomic_bool stop; + LGSemaphore * wake; + bool wakeInitialized; + atomic_bool wakePending; + LGThread * thread; + atomic_bool stop; } worker; _Atomic(StreamState) state; atomic_uint callbackState; - sem_t callbackIdle; + LGSemaphore * callbackIdle; bool callbackIdleInitialized; atomic_uint streamGeneration; uint64_t requestSerial; @@ -410,13 +409,13 @@ typedef struct struct { - LG_Lock lock; - sem_t wake; - bool wakeInitialized; - atomic_bool wakePending; - LGThread * thread; - atomic_bool stop; - bool pending; + LG_Lock lock; + LGSemaphore * wake; + bool wakeInitialized; + atomic_bool wakePending; + LGThread * thread; + atomic_bool stop; + bool pending; const LG_AudioOps * ops; void * opaque; @@ -1087,7 +1086,7 @@ static void playbackCallbackExit(void) if ((previous & PLAYBACK_CALLBACK_DISABLED) && (previous & PLAYBACK_CALLBACK_COUNT_MASK) == 1 && audio.playback.callbackIdleInitialized) - sem_post(&audio.playback.callbackIdle); + lgSignalSemaphore(audio.playback.callbackIdle); } static void playbackDisableCallbacks(void) @@ -1103,12 +1102,7 @@ static void playbackWaitForCallbacks(void) &audio.playback.callbackState, memory_order_acquire) & PLAYBACK_CALLBACK_COUNT_MASK) != 0) { - int result; - do - result = sem_wait(&audio.playback.callbackIdle); - while (result < 0 && errno == EINTR); - - if (result < 0) + if (!lgWaitSemaphore(audio.playback.callbackIdle)) break; } } @@ -1121,7 +1115,7 @@ static void playbackWorkerWake(void) &audio.playback.worker.wakePending, true, memory_order_acq_rel)) return; - if (sem_post(&audio.playback.worker.wake) < 0 && errno != EOVERFLOW) + if (!lgSignalSemaphore(audio.playback.worker.wake)) atomic_store_explicit( &audio.playback.worker.wakePending, false, memory_order_release); } @@ -1133,7 +1127,7 @@ static void feedbackWorkerWake(void) &audio.feedback.wakePending, true, memory_order_acq_rel)) return; - if (sem_post(&audio.feedback.wake) < 0 && errno != EOVERFLOW) + if (!lgSignalSemaphore(audio.feedback.wake)) atomic_store_explicit( &audio.feedback.wakePending, false, memory_order_release); } @@ -4395,11 +4389,7 @@ static int playbackThread(void * opaque) { for (;;) { - int result; - do - result = sem_wait(&audio.playback.worker.wake); - while (result < 0 && errno == EINTR); - if (result < 0) + if (!lgWaitSemaphore(audio.playback.worker.wake)) break; atomic_store_explicit( @@ -4453,11 +4443,7 @@ static int feedbackThread(void * opaque) for (;;) { - int result; - do - result = sem_wait(&audio.feedback.wake); - while (result < 0 && errno == EINTR); - if (result < 0) + if (!lgWaitSemaphore(audio.feedback.wake)) break; atomic_store_explicit( @@ -4577,7 +4563,8 @@ void lgAudio_init(void) &audio.playback.callbackState, PLAYBACK_CALLBACK_DISABLED, memory_order_release); - if (sem_init(&audio.playback.callbackIdle, 0, 0) != 0) + audio.playback.callbackIdle = lgCreateSemaphore(0); + if (!audio.playback.callbackIdle) { DEBUG_ERROR("Failed to create the audio callback semaphore"); return; @@ -4591,7 +4578,8 @@ void lgAudio_init(void) goto err_callback; } - if (sem_init(&audio.playback.worker.wake, 0, 0) != 0) + audio.playback.worker.wake = lgCreateSemaphore(0); + if (!audio.playback.worker.wake) { DEBUG_ERROR("Failed to create the audio playback semaphore"); goto err_active; @@ -4604,7 +4592,8 @@ void lgAudio_init(void) goto err_playbackWake; } - if (sem_init(&audio.feedback.wake, 0, 0) != 0) + audio.feedback.wake = lgCreateSemaphore(0); + if (!audio.feedback.wake) { DEBUG_ERROR("Failed to create the audio feedback semaphore"); goto err_playbackThread; @@ -4648,7 +4637,8 @@ void lgAudio_init(void) return; err_feedbackWake: - sem_destroy(&audio.feedback.wake); + lgFreeSemaphore(audio.feedback.wake); + audio.feedback.wake = NULL; audio.feedback.wakeInitialized = false; err_playbackThread: @@ -4659,7 +4649,8 @@ err_playbackThread: audio.playback.worker.thread = NULL; err_playbackWake: - sem_destroy(&audio.playback.worker.wake); + lgFreeSemaphore(audio.playback.worker.wake); + audio.playback.worker.wake = NULL; audio.playback.worker.wakeInitialized = false; err_active: @@ -4667,7 +4658,8 @@ err_active: audio.activeIdle = NULL; err_callback: - sem_destroy(&audio.playback.callbackIdle); + lgFreeSemaphore(audio.playback.callbackIdle); + audio.playback.callbackIdle = NULL; audio.playback.callbackIdleInitialized = false; } @@ -4696,7 +4688,8 @@ void lgAudio_free(void) } if (audio.playback.worker.wakeInitialized) { - sem_destroy(&audio.playback.worker.wake); + lgFreeSemaphore(audio.playback.worker.wake); + audio.playback.worker.wake = NULL; audio.playback.worker.wakeInitialized = false; } @@ -4710,7 +4703,8 @@ void lgAudio_free(void) } if (audio.feedback.wakeInitialized) { - sem_destroy(&audio.feedback.wake); + lgFreeSemaphore(audio.feedback.wake); + audio.feedback.wake = NULL; audio.feedback.wakeInitialized = false; } @@ -4740,7 +4734,8 @@ void lgAudio_free(void) if (audio.playback.callbackIdleInitialized) { - sem_destroy(&audio.playback.callbackIdle); + lgFreeSemaphore(audio.playback.callbackIdle); + audio.playback.callbackIdle = NULL; audio.playback.callbackIdleInitialized = false; } if (audio.activeIdle) diff --git a/client/src/clipboard_files.c b/client/src/clipboard_files.c index 06a5d25d..5f3cca22 100644 --- a/client/src/clipboard_files.c +++ b/client/src/clipboard_files.c @@ -27,6 +27,7 @@ #include "common/debug.h" #include "common/event.h" #include "common/locking.h" +#include "common/random.h" #include "common/thread.h" #include "common/time.h" @@ -44,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -228,26 +228,6 @@ static bool filesLockInitialized; static atomic_bool forceLocalEof; #endif -static bool randomBytes(void * buffer, size_t size) -{ - uint8_t * bytes = buffer; - size_t offset = 0; - while (offset < size) - { - const ssize_t result = getrandom(bytes + offset, size - offset, 0); - if (result < 0 && errno == EINTR) - continue; - if (result <= 0) - { - if (!result) - errno = EIO; - return false; - } - offset += (size_t)result; - } - return true; -} - static uint64_t nsFromTimespec(struct timespec value) { if (value.tv_sec < 0 || value.tv_nsec < 0) @@ -3446,7 +3426,7 @@ bool clipboardFiles_init(void) LG_LOCK_INIT(files.lock); filesLockInitialized = true; uint64_t nonce = 0; - if (!randomBytes(&nonce, sizeof(nonce))) + if (!lgRandomBytes(&nonce, sizeof(nonce))) { DEBUG_ERROR("Failed to seed clipboard file transfer identifiers: %s", strerror(errno)); diff --git a/client/tests/common_platform_test.c b/client/tests/common_platform_test.c index c5a4df20..bbce7b04 100644 --- a/client/tests/common_platform_test.c +++ b/client/tests/common_platform_test.c @@ -21,11 +21,14 @@ #include "test.h" #include "common/event.h" +#include "common/random.h" +#include "common/semaphore.h" #include "common/thread.h" #include "common/time.h" #include #include +#include #define TEST_WAIT_NS UINT64_C(1000000000) @@ -40,6 +43,12 @@ struct TimerState atomic_uint calls; }; +struct SemaphoreState +{ + LGSemaphore * semaphore; + LGEvent * complete; +}; + static int signalThread(void * opaque) { const struct SignalState * state = opaque; @@ -60,6 +69,14 @@ static bool timerCallback(void * opaque) return false; } +static int semaphoreThread(void * opaque) +{ + const struct SemaphoreState * state = opaque; + CHECK(lgWaitSemaphore(state->semaphore)); + CHECK(lgSignalEvent(state->complete)); + return 0; +} + static void testEventWaits(void) { LGEvent * event = lgCreateEvent(true, 0); @@ -107,11 +124,50 @@ static void testTimer(void) lgFreeEvent(event); } +static void testSemaphore(void) +{ + LGSemaphore * semaphore = lgCreateSemaphore(0); + LGEvent * complete = lgCreateEvent(true, 0); + CHECK(semaphore); + CHECK(complete); + + const struct SemaphoreState state = + { + .semaphore = semaphore, + .complete = complete, + }; + LGThread * thread; + CHECK(lgCreateThread( + "semaphore", semaphoreThread, (void *)&state, &thread)); + CHECK(!lgWaitEventNS(complete, UINT64_C(1000000))); + CHECK(lgSignalSemaphore(semaphore)); + CHECK(lgWaitEventNS(complete, TEST_WAIT_NS)); + CHECK(lgJoinThread(thread, NULL)); + + CHECK(lgSignalSemaphore(semaphore)); + CHECK(lgSignalSemaphore(semaphore)); + CHECK(lgWaitSemaphore(semaphore)); + CHECK(lgWaitSemaphore(semaphore)); + lgFreeEvent(complete); + lgFreeSemaphore(semaphore); +} + +static void testRandom(void) +{ + uint8_t first [32] = { 0 }; + uint8_t second[32] = { 0 }; + CHECK(lgRandomBytes(first, sizeof(first))); + CHECK(lgRandomBytes(second, sizeof(second))); + CHECK(memcmp(first, second, sizeof(first)) != 0); +} + int main(void) { lgTimeInit(); testEventWaits(); testManualReset(); testTimer(); + testSemaphore(); + testRandom(); return 0; } diff --git a/client/transports/LGMP/audio.c b/client/transports/LGMP/audio.c index dc2c3f74..bec638e6 100644 --- a/client/transports/LGMP/audio.c +++ b/client/transports/LGMP/audio.c @@ -23,19 +23,18 @@ #include #include "common/debug.h" #include "common/locking.h" +#include "common/random.h" #include "common/thread.h" #include "common/time.h" #include #include -#include #include #include #include #include #include -#include #define AUDIO_IDLE_POLL_MS 20U #define AUDIO_ACTIVE_POLL_US 25U @@ -242,22 +241,6 @@ static uint32_t nextNonzero(uint32_t * value) return *value; } -static bool randomBytes(void * buffer, size_t size) -{ - uint8_t * output = buffer; - while (size) - { - const ssize_t received = getrandom(output, size, 0); - if (received < 0 && errno == EINTR) - continue; - if (received <= 0) - return false; - output += (size_t)received; - size -= (size_t)received; - } - return true; -} - static bool tryControlWriteGate(LGMPAudio * audio) { return !atomic_flag_test_and_set_explicit( @@ -2512,7 +2495,7 @@ bool lgmpAudio_create(PLGMPClient client, LGEvent * pointerWake, return false; } audio->generationSerial = 1; - if (!randomBytes(&audio->requestSerial, + if (!lgRandomBytes(&audio->requestSerial, sizeof(audio->requestSerial))) { lgFreeEvent(audio->event); @@ -2668,7 +2651,7 @@ void lgmpAudio_disconnect(LGMPAudio * audio) static bool newClaimNL(LGMPAudio * audio) { uint64_t token = 0; - if (!randomBytes(&token, sizeof(token))) + if (!lgRandomBytes(&token, sizeof(token))) { token = microtime() ^ ((uint64_t)audio->clientID << 32) ^ audio->requestSerial; diff --git a/client/transports/LGMP/clipboard.c b/client/transports/LGMP/clipboard.c index 4112f1de..dcacbd03 100644 --- a/client/transports/LGMP/clipboard.c +++ b/client/transports/LGMP/clipboard.c @@ -25,17 +25,16 @@ #include "common/debug.h" #include "common/event.h" #include "common/locking.h" +#include "common/random.h" #include "common/thread.h" #include "common/time.h" #include -#include #include #include #include #include -#include #define CLIPBOARD_PENDING_MAX 128U #define CLIPBOARD_PENDING_NORMAL_MAX 64U @@ -366,26 +365,6 @@ static uint64_t nextClientGeneration(LGMPClipboard * clipboard) return clipboard->localGenerationSerial; } -static bool randomBytes(void * buffer, size_t size) -{ - uint8_t * output = buffer; - while (size) - { - const ssize_t received = getrandom(output, size, 0); - if (received < 0 && errno == EINTR) - continue; - if (received <= 0) - { - if (!received) - errno = EIO; - return false; - } - output += (size_t)received; - size -= (size_t)received; - } - return true; -} - static LG_ClipboardFileError fromWireFileError(uint32_t error) { switch (error) @@ -2365,7 +2344,7 @@ bool lgmpClipboard_create(PLGMPClient client, LGMPClipboard ** result) return false; clipboard->client = client; uint64_t nonce = 0; - if (!randomBytes(&nonce, sizeof(nonce))) + if (!lgRandomBytes(&nonce, sizeof(nonce))) { free(clipboard); return false; diff --git a/common/include/common/random.h b/common/include/common/random.h new file mode 100644 index 00000000..bf40122e --- /dev/null +++ b/common/include/common/random.h @@ -0,0 +1,29 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _H_LG_COMMON_RANDOM_ +#define _H_LG_COMMON_RANDOM_ + +#include +#include + +bool lgRandomBytes(void * buffer, size_t size); + +#endif diff --git a/common/include/common/semaphore.h b/common/include/common/semaphore.h new file mode 100644 index 00000000..053ba9c6 --- /dev/null +++ b/common/include/common/semaphore.h @@ -0,0 +1,33 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _H_LG_COMMON_SEMAPHORE_ +#define _H_LG_COMMON_SEMAPHORE_ + +#include + +typedef struct LGSemaphore LGSemaphore; + +LGSemaphore * lgCreateSemaphore(unsigned int initialValue); +void lgFreeSemaphore (LGSemaphore * semaphore); +bool lgWaitSemaphore (LGSemaphore * semaphore); +bool lgSignalSemaphore(LGSemaphore * semaphore); + +#endif diff --git a/common/src/platform/linux/CMakeLists.txt b/common/src/platform/linux/CMakeLists.txt index 82b91adf..8785cdaa 100644 --- a/common/src/platform/linux/CMakeLists.txt +++ b/common/src/platform/linux/CMakeLists.txt @@ -12,6 +12,8 @@ add_library(lg_common_platform_code STATIC sysinfo.c thread.c event.c + semaphore.c + random.c ivshmem.c time.c paths.c diff --git a/common/src/platform/linux/random.c b/common/src/platform/linux/random.c new file mode 100644 index 00000000..7fb813d0 --- /dev/null +++ b/common/src/platform/linux/random.c @@ -0,0 +1,43 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/random.h" + +#include +#include +#include + +bool lgRandomBytes(void * buffer, size_t size) +{ + uint8_t * output = buffer; + while (size) + { + const ssize_t received = getrandom(output, size, 0); + if (received < 0 && errno == EINTR) + continue; + if (received <= 0) + return false; + + output += (size_t)received; + size -= (size_t)received; + } + + return true; +} diff --git a/common/src/platform/linux/semaphore.c b/common/src/platform/linux/semaphore.c new file mode 100644 index 00000000..1ad2692f --- /dev/null +++ b/common/src/platform/linux/semaphore.c @@ -0,0 +1,80 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/semaphore.h" + +#include "common/debug.h" + +#include +#include +#include +#include + +struct LGSemaphore +{ + sem_t handle; +}; + +LGSemaphore * lgCreateSemaphore(unsigned int initialValue) +{ + LGSemaphore * semaphore = malloc(sizeof(*semaphore)); + if (!semaphore) + return NULL; + + if (sem_init(&semaphore->handle, 0, initialValue) != 0) + { + DEBUG_ERROR("Failed to create semaphore: %s", strerror(errno)); + free(semaphore); + return NULL; + } + + return semaphore; +} + +void lgFreeSemaphore(LGSemaphore * semaphore) +{ + if (!semaphore) + return; + + if (sem_destroy(&semaphore->handle) != 0) + DEBUG_ERROR("Failed to destroy semaphore: %s", strerror(errno)); + free(semaphore); +} + +bool lgWaitSemaphore(LGSemaphore * semaphore) +{ + int result; + do + result = sem_wait(&semaphore->handle); + while (result != 0 && errno == EINTR); + + if (result != 0) + DEBUG_ERROR("Failed to wait on semaphore: %s", strerror(errno)); + return result == 0; +} + +bool lgSignalSemaphore(LGSemaphore * semaphore) +{ + if (sem_post(&semaphore->handle) == 0) + return true; + + DEBUG_ERROR("Failed to signal semaphore: %s", strerror(errno)); + return false; +} diff --git a/common/src/platform/windows/CMakeLists.txt b/common/src/platform/windows/CMakeLists.txt index 0f25eb81..a0d713be 100644 --- a/common/src/platform/windows/CMakeLists.txt +++ b/common/src/platform/windows/CMakeLists.txt @@ -35,6 +35,8 @@ add_library(lg_common_platform_code STATIC sysinfo.c thread.c event.c + semaphore.c + random.c windebug.c ivshmem.c time.c @@ -46,6 +48,7 @@ add_library(lg_common_platform_code STATIC target_link_libraries(lg_common_platform_code lg_common setupapi + bcrypt ntdll ) diff --git a/common/src/platform/windows/random.c b/common/src/platform/windows/random.c new file mode 100644 index 00000000..39988c13 --- /dev/null +++ b/common/src/platform/windows/random.c @@ -0,0 +1,43 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/random.h" + +#include +#include +#include +#include + +bool lgRandomBytes(void * buffer, size_t size) +{ + uint8_t * output = buffer; + while (size) + { + const ULONG chunk = size > ULONG_MAX ? ULONG_MAX : (ULONG)size; + if (!BCRYPT_SUCCESS(BCryptGenRandom( + NULL, output, chunk, BCRYPT_USE_SYSTEM_PREFERRED_RNG))) + return false; + + output += chunk; + size -= chunk; + } + + return true; +} diff --git a/common/src/platform/windows/semaphore.c b/common/src/platform/windows/semaphore.c new file mode 100644 index 00000000..9e8fce37 --- /dev/null +++ b/common/src/platform/windows/semaphore.c @@ -0,0 +1,83 @@ +/** + * Looking Glass + * Copyright © 2017-2026 The Looking Glass Authors + * https://looking-glass.io + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 59 + * Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common/semaphore.h" + +#include "common/debug.h" +#include "common/windebug.h" + +#include +#include +#include + +struct LGSemaphore +{ + HANDLE handle; +}; + +LGSemaphore * lgCreateSemaphore(unsigned int initialValue) +{ + if (initialValue > (unsigned int)LONG_MAX) + return NULL; + + LGSemaphore * semaphore = malloc(sizeof(*semaphore)); + if (!semaphore) + return NULL; + + semaphore->handle = CreateSemaphoreW( + NULL, (LONG)initialValue, LONG_MAX, NULL); + if (!semaphore->handle) + { + DEBUG_WINERROR("Failed to create semaphore", GetLastError()); + free(semaphore); + return NULL; + } + + return semaphore; +} + +void lgFreeSemaphore(LGSemaphore * semaphore) +{ + if (!semaphore) + return; + + if (!CloseHandle(semaphore->handle)) + DEBUG_WINERROR("Failed to destroy semaphore", GetLastError()); + free(semaphore); +} + +bool lgWaitSemaphore(LGSemaphore * semaphore) +{ + const DWORD result = WaitForSingleObject(semaphore->handle, INFINITE); + if (result == WAIT_OBJECT_0) + return true; + + DEBUG_WINERROR("Failed to wait on semaphore", GetLastError()); + return false; +} + +bool lgSignalSemaphore(LGSemaphore * semaphore) +{ + if (ReleaseSemaphore(semaphore->handle, 1, NULL)) + return true; + + DEBUG_WINERROR("Failed to signal semaphore", GetLastError()); + return false; +}