headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / posix / semaphore.cpp
blobfc4b0fc9216c2f079582637af41d9fcf57b0beef
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 */
7 #include <semaphore.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdarg.h>
12 #include <stdlib.h>
14 #include <OS.h>
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>
24 #define SEM_TYPE_NAMED 1
25 #define SEM_TYPE_UNNAMED 2
28 static int32
29 atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
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;
38 return current;
42 sem_t*
43 sem_open(const char* name, int openFlags,...)
45 if (name == NULL) {
46 __set_errno(B_BAD_VALUE);
47 return SEM_FAILED;
50 // get the mode and semaphore count parameters, if O_CREAT is specified
51 mode_t mode = 0;
52 unsigned semCount = 0;
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;
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;
74 sem->type = SEM_TYPE_NAMED;
75 MemoryDeleter semDeleter(sem);
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;
86 if (usedSem == sem)
87 semDeleter.Detach();
89 return usedSem;
93 int
94 sem_close(sem_t* semaphore)
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);
102 RETURN_AND_SET_ERRNO(error);
107 sem_unlink(const char* name)
109 RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
114 sem_init(sem_t* semaphore, int shared, unsigned value)
116 semaphore->type = SEM_TYPE_UNNAMED;
117 semaphore->u.unnamed_sem = value;
118 return 0;
123 sem_destroy(sem_t* semaphore)
125 if (semaphore->type != SEM_TYPE_UNNAMED)
126 RETURN_AND_SET_ERRNO(EINVAL);
128 return 0;
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;
139 return _kern_mutex_sem_release(sem);
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;
150 return EAGAIN;
154 static int
155 unnamed_sem_timedwait(sem_t* semaphore, const struct timespec* timeout) {
156 int32* sem = (int32*)&semaphore->u.unnamed_sem;
158 bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
159 if (timeout != NULL) {
160 timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
161 + timeout->tv_nsec / 1000;
164 int result = unnamed_sem_trywait(semaphore);
165 if (result == 0)
166 return 0;
168 return _kern_mutex_sem_acquire(sem, NULL,
169 timeoutMicros == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT,
170 timeoutMicros);
175 sem_post(sem_t* semaphore)
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);
183 RETURN_AND_SET_ERRNO(error);
187 static int
188 named_sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
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;
199 bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
200 if (timeout != NULL) {
201 timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
202 + timeout->tv_nsec / 1000;
204 status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
205 timeoutMicros);
206 if (err == B_WOULD_BLOCK)
207 err = ETIMEDOUT;
209 return err;
214 sem_trywait(sem_t* semaphore)
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);
222 RETURN_AND_SET_ERRNO(error);
227 sem_wait(sem_t* semaphore)
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);
235 RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
240 sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
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);
248 RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
253 sem_getvalue(sem_t* semaphore, int* value)
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;