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 30 static int32 31 atomic_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 44 sem_t* 45 sem_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 95 int 96 sem_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 108 int 109 sem_unlink(const char* name) 110 { 111 RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name)); 112 } 113 114 115 int 116 sem_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 124 int 125 sem_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 134 static int 135 unnamed_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 150 static int 151 unnamed_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 162 static int 163 unnamed_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 195 int 196 sem_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 208 static int 209 named_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 247 int 248 sem_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 261 int 262 sem_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 274 int 275 sem_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 287 int 288 sem_timedwait(sem_t* semaphore, const struct timespec* abstime) 289 { 290 return sem_clockwait(semaphore, CLOCK_REALTIME, abstime); 291 } 292 293 294 int 295 sem_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