xref: /haiku/src/system/libroot/posix/semaphore.cpp (revision a866e2d902764179fe790385bd30c27074616244)
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>
22d6d439f3SHamish Morrison #include <user_mutex_defs.h>
23d6d439f3SHamish Morrison 
24d6d439f3SHamish Morrison 
25d6d439f3SHamish Morrison #define SEM_TYPE_NAMED		1
26d6d439f3SHamish Morrison #define SEM_TYPE_UNNAMED	2
27d6d439f3SHamish Morrison 
28d6d439f3SHamish Morrison 
29d6d439f3SHamish Morrison static int32
30d6d439f3SHamish Morrison atomic_add_if_greater(int32* value, int32 amount, int32 testValue)
31d6d439f3SHamish Morrison {
32d6d439f3SHamish Morrison 	int32 current = atomic_get(value);
33d6d439f3SHamish Morrison 	while (current > testValue) {
34d6d439f3SHamish Morrison 		int32 old = atomic_test_and_set(value, current + amount, current);
35d6d439f3SHamish Morrison 		if (old == current)
36d6d439f3SHamish Morrison 			return old;
37d6d439f3SHamish Morrison 		current = old;
38d6d439f3SHamish Morrison 	}
39d6d439f3SHamish Morrison 	return current;
40d6d439f3SHamish Morrison }
415142c2acSIngo Weinhold 
425142c2acSIngo Weinhold 
435142c2acSIngo Weinhold sem_t*
445142c2acSIngo Weinhold sem_open(const char* name, int openFlags,...)
455142c2acSIngo Weinhold {
465142c2acSIngo Weinhold 	if (name == NULL) {
47ae901935SOliver Tappe 		__set_errno(B_BAD_VALUE);
485142c2acSIngo Weinhold 		return SEM_FAILED;
495142c2acSIngo Weinhold 	}
505142c2acSIngo Weinhold 
515142c2acSIngo Weinhold 	// get the mode and semaphore count parameters, if O_CREAT is specified
525142c2acSIngo Weinhold 	mode_t mode = 0;
535142c2acSIngo Weinhold 	unsigned semCount = 0;
545142c2acSIngo Weinhold 
555142c2acSIngo Weinhold 	if ((openFlags & O_CREAT) != 0) {
565142c2acSIngo Weinhold 		va_list args;
575142c2acSIngo Weinhold 		va_start(args, openFlags);
585142c2acSIngo Weinhold 		mode = va_arg(args, mode_t);
595142c2acSIngo Weinhold 		semCount = va_arg(args, unsigned);
605142c2acSIngo Weinhold 		va_end(args);
61e8f1b18eSIngo Weinhold 	} else {
62e8f1b18eSIngo Weinhold 		// clear O_EXCL, if O_CREAT is not given
63e8f1b18eSIngo Weinhold 		openFlags &= ~O_EXCL;
645142c2acSIngo Weinhold 	}
655142c2acSIngo Weinhold 
665142c2acSIngo Weinhold 	// Allocate a sem_t structure -- we don't know, whether this is the first
675142c2acSIngo Weinhold 	// call of this process to open the semaphore. If it is, we will keep the
685142c2acSIngo Weinhold 	// structure, otherwise we will delete it later.
695142c2acSIngo Weinhold 	sem_t* sem = (sem_t*)malloc(sizeof(sem_t));
705142c2acSIngo Weinhold 	if (sem == NULL) {
71ae901935SOliver Tappe 		__set_errno(B_NO_MEMORY);
725142c2acSIngo Weinhold 		return SEM_FAILED;
735142c2acSIngo Weinhold 	}
74d6d439f3SHamish Morrison 
75d6d439f3SHamish Morrison 	sem->type = SEM_TYPE_NAMED;
765142c2acSIngo Weinhold 	MemoryDeleter semDeleter(sem);
775142c2acSIngo Weinhold 
785142c2acSIngo Weinhold 	// ask the kernel to open the semaphore
795142c2acSIngo Weinhold 	sem_t* usedSem;
805142c2acSIngo Weinhold 	status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount,
815142c2acSIngo Weinhold 		sem, &usedSem);
825142c2acSIngo Weinhold 	if (error != B_OK) {
83ae901935SOliver Tappe 		__set_errno(error);
845142c2acSIngo Weinhold 		return SEM_FAILED;
855142c2acSIngo Weinhold 	}
865142c2acSIngo Weinhold 
875142c2acSIngo Weinhold 	if (usedSem == sem)
885142c2acSIngo Weinhold 		semDeleter.Detach();
895142c2acSIngo Weinhold 
905142c2acSIngo Weinhold 	return usedSem;
915142c2acSIngo Weinhold }
925142c2acSIngo Weinhold 
935142c2acSIngo Weinhold 
945142c2acSIngo Weinhold int
955142c2acSIngo Weinhold sem_close(sem_t* semaphore)
965142c2acSIngo Weinhold {
975142c2acSIngo Weinhold 	sem_t* deleteSem = NULL;
98d6d439f3SHamish Morrison 	status_t error = _kern_realtime_sem_close(semaphore->u.named_sem_id,
99d6d439f3SHamish Morrison 		&deleteSem);
1005142c2acSIngo Weinhold 	if (error == B_OK)
1015142c2acSIngo Weinhold 		free(deleteSem);
1025142c2acSIngo Weinhold 
1035142c2acSIngo Weinhold 	RETURN_AND_SET_ERRNO(error);
1045142c2acSIngo Weinhold }
1055142c2acSIngo Weinhold 
1065142c2acSIngo Weinhold 
1075142c2acSIngo Weinhold int
1085142c2acSIngo Weinhold sem_unlink(const char* name)
1095142c2acSIngo Weinhold {
1105142c2acSIngo Weinhold 	RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
1115142c2acSIngo Weinhold }
1125142c2acSIngo Weinhold 
1135142c2acSIngo Weinhold 
1145142c2acSIngo Weinhold int
1155142c2acSIngo Weinhold sem_init(sem_t* semaphore, int shared, unsigned value)
1165142c2acSIngo Weinhold {
117d6d439f3SHamish Morrison 	semaphore->type = SEM_TYPE_UNNAMED;
118d6d439f3SHamish Morrison 	semaphore->u.unnamed_sem = value;
119d6d439f3SHamish Morrison 	return 0;
1205142c2acSIngo Weinhold }
1215142c2acSIngo Weinhold 
1225142c2acSIngo Weinhold 
1235142c2acSIngo Weinhold int
1245142c2acSIngo Weinhold sem_destroy(sem_t* semaphore)
1255142c2acSIngo Weinhold {
126d6d439f3SHamish Morrison 	if (semaphore->type != SEM_TYPE_UNNAMED)
127d6d439f3SHamish Morrison 		RETURN_AND_SET_ERRNO(EINVAL);
128d6d439f3SHamish Morrison 
129d6d439f3SHamish Morrison 	return 0;
130d6d439f3SHamish Morrison }
131d6d439f3SHamish Morrison 
132d6d439f3SHamish Morrison 
133d6d439f3SHamish Morrison static int
134*a866e2d9SJérôme Duval unnamed_sem_post(sem_t* semaphore)
135*a866e2d9SJérôme Duval {
136d6d439f3SHamish Morrison 	int32* sem = (int32*)&semaphore->u.unnamed_sem;
137d6d439f3SHamish Morrison 	int32 oldValue = atomic_add_if_greater(sem, 1, -1);
138d6d439f3SHamish Morrison 	if (oldValue > -1)
139d6d439f3SHamish Morrison 		return 0;
140d6d439f3SHamish Morrison 
141d6d439f3SHamish Morrison 	return _kern_mutex_sem_release(sem);
142d6d439f3SHamish Morrison }
143d6d439f3SHamish Morrison 
144d6d439f3SHamish Morrison 
145d6d439f3SHamish Morrison static int
146*a866e2d9SJérôme Duval unnamed_sem_trywait(sem_t* semaphore)
147*a866e2d9SJérôme Duval {
148d6d439f3SHamish Morrison 	int32* sem = (int32*)&semaphore->u.unnamed_sem;
149d6d439f3SHamish Morrison 	int32 oldValue = atomic_add_if_greater(sem, -1, 0);
150d6d439f3SHamish Morrison 	if (oldValue > 0)
151d6d439f3SHamish Morrison 		return 0;
152d6d439f3SHamish Morrison 
153d6d439f3SHamish Morrison 	return EAGAIN;
154d6d439f3SHamish Morrison }
155d6d439f3SHamish Morrison 
156d6d439f3SHamish Morrison 
157d6d439f3SHamish Morrison static int
158*a866e2d9SJérôme Duval unnamed_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
159*a866e2d9SJérôme Duval 	const struct timespec* timeout)
160*a866e2d9SJérôme Duval {
161d6d439f3SHamish Morrison 	int32* sem = (int32*)&semaphore->u.unnamed_sem;
162d6d439f3SHamish Morrison 
163d6d439f3SHamish Morrison 	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
164*a866e2d9SJérôme Duval 	uint32 flags = 0;
165d6d439f3SHamish Morrison 	if (timeout != NULL) {
166d6d439f3SHamish Morrison 		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
167d6d439f3SHamish Morrison 			+ timeout->tv_nsec / 1000;
168*a866e2d9SJérôme Duval 		switch (clock_id) {
169*a866e2d9SJérôme Duval 			case CLOCK_REALTIME:
170*a866e2d9SJérôme Duval 				flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
171*a866e2d9SJérôme Duval 				break;
172*a866e2d9SJérôme Duval 			case CLOCK_MONOTONIC:
173*a866e2d9SJérôme Duval 				flags = B_ABSOLUTE_TIMEOUT;
174*a866e2d9SJérôme Duval 				break;
175*a866e2d9SJérôme Duval 			default:
176*a866e2d9SJérôme Duval 				return EINVAL;
177*a866e2d9SJérôme Duval 		}
178d6d439f3SHamish Morrison 	}
179d6d439f3SHamish Morrison 
180d6d439f3SHamish Morrison 	int result = unnamed_sem_trywait(semaphore);
181d6d439f3SHamish Morrison 	if (result == 0)
182d6d439f3SHamish Morrison 		return 0;
183d6d439f3SHamish Morrison 
184*a866e2d9SJérôme Duval 	return _kern_mutex_sem_acquire(sem, NULL, flags, timeoutMicros);
1855142c2acSIngo Weinhold }
1865142c2acSIngo Weinhold 
1875142c2acSIngo Weinhold 
1885142c2acSIngo Weinhold int
1895142c2acSIngo Weinhold sem_post(sem_t* semaphore)
1905142c2acSIngo Weinhold {
191d6d439f3SHamish Morrison 	status_t error;
192d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
193d6d439f3SHamish Morrison 		error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
194d6d439f3SHamish Morrison 	else
195d6d439f3SHamish Morrison 		error = unnamed_sem_post(semaphore);
196d6d439f3SHamish Morrison 
197d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO(error);
1985142c2acSIngo Weinhold }
1995142c2acSIngo Weinhold 
2005142c2acSIngo Weinhold 
201d6d439f3SHamish Morrison static int
202*a866e2d9SJérôme Duval named_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
203*a866e2d9SJérôme Duval 	const struct timespec* timeout)
2045142c2acSIngo Weinhold {
205f1a3e05dSJérôme Duval 	if (timeout != NULL
206f1a3e05dSJérôme Duval 		&& (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) {
207*a866e2d9SJérôme Duval 		status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
208*a866e2d9SJérôme Duval 			B_RELATIVE_TIMEOUT, 0);
2093cdae651SSam Toyer 		if (err == B_WOULD_BLOCK)
2103cdae651SSam Toyer 			err = EINVAL;
2113cdae651SSam Toyer 		// do nothing, return err as it is.
212d6d439f3SHamish Morrison 		return err;
2133cdae651SSam Toyer 	}
2145142c2acSIngo Weinhold 
2153cdae651SSam Toyer 	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
216*a866e2d9SJérôme Duval 	uint32 flags = 0;
2173cdae651SSam Toyer 	if (timeout != NULL) {
2183cdae651SSam Toyer 		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
2193cdae651SSam Toyer 			+ timeout->tv_nsec / 1000;
220*a866e2d9SJérôme Duval 		switch (clock_id) {
221*a866e2d9SJérôme Duval 			case CLOCK_REALTIME:
222*a866e2d9SJérôme Duval 				flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
223*a866e2d9SJérôme Duval 				break;
224*a866e2d9SJérôme Duval 			case CLOCK_MONOTONIC:
225*a866e2d9SJérôme Duval 				flags = B_ABSOLUTE_TIMEOUT;
226*a866e2d9SJérôme Duval 				break;
227*a866e2d9SJérôme Duval 			default:
228*a866e2d9SJérôme Duval 				return EINVAL;
2293cdae651SSam Toyer 		}
230*a866e2d9SJérôme Duval 	}
231*a866e2d9SJérôme Duval 	status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, flags,
232d6d439f3SHamish Morrison 		timeoutMicros);
2333cdae651SSam Toyer 	if (err == B_WOULD_BLOCK)
2343cdae651SSam Toyer 		err = ETIMEDOUT;
2353cdae651SSam Toyer 
236d6d439f3SHamish Morrison 	return err;
2375142c2acSIngo Weinhold }
2385142c2acSIngo Weinhold 
2395142c2acSIngo Weinhold 
2405142c2acSIngo Weinhold int
2415142c2acSIngo Weinhold sem_trywait(sem_t* semaphore)
2425142c2acSIngo Weinhold {
243d6d439f3SHamish Morrison 	status_t error;
244*a866e2d9SJérôme Duval 	if (semaphore->type == SEM_TYPE_NAMED) {
245*a866e2d9SJérôme Duval 		error = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
246*a866e2d9SJérôme Duval 			B_RELATIVE_TIMEOUT, 0);
247*a866e2d9SJérôme Duval 	} else
248d6d439f3SHamish Morrison 		error = unnamed_sem_trywait(semaphore);
249d6d439f3SHamish Morrison 
250d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO(error);
2515142c2acSIngo Weinhold }
2525142c2acSIngo Weinhold 
2535142c2acSIngo Weinhold 
2545142c2acSIngo Weinhold int
2555142c2acSIngo Weinhold sem_wait(sem_t* semaphore)
2565142c2acSIngo Weinhold {
257d6d439f3SHamish Morrison 	status_t error;
258d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
259*a866e2d9SJérôme Duval 		error = named_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
260d6d439f3SHamish Morrison 	else
261*a866e2d9SJérôme Duval 		error = unnamed_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
262d6d439f3SHamish Morrison 
263d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
264d6d439f3SHamish Morrison }
265d6d439f3SHamish Morrison 
266d6d439f3SHamish Morrison 
267d6d439f3SHamish Morrison int
268*a866e2d9SJérôme Duval sem_clockwait(sem_t* semaphore, clockid_t clock_id, const struct timespec* abstime)
269d6d439f3SHamish Morrison {
270d6d439f3SHamish Morrison 	status_t error;
271d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED)
272*a866e2d9SJérôme Duval 		error = named_sem_timedwait(semaphore, clock_id, abstime);
273d6d439f3SHamish Morrison 	else
274*a866e2d9SJérôme Duval 		error = unnamed_sem_timedwait(semaphore, clock_id, abstime);
275d6d439f3SHamish Morrison 
276d6d439f3SHamish Morrison 	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
2775142c2acSIngo Weinhold }
2785142c2acSIngo Weinhold 
2795142c2acSIngo Weinhold 
2805142c2acSIngo Weinhold int
281*a866e2d9SJérôme Duval sem_timedwait(sem_t* semaphore, const struct timespec* abstime)
282*a866e2d9SJérôme Duval {
283*a866e2d9SJérôme Duval 	return sem_clockwait(semaphore, CLOCK_REALTIME, abstime);
284*a866e2d9SJérôme Duval }
285*a866e2d9SJérôme Duval 
286*a866e2d9SJérôme Duval 
287*a866e2d9SJérôme Duval int
2885142c2acSIngo Weinhold sem_getvalue(sem_t* semaphore, int* value)
2895142c2acSIngo Weinhold {
290d6d439f3SHamish Morrison 	if (semaphore->type == SEM_TYPE_NAMED) {
291d6d439f3SHamish Morrison 		RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
292d6d439f3SHamish Morrison 			semaphore->u.named_sem_id, value));
293d6d439f3SHamish Morrison 	} else {
294d6d439f3SHamish Morrison 		*value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
295d6d439f3SHamish Morrison 		return 0;
296d6d439f3SHamish Morrison 	}
2975142c2acSIngo Weinhold }
298