summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/zix/common.h1
-rw-r--r--include/zix/sem.h202
-rw-r--r--meson.build27
-rw-r--r--src/sem.c206
-rw-r--r--src/status.c2
-rw-r--r--src/zix_config.h30
-rw-r--r--test/test_status.c2
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"));
}