1/* 2 * Copyright 2015, Hamish Morrison, hamishm53@gmail.com. 3 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7#include <semaphore.h> 8 9#include <errno.h> 10#include <fcntl.h> 11#include <stdarg.h> 12#include <stdlib.h> 13#include <pthread.h> 14 15#include <OS.h> 16 17#include <AutoDeleter.h> 18#include <errno_private.h> 19#include <posix/realtime_sem_defs.h> 20#include <syscall_utils.h> 21#include <syscalls.h> 22#include <user_mutex_defs.h> 23 24 25#define SEM_TYPE_NAMED 1 26#define SEM_TYPE_UNNAMED 2 27#define SEM_TYPE_UNNAMED_SHARED 3 28 29 30static int32 31atomic_add_if_greater(int32* value, int32 amount, int32 testValue) 32{ 33 int32 current = atomic_get(value); 34 while (current > testValue) { 35 int32 old = atomic_test_and_set(value, current + amount, current); 36 if (old == current) 37 return old; 38 current = old; 39 } 40 return current; 41} 42 43 44sem_t* 45sem_open(const char* name, int openFlags,...) 46{ 47 if (name == NULL) { 48 __set_errno(B_BAD_VALUE); 49 return SEM_FAILED; 50 } 51 52 // get the mode and semaphore count parameters, if O_CREAT is specified 53 mode_t mode = 0; 54 unsigned semCount = 0; 55 56 if ((openFlags & O_CREAT) != 0) { 57 va_list args; 58 va_start(args, openFlags); 59 mode = va_arg(args, mode_t); 60 semCount = va_arg(args, unsigned); 61 va_end(args); 62 } else { 63 // clear O_EXCL, if O_CREAT is not given 64 openFlags &= ~O_EXCL; 65 } 66 67 // Allocate a sem_t structure -- we don't know, whether this is the first 68 // call of this process to open the semaphore. If it is, we will keep the 69 // structure, otherwise we will delete it later. 70 sem_t* sem = (sem_t*)malloc(sizeof(sem_t)); 71 if (sem == NULL) { 72 __set_errno(B_NO_MEMORY); 73 return SEM_FAILED; 74 } 75 76 sem->type = SEM_TYPE_NAMED; 77 MemoryDeleter semDeleter(sem); 78 79 // ask the kernel to open the semaphore 80 sem_t* usedSem; 81 status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount, 82 sem, &usedSem); 83 if (error != B_OK) { 84 __set_errno(error); 85 return SEM_FAILED; 86 } 87 88 if (usedSem == sem) 89 semDeleter.Detach(); 90 91 return usedSem; 92} 93 94 95int 96sem_close(sem_t* semaphore) 97{ 98 sem_t* deleteSem = NULL; 99 status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id, 100 &deleteSem); 101 if (error == B_OK) 102 free(deleteSem); 103 104 RETURN_AND_SET_ERRNO(error); 105} 106 107 108int 109sem_unlink(const char* name) 110{ 111 RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name)); 112} 113 114 115int 116sem_init(sem_t* semaphore, int shared, unsigned value) 117{ 118 semaphore->type = shared ? SEM_TYPE_UNNAMED_SHARED : SEM_TYPE_UNNAMED; 119 semaphore->u.unnamed_sem = value; 120 return 0; 121} 122 123 124int 125sem_destroy(sem_t* semaphore) 126{ 127 if (semaphore->type != SEM_TYPE_UNNAMED && semaphore->type != SEM_TYPE_UNNAMED_SHARED) 128 RETURN_AND_SET_ERRNO(EINVAL); 129 130 return 0; 131} 132 133 134static int 135unnamed_sem_post(sem_t* semaphore) 136{ 137 int32* sem = (int32*)&semaphore->u.unnamed_sem; 138 int32 oldValue = atomic_add_if_greater(sem, 1, -1); 139 if (oldValue > -1) 140 return 0; 141 142 uint32 flags = 0; 143 if (semaphore->type == SEM_TYPE_UNNAMED_SHARED) 144 flags |= B_USER_MUTEX_SHARED; 145 146 return _kern_mutex_sem_release(sem, flags); 147} 148 149 150static int 151unnamed_sem_trywait(sem_t* semaphore) 152{ 153 int32* sem = (int32*)&semaphore->u.unnamed_sem; 154 int32 oldValue = atomic_add_if_greater(sem, -1, 0); 155 if (oldValue > 0) 156 return 0; 157 158 return EAGAIN; 159} 160 161 162static int 163unnamed_sem_timedwait(sem_t* semaphore, clockid_t clock_id, 164 const struct timespec* timeout) 165{ 166 int32* sem = (int32*)&semaphore->u.unnamed_sem; 167 168 bigtime_t timeoutMicros = B_INFINITE_TIMEOUT; 169 uint32 flags = 0; 170 if (semaphore->type == SEM_TYPE_UNNAMED_SHARED) 171 flags |= B_USER_MUTEX_SHARED; 172 if (timeout != NULL) { 173 timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000 174 + timeout->tv_nsec / 1000; 175 switch (clock_id) { 176 case CLOCK_REALTIME: 177 flags = B_ABSOLUTE_REAL_TIME_TIMEOUT; 178 break; 179 case CLOCK_MONOTONIC: 180 flags = B_ABSOLUTE_TIMEOUT; 181 break; 182 default: 183 return EINVAL; 184 } 185 } 186 187 int result = unnamed_sem_trywait(semaphore); 188 if (result == 0) 189 return 0; 190 191 return _kern_mutex_sem_acquire(sem, NULL, flags, timeoutMicros); 192} 193 194 195int 196sem_post(sem_t* semaphore) 197{ 198 status_t error; 199 if (semaphore->type == SEM_TYPE_NAMED) 200 error = _kern_realtime_sem_post(semaphore->u.named_sem_id); 201 else 202 error = unnamed_sem_post(semaphore); 203 204 RETURN_AND_SET_ERRNO(error); 205} 206 207 208static int 209named_sem_timedwait(sem_t* semaphore, clockid_t clock_id, 210 const struct timespec* timeout) 211{ 212 if (timeout != NULL 213 && (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) { 214 status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 215 B_RELATIVE_TIMEOUT, 0); 216 if (err == B_WOULD_BLOCK) 217 err = EINVAL; 218 // do nothing, return err as it is. 219 return err; 220 } 221 222 bigtime_t timeoutMicros = B_INFINITE_TIMEOUT; 223 uint32 flags = 0; 224 if (timeout != NULL) { 225 timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000 226 + timeout->tv_nsec / 1000; 227 switch (clock_id) { 228 case CLOCK_REALTIME: 229 flags = B_ABSOLUTE_REAL_TIME_TIMEOUT; 230 break; 231 case CLOCK_MONOTONIC: 232 flags = B_ABSOLUTE_TIMEOUT; 233 break; 234 default: 235 return EINVAL; 236 } 237 } 238 status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, flags, 239 timeoutMicros); 240 if (err == B_WOULD_BLOCK) 241 err = ETIMEDOUT; 242 243 return err; 244} 245 246 247int 248sem_trywait(sem_t* semaphore) 249{ 250 status_t error; 251 if (semaphore->type == SEM_TYPE_NAMED) { 252 error = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 253 B_RELATIVE_TIMEOUT, 0); 254 } else 255 error = unnamed_sem_trywait(semaphore); 256 257 RETURN_AND_SET_ERRNO(error); 258} 259 260 261int 262sem_wait(sem_t* semaphore) 263{ 264 status_t error; 265 if (semaphore->type == SEM_TYPE_NAMED) 266 error = named_sem_timedwait(semaphore, CLOCK_REALTIME, NULL); 267 else 268 error = unnamed_sem_timedwait(semaphore, CLOCK_REALTIME, NULL); 269 270 RETURN_AND_SET_ERRNO_TEST_CANCEL(error); 271} 272 273 274int 275sem_clockwait(sem_t* semaphore, clockid_t clock_id, const struct timespec* abstime) 276{ 277 status_t error; 278 if (semaphore->type == SEM_TYPE_NAMED) 279 error = named_sem_timedwait(semaphore, clock_id, abstime); 280 else 281 error = unnamed_sem_timedwait(semaphore, clock_id, abstime); 282 283 RETURN_AND_SET_ERRNO_TEST_CANCEL(error); 284} 285 286 287int 288sem_timedwait(sem_t* semaphore, const struct timespec* abstime) 289{ 290 return sem_clockwait(semaphore, CLOCK_REALTIME, abstime); 291} 292 293 294int 295sem_getvalue(sem_t* semaphore, int* value) 296{ 297 if (semaphore->type == SEM_TYPE_NAMED) { 298 RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value( 299 semaphore->u.named_sem_id, value)); 300 } else { 301 *value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem; 302 return 0; 303 } 304} 305