From 564b957cc31a33124a935ea8678e5bbb710fc082 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 10 Sep 2014 03:09:41 +0200 Subject: osdep: add POSIX semaphore emulation for OSX OSX is POSIX conformant, but it's a sad joke: it provides the prototype as the standard demands, but they're empty wrappers, and all functions just return ENOSYS. Emulate them similar to how osdep/io.h emulate filesystem functions on Windows. By including the header, working sem_* functions become available. To make it async-signal safe, use a pipe for wakeup (write() is AS-safe, but mutexes can't be). Actually I'm not sure anymore if we really need AS-safety, but for now the emulation can do it. On Linux, the system provides a far more efficient and robust implementation. We definitely want to avoid using the emulation if possible, so this code is active on OSX only. For convenience we always build the source file though, even if the implementation is disabled and no actual code is generated. (Linux provides working semaphores, but is formally not POSIX conformant. On OSX it's the opposite. Is POSIX a complete joke?) --- osdep/semaphore_osx.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 osdep/semaphore_osx.c (limited to 'osdep/semaphore_osx.c') diff --git a/osdep/semaphore_osx.c b/osdep/semaphore_osx.c new file mode 100644 index 0000000000..72173f9564 --- /dev/null +++ b/osdep/semaphore_osx.c @@ -0,0 +1,121 @@ +/* Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "osdep/io.h" + +#include "semaphore.h" + +#ifdef MP_SEMAPHORE_EMULATION + +int mp_sem_init(mp_sem_t *sem, int pshared, unsigned int value) +{ + if (pshared) { + errno = ENOSYS; + return -1; + } + if (value > INT_MAX) { + errno = EINVAL; + return -1; + } + if (mp_make_wakeup_pipe(sem->wakeup_pipe) < 0) + return -1; + sem->count = 0; + pthread_mutex_init(&sem->lock, NULL); + return 0; +} + +int mp_sem_wait(mp_sem_t *sem) +{ + return mp_sem_timedwait(sem, NULL); +} + +int mp_sem_trywait(mp_sem_t *sem) +{ + int r = -1; + pthread_mutex_lock(&sem->lock); + if (sem->count == 0) { + char buf[1024]; + ssize_t s = read(sem->wakeup_pipe[0], buf, sizeof(buf)); + if (s > 0 && s <= INT_MAX - sem->count) // can't handle overflows correctly + sem->count += s; + } + if (sem->count > 0) { + sem->count -= 1; + r = 0; + } + pthread_mutex_unlock(&sem->lock); + if (r < 0) + errno = EAGAIN; + return r; +} + +int mp_sem_timedwait(mp_sem_t *sem, const struct timespec *abs_timeout) +{ + while (1) { + if (!mp_sem_trywait(sem)) + return 0; + + int timeout_ms = -1; + if (abs_timeout) { + timeout_ms = 0; + + // OSX does not provide clock_gettime() either. + struct timeval tv; + gettimeofday(&tv, NULL); + + if (abs_timeout->tv_sec >= tv.tv_sec) { + long long msec = (abs_timeout->tv_sec - tv.tv_sec) * 1000LL + + abs_timeout->tv_nsec / 1000LL / 1000LL - tv.tv_usec / 1000LL; + if (msec > INT_MAX) + msec = INT_MAX; + if (msec < 0) + msec = 0; + timeout_ms = msec; + } + } + struct pollfd fd = {.fd = sem->wakeup_pipe[0], .events = POLLIN}; + int r = poll(&fd, 1, timeout_ms); + if (r < 0) + return -1; + if (r == 0) { + errno = ETIMEDOUT; + return -1; + } + } +} + +int mp_sem_post(mp_sem_t *sem) +{ + if (write(sem->wakeup_pipe[1], &(char){0}, 1) == 1) + return 0; + // Actually we can't handle overflow fully correctly, because we can't + // check sem->count atomically, while still being AS-safe. + errno = EOVERFLOW; + return -1; +} + +int mp_sem_destroy(mp_sem_t *sem) +{ + close(sem->wakeup_pipe[0]); + close(sem->wakeup_pipe[1]); + pthread_mutex_destroy(&sem->lock); + return 0; +} + +#endif -- cgit v1.2.3