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