15142c2acSIngo Weinhold /* 2d6d439f3SHamish Morrison * Copyright 2015, Hamish Morrison, hamishm53@gmail.com. 324df6592SIngo Weinhold * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 45142c2acSIngo Weinhold * Distributed under the terms of the MIT License. 55142c2acSIngo Weinhold */ 65142c2acSIngo Weinhold 75142c2acSIngo Weinhold #include <semaphore.h> 85142c2acSIngo Weinhold 95142c2acSIngo Weinhold #include <errno.h> 105142c2acSIngo Weinhold #include <fcntl.h> 115142c2acSIngo Weinhold #include <stdarg.h> 125142c2acSIngo Weinhold #include <stdlib.h> 13f1fafe31SAugustin Cavalier #include <pthread.h> 145142c2acSIngo Weinhold 155142c2acSIngo Weinhold #include <OS.h> 165142c2acSIngo Weinhold 175142c2acSIngo Weinhold #include <AutoDeleter.h> 18ae901935SOliver Tappe #include <errno_private.h> 196b202f4eSIngo Weinhold #include <posix/realtime_sem_defs.h> 205142c2acSIngo Weinhold #include <syscall_utils.h> 215142c2acSIngo Weinhold #include <syscalls.h> 22d6d439f3SHamish Morrison #include <user_mutex_defs.h> 23d6d439f3SHamish Morrison 24d6d439f3SHamish Morrison 25d6d439f3SHamish Morrison #define SEM_TYPE_NAMED 1 26d6d439f3SHamish Morrison #define SEM_TYPE_UNNAMED 2 27*f64c46e6SAugustin Cavalier #define SEM_TYPE_UNNAMED_SHARED 3 28d6d439f3SHamish Morrison 29d6d439f3SHamish Morrison 30d6d439f3SHamish Morrison static int32 31d6d439f3SHamish Morrison atomic_add_if_greater(int32* value, int32 amount, int32 testValue) 32d6d439f3SHamish Morrison { 33d6d439f3SHamish Morrison int32 current = atomic_get(value); 34d6d439f3SHamish Morrison while (current > testValue) { 35d6d439f3SHamish Morrison int32 old = atomic_test_and_set(value, current + amount, current); 36d6d439f3SHamish Morrison if (old == current) 37d6d439f3SHamish Morrison return old; 38d6d439f3SHamish Morrison current = old; 39d6d439f3SHamish Morrison } 40d6d439f3SHamish Morrison return current; 41d6d439f3SHamish Morrison } 425142c2acSIngo Weinhold 435142c2acSIngo Weinhold 445142c2acSIngo Weinhold sem_t* 455142c2acSIngo Weinhold sem_open(const char* name, int openFlags,...) 465142c2acSIngo Weinhold { 475142c2acSIngo Weinhold if (name == NULL) { 48ae901935SOliver Tappe __set_errno(B_BAD_VALUE); 495142c2acSIngo Weinhold return SEM_FAILED; 505142c2acSIngo Weinhold } 515142c2acSIngo Weinhold 525142c2acSIngo Weinhold // get the mode and semaphore count parameters, if O_CREAT is specified 535142c2acSIngo Weinhold mode_t mode = 0; 545142c2acSIngo Weinhold unsigned semCount = 0; 555142c2acSIngo Weinhold 565142c2acSIngo Weinhold if ((openFlags & O_CREAT) != 0) { 575142c2acSIngo Weinhold va_list args; 585142c2acSIngo Weinhold va_start(args, openFlags); 595142c2acSIngo Weinhold mode = va_arg(args, mode_t); 605142c2acSIngo Weinhold semCount = va_arg(args, unsigned); 615142c2acSIngo Weinhold va_end(args); 62e8f1b18eSIngo Weinhold } else { 63e8f1b18eSIngo Weinhold // clear O_EXCL, if O_CREAT is not given 64e8f1b18eSIngo Weinhold openFlags &= ~O_EXCL; 655142c2acSIngo Weinhold } 665142c2acSIngo Weinhold 675142c2acSIngo Weinhold // Allocate a sem_t structure -- we don't know, whether this is the first 685142c2acSIngo Weinhold // call of this process to open the semaphore. If it is, we will keep the 695142c2acSIngo Weinhold // structure, otherwise we will delete it later. 705142c2acSIngo Weinhold sem_t* sem = (sem_t*)malloc(sizeof(sem_t)); 715142c2acSIngo Weinhold if (sem == NULL) { 72ae901935SOliver Tappe __set_errno(B_NO_MEMORY); 735142c2acSIngo Weinhold return SEM_FAILED; 745142c2acSIngo Weinhold } 75d6d439f3SHamish Morrison 76d6d439f3SHamish Morrison sem->type = SEM_TYPE_NAMED; 775142c2acSIngo Weinhold MemoryDeleter semDeleter(sem); 785142c2acSIngo Weinhold 795142c2acSIngo Weinhold // ask the kernel to open the semaphore 805142c2acSIngo Weinhold sem_t* usedSem; 815142c2acSIngo Weinhold status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount, 825142c2acSIngo Weinhold sem, &usedSem); 835142c2acSIngo Weinhold if (error != B_OK) { 84ae901935SOliver Tappe __set_errno(error); 855142c2acSIngo Weinhold return SEM_FAILED; 865142c2acSIngo Weinhold } 875142c2acSIngo Weinhold 885142c2acSIngo Weinhold if (usedSem == sem) 895142c2acSIngo Weinhold semDeleter.Detach(); 905142c2acSIngo Weinhold 915142c2acSIngo Weinhold return usedSem; 925142c2acSIngo Weinhold } 935142c2acSIngo Weinhold 945142c2acSIngo Weinhold 955142c2acSIngo Weinhold int 965142c2acSIngo Weinhold sem_close(sem_t* semaphore) 975142c2acSIngo Weinhold { 985142c2acSIngo Weinhold sem_t* deleteSem = NULL; 99d6d439f3SHamish Morrison status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id, 100d6d439f3SHamish Morrison &deleteSem); 1015142c2acSIngo Weinhold if (error == B_OK) 1025142c2acSIngo Weinhold free(deleteSem); 1035142c2acSIngo Weinhold 1045142c2acSIngo Weinhold RETURN_AND_SET_ERRNO(error); 1055142c2acSIngo Weinhold } 1065142c2acSIngo Weinhold 1075142c2acSIngo Weinhold 1085142c2acSIngo Weinhold int 1095142c2acSIngo Weinhold sem_unlink(const char* name) 1105142c2acSIngo Weinhold { 1115142c2acSIngo Weinhold RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name)); 1125142c2acSIngo Weinhold } 1135142c2acSIngo Weinhold 1145142c2acSIngo Weinhold 1155142c2acSIngo Weinhold int 1165142c2acSIngo Weinhold sem_init(sem_t* semaphore, int shared, unsigned value) 1175142c2acSIngo Weinhold { 118*f64c46e6SAugustin Cavalier semaphore->type = shared ? SEM_TYPE_UNNAMED_SHARED : SEM_TYPE_UNNAMED; 119d6d439f3SHamish Morrison semaphore->u.unnamed_sem = value; 120d6d439f3SHamish Morrison return 0; 1215142c2acSIngo Weinhold } 1225142c2acSIngo Weinhold 1235142c2acSIngo Weinhold 1245142c2acSIngo Weinhold int 1255142c2acSIngo Weinhold sem_destroy(sem_t* semaphore) 1265142c2acSIngo Weinhold { 127*f64c46e6SAugustin Cavalier if (semaphore->type != SEM_TYPE_UNNAMED && semaphore->type != SEM_TYPE_UNNAMED_SHARED) 128d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(EINVAL); 129d6d439f3SHamish Morrison 130d6d439f3SHamish Morrison return 0; 131d6d439f3SHamish Morrison } 132d6d439f3SHamish Morrison 133d6d439f3SHamish Morrison 134d6d439f3SHamish Morrison static int 135a866e2d9SJérôme Duval unnamed_sem_post(sem_t* semaphore) 136a866e2d9SJérôme Duval { 137d6d439f3SHamish Morrison int32* sem = (int32*)&semaphore->u.unnamed_sem; 138d6d439f3SHamish Morrison int32 oldValue = atomic_add_if_greater(sem, 1, -1); 139d6d439f3SHamish Morrison if (oldValue > -1) 140d6d439f3SHamish Morrison return 0; 141d6d439f3SHamish Morrison 142*f64c46e6SAugustin Cavalier uint32 flags = 0; 143*f64c46e6SAugustin Cavalier if (semaphore->type == SEM_TYPE_UNNAMED_SHARED) 144*f64c46e6SAugustin Cavalier flags |= B_USER_MUTEX_SHARED; 145*f64c46e6SAugustin Cavalier 146*f64c46e6SAugustin Cavalier return _kern_mutex_sem_release(sem, flags); 147d6d439f3SHamish Morrison } 148d6d439f3SHamish Morrison 149d6d439f3SHamish Morrison 150d6d439f3SHamish Morrison static int 151a866e2d9SJérôme Duval unnamed_sem_trywait(sem_t* semaphore) 152a866e2d9SJérôme Duval { 153d6d439f3SHamish Morrison int32* sem = (int32*)&semaphore->u.unnamed_sem; 154d6d439f3SHamish Morrison int32 oldValue = atomic_add_if_greater(sem, -1, 0); 155d6d439f3SHamish Morrison if (oldValue > 0) 156d6d439f3SHamish Morrison return 0; 157d6d439f3SHamish Morrison 158d6d439f3SHamish Morrison return EAGAIN; 159d6d439f3SHamish Morrison } 160d6d439f3SHamish Morrison 161d6d439f3SHamish Morrison 162d6d439f3SHamish Morrison static int 163a866e2d9SJérôme Duval unnamed_sem_timedwait(sem_t* semaphore, clockid_t clock_id, 164a866e2d9SJérôme Duval const struct timespec* timeout) 165a866e2d9SJérôme Duval { 166d6d439f3SHamish Morrison int32* sem = (int32*)&semaphore->u.unnamed_sem; 167d6d439f3SHamish Morrison 168d6d439f3SHamish Morrison bigtime_t timeoutMicros = B_INFINITE_TIMEOUT; 169a866e2d9SJérôme Duval uint32 flags = 0; 170*f64c46e6SAugustin Cavalier if (semaphore->type == SEM_TYPE_UNNAMED_SHARED) 171*f64c46e6SAugustin Cavalier flags |= B_USER_MUTEX_SHARED; 172d6d439f3SHamish Morrison if (timeout != NULL) { 173d6d439f3SHamish Morrison timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000 174d6d439f3SHamish Morrison + timeout->tv_nsec / 1000; 175a866e2d9SJérôme Duval switch (clock_id) { 176a866e2d9SJérôme Duval case CLOCK_REALTIME: 177a866e2d9SJérôme Duval flags = B_ABSOLUTE_REAL_TIME_TIMEOUT; 178a866e2d9SJérôme Duval break; 179a866e2d9SJérôme Duval case CLOCK_MONOTONIC: 180a866e2d9SJérôme Duval flags = B_ABSOLUTE_TIMEOUT; 181a866e2d9SJérôme Duval break; 182a866e2d9SJérôme Duval default: 183a866e2d9SJérôme Duval return EINVAL; 184a866e2d9SJérôme Duval } 185d6d439f3SHamish Morrison } 186d6d439f3SHamish Morrison 187d6d439f3SHamish Morrison int result = unnamed_sem_trywait(semaphore); 188d6d439f3SHamish Morrison if (result == 0) 189d6d439f3SHamish Morrison return 0; 190d6d439f3SHamish Morrison 191a866e2d9SJérôme Duval return _kern_mutex_sem_acquire(sem, NULL, flags, timeoutMicros); 1925142c2acSIngo Weinhold } 1935142c2acSIngo Weinhold 1945142c2acSIngo Weinhold 1955142c2acSIngo Weinhold int 1965142c2acSIngo Weinhold sem_post(sem_t* semaphore) 1975142c2acSIngo Weinhold { 198d6d439f3SHamish Morrison status_t error; 199d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED) 200d6d439f3SHamish Morrison error = _kern_realtime_sem_post(semaphore->u.named_sem_id); 201d6d439f3SHamish Morrison else 202d6d439f3SHamish Morrison error = unnamed_sem_post(semaphore); 203d6d439f3SHamish Morrison 204d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(error); 2055142c2acSIngo Weinhold } 2065142c2acSIngo Weinhold 2075142c2acSIngo Weinhold 208d6d439f3SHamish Morrison static int 209a866e2d9SJérôme Duval named_sem_timedwait(sem_t* semaphore, clockid_t clock_id, 210a866e2d9SJérôme Duval const struct timespec* timeout) 2115142c2acSIngo Weinhold { 212f1a3e05dSJérôme Duval if (timeout != NULL 213f1a3e05dSJérôme Duval && (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) { 214a866e2d9SJérôme Duval status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 215a866e2d9SJérôme Duval B_RELATIVE_TIMEOUT, 0); 2163cdae651SSam Toyer if (err == B_WOULD_BLOCK) 2173cdae651SSam Toyer err = EINVAL; 2183cdae651SSam Toyer // do nothing, return err as it is. 219d6d439f3SHamish Morrison return err; 2203cdae651SSam Toyer } 2215142c2acSIngo Weinhold 2223cdae651SSam Toyer bigtime_t timeoutMicros = B_INFINITE_TIMEOUT; 223a866e2d9SJérôme Duval uint32 flags = 0; 2243cdae651SSam Toyer if (timeout != NULL) { 2253cdae651SSam Toyer timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000 2263cdae651SSam Toyer + timeout->tv_nsec / 1000; 227a866e2d9SJérôme Duval switch (clock_id) { 228a866e2d9SJérôme Duval case CLOCK_REALTIME: 229a866e2d9SJérôme Duval flags = B_ABSOLUTE_REAL_TIME_TIMEOUT; 230a866e2d9SJérôme Duval break; 231a866e2d9SJérôme Duval case CLOCK_MONOTONIC: 232a866e2d9SJérôme Duval flags = B_ABSOLUTE_TIMEOUT; 233a866e2d9SJérôme Duval break; 234a866e2d9SJérôme Duval default: 235a866e2d9SJérôme Duval return EINVAL; 2363cdae651SSam Toyer } 237a866e2d9SJérôme Duval } 238a866e2d9SJérôme Duval status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, flags, 239d6d439f3SHamish Morrison timeoutMicros); 2403cdae651SSam Toyer if (err == B_WOULD_BLOCK) 2413cdae651SSam Toyer err = ETIMEDOUT; 2423cdae651SSam Toyer 243d6d439f3SHamish Morrison return err; 2445142c2acSIngo Weinhold } 2455142c2acSIngo Weinhold 2465142c2acSIngo Weinhold 2475142c2acSIngo Weinhold int 2485142c2acSIngo Weinhold sem_trywait(sem_t* semaphore) 2495142c2acSIngo Weinhold { 250d6d439f3SHamish Morrison status_t error; 251a866e2d9SJérôme Duval if (semaphore->type == SEM_TYPE_NAMED) { 252a866e2d9SJérôme Duval error = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 253a866e2d9SJérôme Duval B_RELATIVE_TIMEOUT, 0); 254a866e2d9SJérôme Duval } else 255d6d439f3SHamish Morrison error = unnamed_sem_trywait(semaphore); 256d6d439f3SHamish Morrison 257d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(error); 2585142c2acSIngo Weinhold } 2595142c2acSIngo Weinhold 2605142c2acSIngo Weinhold 2615142c2acSIngo Weinhold int 2625142c2acSIngo Weinhold sem_wait(sem_t* semaphore) 2635142c2acSIngo Weinhold { 264d6d439f3SHamish Morrison status_t error; 265d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED) 266a866e2d9SJérôme Duval error = named_sem_timedwait(semaphore, CLOCK_REALTIME, NULL); 267d6d439f3SHamish Morrison else 268a866e2d9SJérôme Duval error = unnamed_sem_timedwait(semaphore, CLOCK_REALTIME, NULL); 269d6d439f3SHamish Morrison 270d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO_TEST_CANCEL(error); 271d6d439f3SHamish Morrison } 272d6d439f3SHamish Morrison 273d6d439f3SHamish Morrison 274d6d439f3SHamish Morrison int 275a866e2d9SJérôme Duval sem_clockwait(sem_t* semaphore, clockid_t clock_id, const struct timespec* abstime) 276d6d439f3SHamish Morrison { 277d6d439f3SHamish Morrison status_t error; 278d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED) 279a866e2d9SJérôme Duval error = named_sem_timedwait(semaphore, clock_id, abstime); 280d6d439f3SHamish Morrison else 281a866e2d9SJérôme Duval error = unnamed_sem_timedwait(semaphore, clock_id, abstime); 282d6d439f3SHamish Morrison 283d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO_TEST_CANCEL(error); 2845142c2acSIngo Weinhold } 2855142c2acSIngo Weinhold 2865142c2acSIngo Weinhold 2875142c2acSIngo Weinhold int 288a866e2d9SJérôme Duval sem_timedwait(sem_t* semaphore, const struct timespec* abstime) 289a866e2d9SJérôme Duval { 290a866e2d9SJérôme Duval return sem_clockwait(semaphore, CLOCK_REALTIME, abstime); 291a866e2d9SJérôme Duval } 292a866e2d9SJérôme Duval 293a866e2d9SJérôme Duval 294a866e2d9SJérôme Duval int 2955142c2acSIngo Weinhold sem_getvalue(sem_t* semaphore, int* value) 2965142c2acSIngo Weinhold { 297d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED) { 298d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value( 299d6d439f3SHamish Morrison semaphore->u.named_sem_id, value)); 300d6d439f3SHamish Morrison } else { 301d6d439f3SHamish Morrison *value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem; 302d6d439f3SHamish Morrison return 0; 303d6d439f3SHamish Morrison } 3045142c2acSIngo Weinhold } 305