xref: /haiku/src/system/libroot/posix/semaphore.cpp (revision d6d439f3f75a0986063d42eda8ff5281adcb29b1)
15142c2acSIngo Weinhold /*
2*d6d439f3SHamish 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>
135142c2acSIngo Weinhold 
145142c2acSIngo Weinhold #include <OS.h>
155142c2acSIngo Weinhold 
165142c2acSIngo Weinhold #include <AutoDeleter.h>
17ae901935SOliver Tappe #include <errno_private.h>
186b202f4eSIngo Weinhold #include <posix/realtime_sem_defs.h>
195142c2acSIngo Weinhold #include <syscall_utils.h>
205142c2acSIngo Weinhold #include <syscalls.h>
21*d6d439f3SHamish Morrison #include <user_mutex_defs.h>
22*d6d439f3SHamish Morrison 
23*d6d439f3SHamish Morrison 
24*d6d439f3SHamish Morrison #define SEM_TYPE_NAMED		1
25*d6d439f3SHamish Morrison #define SEM_TYPE_UNNAMED	2
26*d6d439f3SHamish Morrison 
27*d6d439f3SHamish Morrison 
28*d6d439f3SHamish Morrison static int32
29*d6d439f3SHamish Morrison atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
30*d6d439f3SHamish Morrison {
31*d6d439f3SHamish Morrison 	int32 current = atomic_get(value);
32*d6d439f3SHamish Morrison 	while (current > testValue) {
33*d6d439f3SHamish Morrison 		int32 old = atomic_test_and_set(value, current + amount, current);
34*d6d439f3SHamish Morrison 		if (old == current)
35*d6d439f3SHamish Morrison 			return old;
36*d6d439f3SHamish Morrison 		current = old;
37*d6d439f3SHamish Morrison 	}
38*d6d439f3SHamish Morrison 	return current;
39*d6d439f3SHamish Morrison }
405142c2acSIngo Weinhold 
415142c2acSIngo Weinhold 
425142c2acSIngo Weinhold sem_t*
435142c2acSIngo Weinhold sem_open(const char* name, int openFlags,...)
445142c2acSIngo Weinhold {
455142c2acSIngo Weinhold 	if (name == NULL) {
46ae901935SOliver Tappe 		__set_errno(B_BAD_VALUE);
475142c2acSIngo Weinhold 		return SEM_FAILED;
485142c2acSIngo Weinhold 	}
495142c2acSIngo Weinhold 
505142c2acSIngo Weinhold 	// get the mode and semaphore count parameters, if O_CREAT is specified
515142c2acSIngo Weinhold 	mode_t mode = 0;
525142c2acSIngo Weinhold 	unsigned semCount = 0;
535142c2acSIngo Weinhold 
545142c2acSIngo Weinhold 	if ((openFlags & O_CREAT) != 0) {
555142c2acSIngo Weinhold 		va_list args;
565142c2acSIngo Weinhold 		va_start(args, openFlags);
575142c2acSIngo Weinhold 		mode = va_arg(args, mode_t);
585142c2acSIngo Weinhold 		semCount = va_arg(args, unsigned);
595142c2acSIngo Weinhold 		va_end(args);
60e8f1b18eSIngo Weinhold 	} else {
61e8f1b18eSIngo Weinhold 		// clear O_EXCL, if O_CREAT is not given
62e8f1b18eSIngo Weinhold 		openFlags &= ~O_EXCL;
635142c2acSIngo Weinhold 	}
645142c2acSIngo Weinhold 
655142c2acSIngo Weinhold 	// Allocate a sem_t structure -- we don't know, whether this is the first
665142c2acSIngo Weinhold 	// call of this process to open the semaphore. If it is, we will keep the
675142c2acSIngo Weinhold 	// structure, otherwise we will delete it later.
685142c2acSIngo Weinhold 	sem_t* sem = (sem_t*)malloc(sizeof(sem_t));
695142c2acSIngo Weinhold 	if (sem == NULL) {
70ae901935SOliver Tappe 		__set_errno(B_NO_MEMORY);
715142c2acSIngo Weinhold 		return SEM_FAILED;
725142c2acSIngo Weinhold 	}
73*d6d439f3SHamish Morrison 
74*d6d439f3SHamish Morrison 	sem->type = SEM_TYPE_NAMED;
755142c2acSIngo Weinhold 	MemoryDeleter semDeleter(sem);
765142c2acSIngo Weinhold 
775142c2acSIngo Weinhold 	// ask the kernel to open the semaphore
785142c2acSIngo Weinhold 	sem_t* usedSem;
795142c2acSIngo Weinhold 	status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount,
805142c2acSIngo Weinhold 		sem, &usedSem);
815142c2acSIngo Weinhold 	if (error != B_OK) {
82ae901935SOliver Tappe 		__set_errno(error);
835142c2acSIngo Weinhold 		return SEM_FAILED;
845142c2acSIngo Weinhold 	}
855142c2acSIngo Weinhold 
865142c2acSIngo Weinhold 	if (usedSem == sem)
875142c2acSIngo Weinhold 		semDeleter.Detach();
885142c2acSIngo Weinhold 
895142c2acSIngo Weinhold 	return usedSem;
905142c2acSIngo Weinhold }
915142c2acSIngo Weinhold 
925142c2acSIngo Weinhold 
935142c2acSIngo Weinhold int
945142c2acSIngo Weinhold sem_close(sem_t* semaphore)
955142c2acSIngo Weinhold {
965142c2acSIngo Weinhold 	sem_t* deleteSem = NULL;
97*d6d439f3SHamish Morrison 	status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id,
98*d6d439f3SHamish Morrison 		&deleteSem);
995142c2acSIngo Weinhold 	if (error == B_OK)
1005142c2acSIngo Weinhold 		free(deleteSem);
1015142c2acSIngo Weinhold 
1025142c2acSIngo Weinhold 	RETURN_AND_SET_ERRNO(error);
1035142c2acSIngo Weinhold }
1045142c2acSIngo Weinhold 
1055142c2acSIngo Weinhold 
1065142c2acSIngo Weinhold int
1075142c2acSIngo Weinhold sem_unlink(const char* name)
1085142c2acSIngo Weinhold {
1095142c2acSIngo Weinhold 	RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
1105142c2acSIngo Weinhold }
1115142c2acSIngo Weinhold 
1125142c2acSIngo Weinhold 
1135142c2acSIngo Weinhold int
1145142c2acSIngo Weinhold sem_init(sem_t* semaphore, int shared, unsigned value)
1155142c2acSIngo Weinhold {
116*d6d439f3SHamish Morrison 	semaphore->type = SEM_TYPE_UNNAMED;
117*d6d439f3SHamish Morrison 	semaphore->u.unnamed_sem = value;
118*d6d439f3SHamish Morrison 	return 0;
1195142c2acSIngo Weinhold }
1205142c2acSIngo Weinhold 
1215142c2acSIngo Weinhold 
1225142c2acSIngo Weinhold int
1235142c2acSIngo Weinhold sem_destroy(sem_t* semaphore)
1245142c2acSIngo Weinhold {
125*d6d439f3SHamish Morrison 	if (semaphore->type != SEM_TYPE_UNNAMED)
126*d6d439f3SHamish Morrison 		RETURN_AND_SET_ERRNO(EINVAL);
127*d6d439f3SHamish Morrison 
128*d6d439f3SHamish Morrison 	return 0;
129*d6d439f3SHamish Morrison }
130*d6d439f3SHamish Morrison 
131*d6d439f3SHamish Morrison 
132*d6d439f3SHamish Morrison static int
133*d6d439f3SHamish Morrison unnamed_sem_post(sem_t* semaphore) {
134*d6d439f3SHamish Morrison 	int32* sem = (int32*)&semaphore->u.unnamed_sem;
135*d6d439f3SHamish Morrison 	int32 oldValue = atomic_add_if_greater(sem, 1, -1);
136*d6d439f3SHamish Morrison 	if (oldValue > -1)
137*d6d439f3SHamish Morrison 		return 0;
138*d6d439f3SHamish Morrison 
139*d6d439f3SHamish Morrison 	return _kern_mutex_sem_release(sem);
140*d6d439f3SHamish Morrison }
141*d6d439f3SHamish Morrison 
142*d6d439f3SHamish Morrison 
143*d6d439f3SHamish Morrison static int
144*d6d439f3SHamish Morrison unnamed_sem_trywait(sem_t* semaphore) {
145*d6d439f3SHamish Morrison 	int32* sem = (int32*)&semaphore->u.unnamed_sem;
146*d6d439f3SHamish Morrison 	int32 oldValue = atomic_add_if_greater(sem, -1, 0);
147*d6d439f3SHamish Morrison 	if (oldValue > 0)
148*d6d439f3SHamish Morrison 		return 0;
149*d6d439f3SHamish Morrison 
150*d6d439f3SHamish Morrison 	return EAGAIN;
151*d6d439f3SHamish Morrison }
152*d6d439f3SHamish Morrison 
153*d6d439f3SHamish Morrison 
154*d6d439f3SHamish Morrison static int
155*d6d439f3SHamish Morrison unnamed_sem_timedwait(sem_t* semaphore, const struct timespec* timeout) {
156*d6d439f3SHamish Morrison 	int32* sem = (int32*)&semaphore->u.unnamed_sem;
157*d6d439f3SHamish Morrison 
158*d6d439f3SHamish Morrison 	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
159*d6d439f3SHamish Morrison 	if (timeout != NULL) {
160*d6d439f3SHamish Morrison 		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
161*d6d439f3SHamish Morrison 			+ timeout->tv_nsec / 1000;
162*d6d439f3SHamish Morrison 	}
163*d6d439f3SHamish Morrison 
164*d6d439f3SHamish Morrison 	int result = unnamed_sem_trywait(semaphore);
165*d6d439f3SHamish Morrison 	if (result == 0)
166*d6d439f3SHamish Morrison 		return 0;
167*d6d439f3SHamish Morrison 
168*d6d439f3SHamish Morrison 	return _kern_mutex_sem_acquire(sem,	NULL,
169*d6d439f3SHamish Morrison 		timeoutMicros == B_INFINITE_TIMEOUT ? 0 : B_ABSOLUTE_REAL_TIME_TIMEOUT,
170*d6d439f3SHamish Morrison 		timeoutMicros);
1715142c2acSIngo Weinhold }
1725142c2acSIngo Weinhold 
1735142c2acSIngo Weinhold 
1745142c2acSIngo Weinhold int
1755142c2acSIngo Weinhold sem_post(sem_t* semaphore)
1765142c2acSIngo Weinhold {
177*d6d439f3SHamish Morrison 	status_t error;
178*d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
179*d6d439f3SHamish Morrison 		error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
180*d6d439f3SHamish Morrison 	else
181*d6d439f3SHamish Morrison 		error = unnamed_sem_post(semaphore);
182*d6d439f3SHamish Morrison 
183*d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO(error);
1845142c2acSIngo Weinhold }
1855142c2acSIngo Weinhold 
1865142c2acSIngo Weinhold 
187*d6d439f3SHamish Morrison static int
188*d6d439f3SHamish Morrison named_sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
1895142c2acSIngo Weinhold {
190f1a3e05dSJérôme Duval 	if (timeout != NULL
191f1a3e05dSJérôme Duval 		&& (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) {
192*d6d439f3SHamish Morrison 		status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 0);
1933cdae651SSam Toyer 		if (err == B_WOULD_BLOCK)
1943cdae651SSam Toyer 			err = EINVAL;
1953cdae651SSam Toyer 		// do nothing, return err as it is.
196*d6d439f3SHamish Morrison 		return err;
1973cdae651SSam Toyer 	}
1985142c2acSIngo Weinhold 
1993cdae651SSam Toyer 	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
2003cdae651SSam Toyer 	if (timeout != NULL) {
2013cdae651SSam Toyer 		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
2023cdae651SSam Toyer 			+ timeout->tv_nsec / 1000;
2033cdae651SSam Toyer 	}
204*d6d439f3SHamish Morrison 	status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
205*d6d439f3SHamish Morrison 		timeoutMicros);
2063cdae651SSam Toyer 	if (err == B_WOULD_BLOCK)
2073cdae651SSam Toyer 		err = ETIMEDOUT;
2083cdae651SSam Toyer 
209*d6d439f3SHamish Morrison 	return err;
2105142c2acSIngo Weinhold }
2115142c2acSIngo Weinhold 
2125142c2acSIngo Weinhold 
2135142c2acSIngo Weinhold int
2145142c2acSIngo Weinhold sem_trywait(sem_t* semaphore)
2155142c2acSIngo Weinhold {
216*d6d439f3SHamish Morrison 	status_t error;
217*d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
218*d6d439f3SHamish Morrison 		error = _kern_realtime_sem_wait(semaphore->u.named_sem_id, 0);
219*d6d439f3SHamish Morrison 	else
220*d6d439f3SHamish Morrison 		error = unnamed_sem_trywait(semaphore);
221*d6d439f3SHamish Morrison 
222*d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO(error);
2235142c2acSIngo Weinhold }
2245142c2acSIngo Weinhold 
2255142c2acSIngo Weinhold 
2265142c2acSIngo Weinhold int
2275142c2acSIngo Weinhold sem_wait(sem_t* semaphore)
2285142c2acSIngo Weinhold {
229*d6d439f3SHamish Morrison 	status_t error;
230*d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
231*d6d439f3SHamish Morrison 		error = named_sem_timedwait(semaphore, NULL);
232*d6d439f3SHamish Morrison 	else
233*d6d439f3SHamish Morrison 		error = unnamed_sem_timedwait(semaphore, NULL);
234*d6d439f3SHamish Morrison 
235*d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
236*d6d439f3SHamish Morrison }
237*d6d439f3SHamish Morrison 
238*d6d439f3SHamish Morrison 
239*d6d439f3SHamish Morrison int
240*d6d439f3SHamish Morrison sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
241*d6d439f3SHamish Morrison {
242*d6d439f3SHamish Morrison 	status_t error;
243*d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
244*d6d439f3SHamish Morrison 		error = named_sem_timedwait(semaphore, timeout);
245*d6d439f3SHamish Morrison 	else
246*d6d439f3SHamish Morrison 		error = unnamed_sem_timedwait(semaphore, timeout);
247*d6d439f3SHamish Morrison 
248*d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
2495142c2acSIngo Weinhold }
2505142c2acSIngo Weinhold 
2515142c2acSIngo Weinhold 
2525142c2acSIngo Weinhold int
2535142c2acSIngo Weinhold sem_getvalue(sem_t* semaphore, int* value)
2545142c2acSIngo Weinhold {
255*d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED) {
256*d6d439f3SHamish Morrison 		RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
257*d6d439f3SHamish Morrison 			semaphore->u.named_sem_id, value));
258*d6d439f3SHamish Morrison 	} else {
259*d6d439f3SHamish Morrison 		*value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
260*d6d439f3SHamish Morrison 		return 0;
261*d6d439f3SHamish Morrison 	}
2625142c2acSIngo Weinhold }
263