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