// Copyright 2012-2022 David Robillard // SPDX-License-Identifier: ISC #include "zix/sem.h" #include "errno_status.h" #include "zix_config.h" #include "zix/status.h" #ifdef __APPLE__ # include #elif defined(_WIN32) # include # include #else # include # include #endif #include #include #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_UNAVAILABLE : 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_UNAVAILABLE : 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 zix_errno_status_if(sem_init(&sem->sem, 0, initial)); } ZixStatus zix_sem_destroy(ZixSem* sem) { return zix_errno_status_if(sem_destroy(&sem->sem)); } ZixStatus zix_sem_post(ZixSem* sem) { return zix_errno_status_if(sem_post(&sem->sem)); } ZixStatus zix_sem_wait(ZixSem* sem) { int r = 0; while ((r = sem_wait(&sem->sem)) && errno == EINTR) { // Interrupted, try again } return zix_errno_status_if(r); } ZixStatus zix_sem_try_wait(ZixSem* sem) { int r = 0; while ((r = sem_trywait(&sem->sem)) && errno == EINTR) { // Interrupted, try again } return zix_errno_status_if(r); } ZixStatus zix_sem_timed_wait(ZixSem* sem, const uint32_t seconds, const uint32_t nanoseconds) { # define NS_PER_SECOND 1000000000L # if !USE_CLOCK_GETTIME || !USE_SEM_TIMEDWAIT return ZIX_STATUS_NOT_SUPPORTED; # else struct timespec ts = {0, 0}; int r = 0; if (!(r = clock_gettime(CLOCK_REALTIME, &ts))) { ts.tv_sec += (time_t)seconds; ts.tv_nsec += (long)nanoseconds; if (ts.tv_nsec >= NS_PER_SECOND) { ts.tv_nsec -= NS_PER_SECOND; ts.tv_sec++; } while ((r = sem_timedwait(&sem->sem, &ts)) && errno == EINTR) { // Interrupted, try again } } return zix_errno_status_if(r); # endif # undef NS_PER_SECOND } #endif