From c76c2066c64a3bd390e63eb08a00b0a0c106f445 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 3 Jun 2012 03:17:21 +0200 Subject: [PATCH] unix, windows: add semaphore functions --- include/uv-private/uv-unix.h | 3 + include/uv-private/uv-win.h | 2 + include/uv.h | 9 +++ src/unix/thread.c | 43 ++++++++++++++ src/win/thread.c | 38 ++++++++++++ test/test-list.h | 6 ++ test/test-semaphore.c | 111 +++++++++++++++++++++++++++++++++++ uv.gyp | 1 + 8 files changed, 213 insertions(+) create mode 100644 test/test-semaphore.c diff --git a/include/uv-private/uv-unix.h b/include/uv-private/uv-unix.h index ae9f5fb3..3b9619c8 100644 --- a/include/uv-private/uv-unix.h +++ b/include/uv-private/uv-unix.h @@ -35,6 +35,8 @@ #include #include #include + +#include #include #if __sun @@ -58,6 +60,7 @@ typedef pthread_once_t uv_once_t; typedef pthread_t uv_thread_t; typedef pthread_mutex_t uv_mutex_t; typedef pthread_rwlock_t uv_rwlock_t; +typedef sem_t uv_sem_t; /* Platform-specific definitions for uv_spawn support. */ typedef gid_t uv_gid_t; diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index 3b091b93..25998494 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -169,6 +169,8 @@ typedef SOCKET uv_os_sock_t; typedef HANDLE uv_thread_t; +typedef HANDLE uv_sem_t; + typedef CRITICAL_SECTION uv_mutex_t; typedef union { diff --git a/include/uv.h b/include/uv.h index 2ddcfa7c..849d0cd8 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1624,6 +1624,15 @@ UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock); UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock); UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock); +/* + * Same goes for the semaphore functions. + */ +UV_EXTERN int uv_sem_init(uv_sem_t* sem, unsigned int value); +UV_EXTERN void uv_sem_destroy(uv_sem_t* sem); +UV_EXTERN void uv_sem_post(uv_sem_t* sem); +UV_EXTERN void uv_sem_wait(uv_sem_t* sem); +UV_EXTERN int uv_sem_trywait(uv_sem_t* sem); + /* Runs a function once and only once. Concurrent calls to uv_once() with the * same guard will block all callers except one (it's unspecified which one). * The guard should be initialized statically with the UV_ONCE_INIT macro. diff --git a/src/unix/thread.c b/src/unix/thread.c index 02352e0d..cd4e3333 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -166,3 +166,46 @@ void uv_once(uv_once_t* guard, void (*callback)(void)) { if (pthread_once(guard, callback)) abort(); } + + +int uv_sem_init(uv_sem_t* sem, unsigned int value) { + return sem_init(sem, 0, value); +} + + +void uv_sem_destroy(uv_sem_t* sem) { + if (sem_destroy(sem)) + abort(); +} + + +void uv_sem_post(uv_sem_t* sem) { + if (sem_post(sem)) + abort(); +} + + +void uv_sem_wait(uv_sem_t* sem) { + int r; + + do + r = sem_wait(sem); + while (r == -1 && errno == EINTR); + + if (r) + abort(); +} + + +int uv_sem_trywait(uv_sem_t* sem) { + int r; + + do + r = sem_trywait(sem); + while (r == -1 && errno == EINTR); + + if (r && errno != EAGAIN) + abort(); + + return r; +} diff --git a/src/win/thread.c b/src/win/thread.c index 01b81408..d66e40aa 100644 --- a/src/win/thread.c +++ b/src/win/thread.c @@ -20,6 +20,7 @@ */ #include +#include #include "uv.h" #include "internal.h" @@ -207,6 +208,43 @@ void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { } +int uv_sem_init(uv_sem_t* sem, unsigned int value) { + *sem = CreateSemaphore(NULL, value, INT_MAX, NULL); + return *sem ? 0 : -1; +} + + +void uv_sem_destroy(uv_sem_t* sem) { + if (!CloseHandle(*sem)) + abort(); +} + + +void uv_sem_post(uv_sem_t* sem) { + if (!ReleaseSemaphore(*sem, 1, NULL)) + abort(); +} + + +void uv_sem_wait(uv_sem_t* sem) { + if (WaitForSingleObject(*sem, INFINITE) != WAIT_OBJECT_0) + abort(); +} + + +int uv_sem_trywait(uv_sem_t* sem) { + DWORD r = WaitForSingleObject(*sem, 0); + + if (r == WAIT_OBJECT_0) + return 0; + + if (r == WAIT_TIMEOUT) + return -1; + + abort(); +} + + inline static int uv__rwlock_srwlock_init(uv_rwlock_t* rwlock) { pInitializeSRWLock(&rwlock->srwlock_); return 0; diff --git a/test/test-list.h b/test/test-list.h index acb15150..a0b67e42 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -22,6 +22,9 @@ TEST_DECLARE (platform_output) TEST_DECLARE (callback_order) TEST_DECLARE (run_once) +TEST_DECLARE (semaphore_1) +TEST_DECLARE (semaphore_2) +TEST_DECLARE (semaphore_3) TEST_DECLARE (tty) TEST_DECLARE (stdio_over_pipes) TEST_DECLARE (ipc_listen_before_write) @@ -193,6 +196,9 @@ TASK_LIST_START TEST_ENTRY (callback_order) #endif TEST_ENTRY (run_once) + TEST_ENTRY (semaphore_1) + TEST_ENTRY (semaphore_2) + TEST_ENTRY (semaphore_3) TEST_ENTRY (pipe_connect_bad_name) TEST_ENTRY (pipe_connect_to_file) diff --git a/test/test-semaphore.c b/test/test-semaphore.c new file mode 100644 index 00000000..ee89d4fc --- /dev/null +++ b/test/test-semaphore.c @@ -0,0 +1,111 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include + +typedef struct { + uv_mutex_t mutex; + uv_sem_t sem; + int delay; + volatile int posted; +} worker_config; + + +static void worker(void* arg) { + worker_config* c = arg; + + if (c->delay) + uv_sleep(c->delay); + + uv_mutex_lock(&c->mutex); + ASSERT(c->posted == 0); + uv_sem_post(&c->sem); + c->posted = 1; + uv_mutex_unlock(&c->mutex); +} + + +TEST_IMPL(semaphore_1) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + + ASSERT(0 == uv_sem_init(&wc.sem, 0)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_sleep(100); + uv_mutex_lock(&wc.mutex); + ASSERT(wc.posted == 1); + uv_sem_wait(&wc.sem); /* should not block */ + uv_mutex_unlock(&wc.mutex); /* ergo, it should be ok to unlock after wait */ + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_sem_destroy(&wc.sem); + + return 0; +} + + +TEST_IMPL(semaphore_2) { + uv_thread_t thread; + worker_config wc; + + memset(&wc, 0, sizeof(wc)); + wc.delay = 100; + + ASSERT(0 == uv_sem_init(&wc.sem, 0)); + ASSERT(0 == uv_mutex_init(&wc.mutex)); + ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + + uv_sem_wait(&wc.sem); + + ASSERT(0 == uv_thread_join(&thread)); + uv_mutex_destroy(&wc.mutex); + uv_sem_destroy(&wc.sem); + + return 0; +} + + +TEST_IMPL(semaphore_3) { + uv_sem_t sem; + + ASSERT(0 == uv_sem_init(&sem, 3)); + uv_sem_wait(&sem); /* should not block */ + uv_sem_wait(&sem); /* should not block */ + ASSERT(0 == uv_sem_trywait(&sem)); + ASSERT(-1 == uv_sem_trywait(&sem)); + + uv_sem_post(&sem); + ASSERT(0 == uv_sem_trywait(&sem)); + ASSERT(-1 == uv_sem_trywait(&sem)); + + uv_sem_destroy(&sem); + + return 0; +} diff --git a/uv.gyp b/uv.gyp index 4faf7d83..9bdfa458 100644 --- a/uv.gyp +++ b/uv.gyp @@ -333,6 +333,7 @@ 'test/test-process-title.c', 'test/test-ref.c', 'test/test-run-once.c', + 'test/test-semaphore.c', 'test/test-shutdown-close.c', 'test/test-shutdown-eof.c', 'test/test-spawn.c',