15142c2acSIngo Weinhold /*
2d6d439f3SHamish Morrison * Copyright 2015, Hamish Morrison, hamishm53@gmail.com.
324df6592SIngo Weinhold * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
45142c2acSIngo Weinhold * Distributed under the terms of the MIT License.
55142c2acSIngo Weinhold */
65142c2acSIngo Weinhold
75142c2acSIngo Weinhold #include <semaphore.h>
85142c2acSIngo Weinhold
95142c2acSIngo Weinhold #include <errno.h>
105142c2acSIngo Weinhold #include <fcntl.h>
115142c2acSIngo Weinhold #include <stdarg.h>
125142c2acSIngo Weinhold #include <stdlib.h>
13f1fafe31SAugustin Cavalier #include <pthread.h>
145142c2acSIngo Weinhold
155142c2acSIngo Weinhold #include <OS.h>
165142c2acSIngo Weinhold
175142c2acSIngo Weinhold #include <AutoDeleter.h>
18ae901935SOliver Tappe #include <errno_private.h>
196b202f4eSIngo Weinhold #include <posix/realtime_sem_defs.h>
205142c2acSIngo Weinhold #include <syscall_utils.h>
215142c2acSIngo Weinhold #include <syscalls.h>
22*c6355861SAugustin Cavalier #include <time_private.h>
23d6d439f3SHamish Morrison #include <user_mutex_defs.h>
24d6d439f3SHamish Morrison
25d6d439f3SHamish Morrison
26d6d439f3SHamish Morrison #define SEM_TYPE_NAMED 1
27d6d439f3SHamish Morrison #define SEM_TYPE_UNNAMED 2
28f64c46e6SAugustin Cavalier #define SEM_TYPE_UNNAMED_SHARED 3
29d6d439f3SHamish Morrison
30d6d439f3SHamish Morrison
31d6d439f3SHamish Morrison static int32
atomic_add_if_greater(int32 * value,int32 amount,int32 testValue)32d6d439f3SHamish Morrison atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
33d6d439f3SHamish Morrison {
34d6d439f3SHamish Morrison int32 current = atomic_get(value);
35d6d439f3SHamish Morrison while (current > testValue) {
36d6d439f3SHamish Morrison int32 old = atomic_test_and_set(value, current + amount, current);
37d6d439f3SHamish Morrison if (old == current)
38d6d439f3SHamish Morrison return old;
39d6d439f3SHamish Morrison current = old;
40d6d439f3SHamish Morrison }
41d6d439f3SHamish Morrison return current;
42d6d439f3SHamish Morrison }
435142c2acSIngo Weinhold
445142c2acSIngo Weinhold
455142c2acSIngo Weinhold sem_t*
sem_open(const char * name,int openFlags,...)465142c2acSIngo Weinhold sem_open(const char* name, int openFlags,...)
475142c2acSIngo Weinhold {
485142c2acSIngo Weinhold if (name == NULL) {
49ae901935SOliver Tappe __set_errno(B_BAD_VALUE);
505142c2acSIngo Weinhold return SEM_FAILED;
515142c2acSIngo Weinhold }
525142c2acSIngo Weinhold
535142c2acSIngo Weinhold // get the mode and semaphore count parameters, if O_CREAT is specified
545142c2acSIngo Weinhold mode_t mode = 0;
555142c2acSIngo Weinhold unsigned semCount = 0;
565142c2acSIngo Weinhold
575142c2acSIngo Weinhold if ((openFlags & O_CREAT) != 0) {
585142c2acSIngo Weinhold va_list args;
595142c2acSIngo Weinhold va_start(args, openFlags);
605142c2acSIngo Weinhold mode = va_arg(args, mode_t);
615142c2acSIngo Weinhold semCount = va_arg(args, unsigned);
625142c2acSIngo Weinhold va_end(args);
63e8f1b18eSIngo Weinhold } else {
64e8f1b18eSIngo Weinhold // clear O_EXCL, if O_CREAT is not given
65e8f1b18eSIngo Weinhold openFlags &= ~O_EXCL;
665142c2acSIngo Weinhold }
675142c2acSIngo Weinhold
685142c2acSIngo Weinhold // Allocate a sem_t structure -- we don't know, whether this is the first
695142c2acSIngo Weinhold // call of this process to open the semaphore. If it is, we will keep the
705142c2acSIngo Weinhold // structure, otherwise we will delete it later.
715142c2acSIngo Weinhold sem_t* sem = (sem_t*)malloc(sizeof(sem_t));
725142c2acSIngo Weinhold if (sem == NULL) {
73ae901935SOliver Tappe __set_errno(B_NO_MEMORY);
745142c2acSIngo Weinhold return SEM_FAILED;
755142c2acSIngo Weinhold }
76d6d439f3SHamish Morrison
77d6d439f3SHamish Morrison sem->type = SEM_TYPE_NAMED;
785142c2acSIngo Weinhold MemoryDeleter semDeleter(sem);
795142c2acSIngo Weinhold
805142c2acSIngo Weinhold // ask the kernel to open the semaphore
815142c2acSIngo Weinhold sem_t* usedSem;
825142c2acSIngo Weinhold status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount,
835142c2acSIngo Weinhold sem, &usedSem);
845142c2acSIngo Weinhold if (error != B_OK) {
85ae901935SOliver Tappe __set_errno(error);
865142c2acSIngo Weinhold return SEM_FAILED;
875142c2acSIngo Weinhold }
885142c2acSIngo Weinhold
895142c2acSIngo Weinhold if (usedSem == sem)
905142c2acSIngo Weinhold semDeleter.Detach();
915142c2acSIngo Weinhold
925142c2acSIngo Weinhold return usedSem;
935142c2acSIngo Weinhold }
945142c2acSIngo Weinhold
955142c2acSIngo Weinhold
965142c2acSIngo Weinhold int
sem_close(sem_t * semaphore)975142c2acSIngo Weinhold sem_close(sem_t* semaphore)
985142c2acSIngo Weinhold {
995142c2acSIngo Weinhold sem_t* deleteSem = NULL;
100d6d439f3SHamish Morrison status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id,
101d6d439f3SHamish Morrison &deleteSem);
1025142c2acSIngo Weinhold if (error == B_OK)
1035142c2acSIngo Weinhold free(deleteSem);
1045142c2acSIngo Weinhold
1055142c2acSIngo Weinhold RETURN_AND_SET_ERRNO(error);
1065142c2acSIngo Weinhold }
1075142c2acSIngo Weinhold
1085142c2acSIngo Weinhold
1095142c2acSIngo Weinhold int
sem_unlink(const char * name)1105142c2acSIngo Weinhold sem_unlink(const char* name)
1115142c2acSIngo Weinhold {
1125142c2acSIngo Weinhold RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
1135142c2acSIngo Weinhold }
1145142c2acSIngo Weinhold
1155142c2acSIngo Weinhold
1165142c2acSIngo Weinhold int
sem_init(sem_t * semaphore,int shared,unsigned value)1175142c2acSIngo Weinhold sem_init(sem_t* semaphore, int shared, unsigned value)
1185142c2acSIngo Weinhold {
119f64c46e6SAugustin Cavalier semaphore->type = shared ? SEM_TYPE_UNNAMED_SHARED : SEM_TYPE_UNNAMED;
120d6d439f3SHamish Morrison semaphore->u.unnamed_sem = value;
121d6d439f3SHamish Morrison return 0;
1225142c2acSIngo Weinhold }
1235142c2acSIngo Weinhold
1245142c2acSIngo Weinhold
1255142c2acSIngo Weinhold int
sem_destroy(sem_t * semaphore)1265142c2acSIngo Weinhold sem_destroy(sem_t* semaphore)
1275142c2acSIngo Weinhold {
128f64c46e6SAugustin Cavalier if (semaphore->type != SEM_TYPE_UNNAMED && semaphore->type != SEM_TYPE_UNNAMED_SHARED)
129d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(EINVAL);
130d6d439f3SHamish Morrison
131d6d439f3SHamish Morrison return 0;
132d6d439f3SHamish Morrison }
133d6d439f3SHamish Morrison
134d6d439f3SHamish Morrison
135d6d439f3SHamish Morrison static int
unnamed_sem_post(sem_t * semaphore)136a866e2d9SJérôme Duval unnamed_sem_post(sem_t* semaphore)
137a866e2d9SJérôme Duval {
138d6d439f3SHamish Morrison int32* sem = (int32*)&semaphore->u.unnamed_sem;
139d6d439f3SHamish Morrison int32 oldValue = atomic_add_if_greater(sem, 1, -1);
140d6d439f3SHamish Morrison if (oldValue > -1)
141d6d439f3SHamish Morrison return 0;
142d6d439f3SHamish Morrison
143f64c46e6SAugustin Cavalier uint32 flags = 0;
144f64c46e6SAugustin Cavalier if (semaphore->type == SEM_TYPE_UNNAMED_SHARED)
145f64c46e6SAugustin Cavalier flags |= B_USER_MUTEX_SHARED;
146f64c46e6SAugustin Cavalier
147f64c46e6SAugustin Cavalier return _kern_mutex_sem_release(sem, flags);
148d6d439f3SHamish Morrison }
149d6d439f3SHamish Morrison
150d6d439f3SHamish Morrison
151d6d439f3SHamish Morrison static int
unnamed_sem_trywait(sem_t * semaphore)152a866e2d9SJérôme Duval unnamed_sem_trywait(sem_t* semaphore)
153a866e2d9SJérôme Duval {
154d6d439f3SHamish Morrison int32* sem = (int32*)&semaphore->u.unnamed_sem;
155d6d439f3SHamish Morrison int32 oldValue = atomic_add_if_greater(sem, -1, 0);
156d6d439f3SHamish Morrison if (oldValue > 0)
157d6d439f3SHamish Morrison return 0;
158d6d439f3SHamish Morrison
159d6d439f3SHamish Morrison return EAGAIN;
160d6d439f3SHamish Morrison }
161d6d439f3SHamish Morrison
162d6d439f3SHamish Morrison
163d6d439f3SHamish Morrison static int
unnamed_sem_timedwait(sem_t * semaphore,clockid_t clock_id,const struct timespec * timeout)164a866e2d9SJérôme Duval unnamed_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
165a866e2d9SJérôme Duval const struct timespec* timeout)
166a866e2d9SJérôme Duval {
167d6d439f3SHamish Morrison int32* sem = (int32*)&semaphore->u.unnamed_sem;
168d6d439f3SHamish Morrison
169d6d439f3SHamish Morrison bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
170a866e2d9SJérôme Duval uint32 flags = 0;
171f64c46e6SAugustin Cavalier if (semaphore->type == SEM_TYPE_UNNAMED_SHARED)
172f64c46e6SAugustin Cavalier flags |= B_USER_MUTEX_SHARED;
173d6d439f3SHamish Morrison if (timeout != NULL) {
174*c6355861SAugustin Cavalier if (!timespec_to_bigtime(*timeout, timeoutMicros))
17595a871b3SAugustin Cavalier timeoutMicros = -1;
176*c6355861SAugustin Cavalier
177a866e2d9SJérôme Duval switch (clock_id) {
178a866e2d9SJérôme Duval case CLOCK_REALTIME:
179d29c0e6eSAugustin Cavalier flags |= B_ABSOLUTE_REAL_TIME_TIMEOUT;
180a866e2d9SJérôme Duval break;
181a866e2d9SJérôme Duval case CLOCK_MONOTONIC:
182d29c0e6eSAugustin Cavalier flags |= B_ABSOLUTE_TIMEOUT;
183a866e2d9SJérôme Duval break;
184a866e2d9SJérôme Duval default:
185a866e2d9SJérôme Duval return EINVAL;
186a866e2d9SJérôme Duval }
187d6d439f3SHamish Morrison }
188d6d439f3SHamish Morrison
189d6d439f3SHamish Morrison int result = unnamed_sem_trywait(semaphore);
190d6d439f3SHamish Morrison if (result == 0)
191d6d439f3SHamish Morrison return 0;
19295a871b3SAugustin Cavalier if (timeoutMicros < 0)
19395a871b3SAugustin Cavalier return EINVAL;
194d6d439f3SHamish Morrison
195a866e2d9SJérôme Duval return _kern_mutex_sem_acquire(sem, NULL, flags, timeoutMicros);
1965142c2acSIngo Weinhold }
1975142c2acSIngo Weinhold
1985142c2acSIngo Weinhold
1995142c2acSIngo Weinhold int
sem_post(sem_t * semaphore)2005142c2acSIngo Weinhold sem_post(sem_t* semaphore)
2015142c2acSIngo Weinhold {
202d6d439f3SHamish Morrison status_t error;
203d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED)
204d6d439f3SHamish Morrison error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
205d6d439f3SHamish Morrison else
206d6d439f3SHamish Morrison error = unnamed_sem_post(semaphore);
207d6d439f3SHamish Morrison
208d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(error);
2095142c2acSIngo Weinhold }
2105142c2acSIngo Weinhold
2115142c2acSIngo Weinhold
212d6d439f3SHamish Morrison static int
named_sem_timedwait(sem_t * semaphore,clockid_t clock_id,const struct timespec * timeout)213a866e2d9SJérôme Duval named_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
214a866e2d9SJérôme Duval const struct timespec* timeout)
2155142c2acSIngo Weinhold {
216*c6355861SAugustin Cavalier bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
217*c6355861SAugustin Cavalier uint32 flags = 0;
218*c6355861SAugustin Cavalier if (timeout != NULL) {
219*c6355861SAugustin Cavalier if (!timespec_to_bigtime(*timeout, timeoutMicros)) {
220a866e2d9SJérôme Duval status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
221a866e2d9SJérôme Duval B_RELATIVE_TIMEOUT, 0);
2223cdae651SSam Toyer if (err == B_WOULD_BLOCK)
2233cdae651SSam Toyer err = EINVAL;
2243cdae651SSam Toyer // do nothing, return err as it is.
225d6d439f3SHamish Morrison return err;
2263cdae651SSam Toyer }
2275142c2acSIngo Weinhold
228a866e2d9SJérôme Duval switch (clock_id) {
229a866e2d9SJérôme Duval case CLOCK_REALTIME:
230a866e2d9SJérôme Duval flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
231a866e2d9SJérôme Duval break;
232a866e2d9SJérôme Duval case CLOCK_MONOTONIC:
233a866e2d9SJérôme Duval flags = B_ABSOLUTE_TIMEOUT;
234a866e2d9SJérôme Duval break;
235a866e2d9SJérôme Duval default:
236a866e2d9SJérôme Duval return EINVAL;
2373cdae651SSam Toyer }
238a866e2d9SJérôme Duval }
239*c6355861SAugustin Cavalier
240a866e2d9SJérôme Duval status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, flags,
241d6d439f3SHamish Morrison timeoutMicros);
2423cdae651SSam Toyer if (err == B_WOULD_BLOCK)
2433cdae651SSam Toyer err = ETIMEDOUT;
2443cdae651SSam Toyer
245d6d439f3SHamish Morrison return err;
2465142c2acSIngo Weinhold }
2475142c2acSIngo Weinhold
2485142c2acSIngo Weinhold
2495142c2acSIngo Weinhold int
sem_trywait(sem_t * semaphore)2505142c2acSIngo Weinhold sem_trywait(sem_t* semaphore)
2515142c2acSIngo Weinhold {
252d6d439f3SHamish Morrison status_t error;
253a866e2d9SJérôme Duval if (semaphore->type == SEM_TYPE_NAMED) {
254a866e2d9SJérôme Duval error = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
255a866e2d9SJérôme Duval B_RELATIVE_TIMEOUT, 0);
256a866e2d9SJérôme Duval } else
257d6d439f3SHamish Morrison error = unnamed_sem_trywait(semaphore);
258d6d439f3SHamish Morrison
259d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(error);
2605142c2acSIngo Weinhold }
2615142c2acSIngo Weinhold
2625142c2acSIngo Weinhold
2635142c2acSIngo Weinhold int
sem_wait(sem_t * semaphore)2645142c2acSIngo Weinhold sem_wait(sem_t* semaphore)
2655142c2acSIngo Weinhold {
266d6d439f3SHamish Morrison status_t error;
267d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED)
268a866e2d9SJérôme Duval error = named_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
269d6d439f3SHamish Morrison else
270a866e2d9SJérôme Duval error = unnamed_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
271d6d439f3SHamish Morrison
272d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
273d6d439f3SHamish Morrison }
274d6d439f3SHamish Morrison
275d6d439f3SHamish Morrison
276d6d439f3SHamish Morrison int
sem_clockwait(sem_t * semaphore,clockid_t clock_id,const struct timespec * abstime)277a866e2d9SJérôme Duval sem_clockwait(sem_t* semaphore, clockid_t clock_id, const struct timespec* abstime)
278d6d439f3SHamish Morrison {
279d6d439f3SHamish Morrison status_t error;
280d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED)
281a866e2d9SJérôme Duval error = named_sem_timedwait(semaphore, clock_id, abstime);
282d6d439f3SHamish Morrison else
283a866e2d9SJérôme Duval error = unnamed_sem_timedwait(semaphore, clock_id, abstime);
284d6d439f3SHamish Morrison
285d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
2865142c2acSIngo Weinhold }
2875142c2acSIngo Weinhold
2885142c2acSIngo Weinhold
2895142c2acSIngo Weinhold int
sem_timedwait(sem_t * semaphore,const struct timespec * abstime)290a866e2d9SJérôme Duval sem_timedwait(sem_t* semaphore, const struct timespec* abstime)
291a866e2d9SJérôme Duval {
292a866e2d9SJérôme Duval return sem_clockwait(semaphore, CLOCK_REALTIME, abstime);
293a866e2d9SJérôme Duval }
294a866e2d9SJérôme Duval
295a866e2d9SJérôme Duval
296a866e2d9SJérôme Duval int
sem_getvalue(sem_t * semaphore,int * value)2975142c2acSIngo Weinhold sem_getvalue(sem_t* semaphore, int* value)
2985142c2acSIngo Weinhold {
299d6d439f3SHamish Morrison if (semaphore->type == SEM_TYPE_NAMED) {
300d6d439f3SHamish Morrison RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
301d6d439f3SHamish Morrison semaphore->u.named_sem_id, value));
302d6d439f3SHamish Morrison } else {
303d6d439f3SHamish Morrison *value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
304d6d439f3SHamish Morrison return 0;
305d6d439f3SHamish Morrison }
3065142c2acSIngo Weinhold }
307