diff options
-rw-r--r-- | include/zix/common.h | 1 | ||||
-rw-r--r-- | include/zix/sem.h | 202 | ||||
-rw-r--r-- | meson.build | 27 | ||||
-rw-r--r-- | src/sem.c | 206 | ||||
-rw-r--r-- | src/status.c | 2 | ||||
-rw-r--r-- | src/zix_config.h | 30 | ||||
-rw-r--r-- | test/test_status.c | 2 |
7 files changed, 284 insertions, 186 deletions
diff --git a/include/zix/common.h b/include/zix/common.h index 2d5133f..0cefd55 100644 --- a/include/zix/common.h +++ b/include/zix/common.h @@ -28,6 +28,7 @@ typedef enum { ZIX_STATUS_REACHED_END, ZIX_STATUS_TIMEOUT, ZIX_STATUS_OVERFLOW, + ZIX_STATUS_NOT_SUPPORTED, } ZixStatus; /// Return a string describing a status code diff --git a/include/zix/sem.h b/include/zix/sem.h index 118f586..4c82f17 100644 --- a/include/zix/sem.h +++ b/include/zix/sem.h @@ -1,4 +1,4 @@ -// Copyright 2012-2020 David Robillard <d@drobilla.net> +// Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #ifndef ZIX_SEM_H @@ -10,10 +10,8 @@ #ifdef __APPLE__ # include <mach/mach.h> #elif defined(_WIN32) -# include <limits.h> # include <windows.h> #else -# include <errno.h> # include <semaphore.h> #endif @@ -58,7 +56,8 @@ typedef struct ZixSemImpl ZixSem; @return #ZIX_STATUS_SUCCESS, or an unlikely error. */ -static inline ZixStatus +ZIX_API +ZixStatus zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned initial); /** @@ -66,7 +65,8 @@ zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned initial); @return #ZIX_STATUS_SUCCESS, or an error. */ -static inline ZixStatus +ZIX_API +ZixStatus zix_sem_destroy(ZixSem* ZIX_NONNULL sem); /** @@ -78,7 +78,8 @@ zix_sem_destroy(ZixSem* ZIX_NONNULL sem); if the maximum possible value would have been exceeded, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -static inline ZixStatus +ZIX_API +ZixStatus zix_sem_post(ZixSem* ZIX_NONNULL sem); /** @@ -89,7 +90,8 @@ zix_sem_post(ZixSem* ZIX_NONNULL sem); @return #ZIX_STATUS_SUCCESS if `sem` was decremented, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -static inline ZixStatus +ZIX_API +ZixStatus zix_sem_wait(ZixSem* ZIX_NONNULL sem); /** @@ -98,7 +100,8 @@ zix_sem_wait(ZixSem* ZIX_NONNULL sem); @return #ZIX_STATUS_SUCCESS if `sem` was decremented, #ZIX_STATUS_TIMEOUT if it was already zero, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -static inline ZixStatus +ZIX_API +ZixStatus zix_sem_try_wait(ZixSem* ZIX_NONNULL sem); /** @@ -107,10 +110,12 @@ zix_sem_try_wait(ZixSem* ZIX_NONNULL sem); Obviously not realtime safe. @return #ZIX_STATUS_SUCCESS if `sem` was decremented, #ZIX_STATUS_TIMEOUT if - it was still zero when the timeout was reached, or #ZIX_STATUS_BAD_ARG if - `sem` is invalid. + it was still zero when the timeout was reached, #ZIX_STATUS_NOT_SUPPORTED if + the system does not support timed waits, or #ZIX_STATUS_BAD_ARG if `sem` is + invalid. */ -static inline ZixStatus +ZIX_API +ZixStatus zix_sem_timed_wait(ZixSem* ZIX_NONNULL sem, uint32_t seconds, uint32_t nanoseconds); @@ -119,197 +124,24 @@ zix_sem_timed_wait(ZixSem* ZIX_NONNULL sem, @cond */ -#ifdef __APPLE__ +#if defined(__APPLE__) struct ZixSemImpl { semaphore_t sem; }; -static inline ZixStatus -zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned val) -{ - return semaphore_create( - mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, (int)val) - ? ZIX_STATUS_ERROR - : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_destroy(ZixSem* ZIX_NONNULL sem) -{ - return semaphore_destroy(mach_task_self(), sem->sem) ? ZIX_STATUS_ERROR - : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_post(ZixSem* ZIX_NONNULL sem) -{ - return semaphore_signal(sem->sem) ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_wait(ZixSem* ZIX_NONNULL sem) -{ - kern_return_t r = 0; - while ((r = semaphore_wait(sem->sem)) && r == KERN_ABORTED) { - // Interrupted, try again - } - - return r ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_try_wait(ZixSem* ZIX_NONNULL sem) -{ - const mach_timespec_t zero = {0, 0}; - const kern_return_t r = semaphore_timedwait(sem->sem, zero); - - return (r == KERN_SUCCESS) ? ZIX_STATUS_SUCCESS - : (r == KERN_OPERATION_TIMED_OUT) ? ZIX_STATUS_TIMEOUT - : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_sem_timed_wait(ZixSem* ZIX_NONNULL sem, - const uint32_t seconds, - const uint32_t nanoseconds) -{ - const mach_timespec_t interval = {seconds, (clock_res_t)nanoseconds}; - const kern_return_t r = semaphore_timedwait(sem->sem, interval); - - return (r == KERN_SUCCESS) ? ZIX_STATUS_SUCCESS - : (r == KERN_OPERATION_TIMED_OUT) ? ZIX_STATUS_TIMEOUT - : ZIX_STATUS_ERROR; -} - #elif defined(_WIN32) struct ZixSemImpl { HANDLE sem; }; -static inline ZixStatus -zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned initial) -{ - sem->sem = CreateSemaphore(NULL, (LONG)initial, LONG_MAX, NULL); - return sem->sem ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_sem_destroy(ZixSem* ZIX_NONNULL sem) -{ - return CloseHandle(sem->sem) ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_sem_post(ZixSem* ZIX_NONNULL sem) -{ - return ReleaseSemaphore(sem->sem, 1, NULL) ? ZIX_STATUS_SUCCESS - : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_sem_wait(ZixSem* ZIX_NONNULL sem) -{ - return WaitForSingleObject(sem->sem, INFINITE) == WAIT_OBJECT_0 - ? ZIX_STATUS_SUCCESS - : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_sem_try_wait(ZixSem* ZIX_NONNULL sem) -{ - const DWORD r = WaitForSingleObject(sem->sem, 0); - - return (r == WAIT_OBJECT_0) ? ZIX_STATUS_SUCCESS - : (r == WAIT_TIMEOUT) ? ZIX_STATUS_TIMEOUT - : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_sem_timed_wait(ZixSem* ZIX_NONNULL sem, - const uint32_t seconds, - const uint32_t nanoseconds) -{ - const uint32_t milliseconds = seconds * 1000U + nanoseconds / 1000000U; - const DWORD r = WaitForSingleObject(sem->sem, milliseconds); - - return (r == WAIT_OBJECT_0) ? ZIX_STATUS_SUCCESS - : (r == WAIT_TIMEOUT) ? ZIX_STATUS_TIMEOUT - : ZIX_STATUS_ERROR; -} - #else /* !defined(__APPLE__) && !defined(_WIN32) */ struct ZixSemImpl { sem_t sem; }; -static inline ZixStatus -zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned initial) -{ - return sem_init(&sem->sem, 0, initial) ? zix_errno_status(errno) - : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_destroy(ZixSem* ZIX_NONNULL sem) -{ - return sem_destroy(&sem->sem) ? zix_errno_status(errno) : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_post(ZixSem* ZIX_NONNULL sem) -{ - return sem_post(&sem->sem) ? zix_errno_status(errno) : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_wait(ZixSem* ZIX_NONNULL sem) -{ - int r = 0; - while ((r = sem_wait(&sem->sem)) && errno == EINTR) { - // Interrupted, try again - } - - return r ? zix_errno_status(errno) : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_try_wait(ZixSem* ZIX_NONNULL sem) -{ - int r = 0; - while ((r = sem_trywait(&sem->sem)) && errno == EINTR) { - // Interrupted, try again - } - - return r ? (errno == EAGAIN ? ZIX_STATUS_TIMEOUT : zix_errno_status(errno)) - : ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_sem_timed_wait(ZixSem* ZIX_NONNULL sem, - const uint32_t seconds, - const uint32_t nanoseconds) -{ - struct timespec ts = {0, 0}; - - if (clock_gettime(CLOCK_REALTIME, &ts)) { - return ZIX_STATUS_ERROR; - } - - ts.tv_sec += (time_t)seconds; - ts.tv_nsec += (long)nanoseconds; - - int r = 0; - while ((r = sem_timedwait(&sem->sem, &ts)) && errno == EINTR) { - // Interrupted, try again - } - - return r ? (errno == ETIMEDOUT ? ZIX_STATUS_TIMEOUT : zix_errno_status(errno)) - : ZIX_STATUS_SUCCESS; -} - #endif /** diff --git a/meson.build b/meson.build index a894d58..ce21dc4 100644 --- a/meson.build +++ b/meson.build @@ -48,12 +48,25 @@ endif if get_option('checks') platform_c_args += ['-DZIX_NO_DEFAULT_CONFIG'] + clock_gettime_code = '''#include <time.h> +int main(void) { struct timespec t; return clock_gettime(CLOCK_MONOTONIC, &t); } +''' + mlock_code = '''#include <sys/mman.h> int main(void) { return mlock(0, 0); }''' posix_memalign_code = '''#include <stdlib.h> int main(void) { void* mem; posix_memalign(&mem, 8, 8); }''' + sem_timedwait_code = '''#include <semaphore.h> +#include <time.h> +int main(void) { sem_t s; struct timespec t; return sem_timedwait(&s, &t); }''' + + platform_c_args += '-DHAVE_CLOCK_GETTIME=@0@'.format( + cc.compiles(clock_gettime_code, + args: platform_c_args, + name: 'clock_gettime').to_int()) + platform_c_args += '-DHAVE_MLOCK=@0@'.format( cc.compiles(mlock_code, args: platform_c_args, @@ -63,8 +76,19 @@ int main(void) { void* mem; posix_memalign(&mem, 8, 8); }''' cc.compiles(posix_memalign_code, args: platform_c_args, name: 'posix_memalign').to_int()) + + platform_c_args += '-DHAVE_SEM_TIMEDWAIT=@0@'.format( + cc.compiles(sem_timedwait_code, + args: platform_c_args, + name: 'sem_timedwait').to_int()) endif +################ +# Dependencies # +################ + +thread_dep = dependency('threads', include_type: 'system') + ########### # Library # ########### @@ -94,6 +118,7 @@ sources = files( 'src/digest.c', 'src/hash.c', 'src/ring.c', + 'src/sem.c', 'src/status.c', 'src/tree.c', ) @@ -139,6 +164,7 @@ libzix = library( meson.project_name() + library_suffix, sources, c_args: c_suppressions + library_c_args, + dependencies: [thread_dep], gnu_symbol_visibility: 'hidden', include_directories: include_dirs, install: true, @@ -160,6 +186,7 @@ pkg.generate( extra_cflags: extra_c_args, filebase: versioned_name, name: 'Zix', + requires: [thread_dep], subdirs: [versioned_name], version: meson.project_version(), ) diff --git a/src/sem.c b/src/sem.c new file mode 100644 index 0000000..066c592 --- /dev/null +++ b/src/sem.c @@ -0,0 +1,206 @@ +// Copyright 2012-2022 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "zix_config.h" + +#include "zix/common.h" +#include "zix/sem.h" + +#ifdef __APPLE__ +# include <mach/mach.h> +#elif defined(_WIN32) +# include <limits.h> +# include <windows.h> +#else +# include <errno.h> +# include <semaphore.h> +#endif + +#include <stdint.h> +#include <time.h> + +#ifdef __APPLE__ + +ZixStatus +zix_sem_init(ZixSem* sem, unsigned val) +{ + return semaphore_create( + mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, (int)val) + ? ZIX_STATUS_ERROR + : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_destroy(ZixSem* sem) +{ + return semaphore_destroy(mach_task_self(), sem->sem) ? ZIX_STATUS_ERROR + : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_post(ZixSem* sem) +{ + return semaphore_signal(sem->sem) ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_wait(ZixSem* sem) +{ + kern_return_t r = 0; + while ((r = semaphore_wait(sem->sem)) && r == KERN_ABORTED) { + // Interrupted, try again + } + + return r ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_try_wait(ZixSem* sem) +{ + const mach_timespec_t zero = {0, 0}; + const kern_return_t r = semaphore_timedwait(sem->sem, zero); + + return (r == KERN_SUCCESS) ? ZIX_STATUS_SUCCESS + : (r == KERN_OPERATION_TIMED_OUT) ? ZIX_STATUS_TIMEOUT + : ZIX_STATUS_ERROR; +} + +ZixStatus +zix_sem_timed_wait(ZixSem* sem, + const uint32_t seconds, + const uint32_t nanoseconds) +{ + const mach_timespec_t interval = {seconds, (clock_res_t)nanoseconds}; + const kern_return_t r = semaphore_timedwait(sem->sem, interval); + + return (r == KERN_SUCCESS) ? ZIX_STATUS_SUCCESS + : (r == KERN_OPERATION_TIMED_OUT) ? ZIX_STATUS_TIMEOUT + : ZIX_STATUS_ERROR; +} + +#elif defined(_WIN32) + +ZixStatus +zix_sem_init(ZixSem* sem, unsigned initial) +{ + sem->sem = CreateSemaphore(NULL, (LONG)initial, LONG_MAX, NULL); + return sem->sem ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; +} + +ZixStatus +zix_sem_destroy(ZixSem* sem) +{ + return CloseHandle(sem->sem) ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; +} + +ZixStatus +zix_sem_post(ZixSem* sem) +{ + return ReleaseSemaphore(sem->sem, 1, NULL) ? ZIX_STATUS_SUCCESS + : ZIX_STATUS_ERROR; +} + +ZixStatus +zix_sem_wait(ZixSem* sem) +{ + return WaitForSingleObject(sem->sem, INFINITE) == WAIT_OBJECT_0 + ? ZIX_STATUS_SUCCESS + : ZIX_STATUS_ERROR; +} + +ZixStatus +zix_sem_try_wait(ZixSem* sem) +{ + const DWORD r = WaitForSingleObject(sem->sem, 0); + + return (r == WAIT_OBJECT_0) ? ZIX_STATUS_SUCCESS + : (r == WAIT_TIMEOUT) ? ZIX_STATUS_TIMEOUT + : ZIX_STATUS_ERROR; +} + +ZixStatus +zix_sem_timed_wait(ZixSem* sem, + const uint32_t seconds, + const uint32_t nanoseconds) +{ + const uint32_t milliseconds = seconds * 1000U + nanoseconds / 1000000U; + const DWORD r = WaitForSingleObject(sem->sem, milliseconds); + + return (r == WAIT_OBJECT_0) ? ZIX_STATUS_SUCCESS + : (r == WAIT_TIMEOUT) ? ZIX_STATUS_TIMEOUT + : ZIX_STATUS_ERROR; +} + +#else /* !defined(__APPLE__) && !defined(_WIN32) */ + +ZixStatus +zix_sem_init(ZixSem* sem, unsigned initial) +{ + return sem_init(&sem->sem, 0, initial) ? zix_errno_status(errno) + : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_destroy(ZixSem* sem) +{ + return sem_destroy(&sem->sem) ? zix_errno_status(errno) : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_post(ZixSem* sem) +{ + return sem_post(&sem->sem) ? zix_errno_status(errno) : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_wait(ZixSem* sem) +{ + int r = 0; + while ((r = sem_wait(&sem->sem)) && errno == EINTR) { + // Interrupted, try again + } + + return r ? zix_errno_status(errno) : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_try_wait(ZixSem* sem) +{ + int r = 0; + while ((r = sem_trywait(&sem->sem)) && errno == EINTR) { + // Interrupted, try again + } + + return r ? (errno == EAGAIN ? ZIX_STATUS_TIMEOUT : zix_errno_status(errno)) + : ZIX_STATUS_SUCCESS; +} + +ZixStatus +zix_sem_timed_wait(ZixSem* sem, + const uint32_t seconds, + const uint32_t nanoseconds) +{ +# if !USE_CLOCK_GETTIME || !USE_SEM_TIMEDWAIT + return ZIX_STATUS_NOT_SUPPORTED; +# else + + struct timespec ts = {0, 0}; + + if (clock_gettime(CLOCK_REALTIME, &ts)) { + return ZIX_STATUS_ERROR; + } + + ts.tv_sec += (time_t)seconds; + ts.tv_nsec += (long)nanoseconds; + + int r = 0; + while ((r = sem_timedwait(&sem->sem, &ts)) && errno == EINTR) { + // Interrupted, try again + } + + return r ? (errno == ETIMEDOUT ? ZIX_STATUS_TIMEOUT : zix_errno_status(errno)) + : ZIX_STATUS_SUCCESS; +# endif +} + +#endif diff --git a/src/status.c b/src/status.c index cbb3d36..a0bb17e 100644 --- a/src/status.c +++ b/src/status.c @@ -29,6 +29,8 @@ zix_strerror(const ZixStatus status) return "Timeout"; case ZIX_STATUS_OVERFLOW: return "Overflow"; + case ZIX_STATUS_NOT_SUPPORTED: + return "Not supported"; } return "Unknown error"; } diff --git a/src/zix_config.h b/src/zix_config.h index 7be46e4..f4965f8 100644 --- a/src/zix_config.h +++ b/src/zix_config.h @@ -39,6 +39,15 @@ # endif # endif +// POSIX.1-2001: clock_gettime() +# ifndef HAVE_CLOCK_GETTIME +# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L +# define HAVE_CLOCK_GETTIME 1 +# else +# define HAVE_CLOCK_GETTIME 0 +# endif +# endif + // POSIX.1-2001: mlock() # ifndef HAVE_MLOCK # if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L @@ -57,6 +66,15 @@ # endif # endif +// POSIX.1-2001: sem_timedwait() +# ifndef HAVE_SEM_TIMEDWAIT +# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L +# define HAVE_SEM_TIMEDWAIT 1 +# else +# define HAVE_SEM_TIMEDWAIT 0 +# endif +# endif + #endif // !defined(ZIX_NO_DEFAULT_CONFIG) /* @@ -67,6 +85,12 @@ if the build system defines them all. */ +#if HAVE_CLOCK_GETTIME +# define USE_CLOCK_GETTIME 1 +#else +# define USE_CLOCK_GETTIME 0 +#endif + #if HAVE_MLOCK # define USE_MLOCK 1 #else @@ -79,4 +103,10 @@ # define USE_POSIX_MEMALIGN 0 #endif +#if HAVE_SEM_TIMEDWAIT +# define USE_SEM_TIMEDWAIT 1 +#else +# define USE_SEM_TIMEDWAIT 0 +#endif + #endif // ZIX_CONFIG_H diff --git a/test/test_status.c b/test/test_status.c index 486c210..6405ec8 100644 --- a/test/test_status.c +++ b/test/test_status.c @@ -37,7 +37,7 @@ test_strerror(void) const char* msg = zix_strerror(ZIX_STATUS_SUCCESS); assert(!strcmp(msg, "Success")); - for (int i = ZIX_STATUS_ERROR; i <= ZIX_STATUS_OVERFLOW; ++i) { + for (int i = ZIX_STATUS_ERROR; i <= ZIX_STATUS_NOT_SUPPORTED; ++i) { msg = zix_strerror((ZixStatus)i); assert(strcmp(msg, "Success")); } |