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