xref: /haiku/src/system/libroot/posix/semaphore.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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 
30 static int32
31 atomic_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 
44 sem_t*
45 sem_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 
95 int
96 sem_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 
108 int
109 sem_unlink(const char* name)
110 {
111 	RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
112 }
113 
114 
115 int
116 sem_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 
124 int
125 sem_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 
134 static int
135 unnamed_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 
150 static int
151 unnamed_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 
162 static int
163 unnamed_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 		if (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)
176 			timeoutMicros = -1;
177 		switch (clock_id) {
178 			case CLOCK_REALTIME:
179 				flags |= B_ABSOLUTE_REAL_TIME_TIMEOUT;
180 				break;
181 			case CLOCK_MONOTONIC:
182 				flags |= B_ABSOLUTE_TIMEOUT;
183 				break;
184 			default:
185 				return EINVAL;
186 		}
187 	}
188 
189 	int result = unnamed_sem_trywait(semaphore);
190 	if (result == 0)
191 		return 0;
192 	if (timeoutMicros < 0)
193 		return EINVAL;
194 
195 	return _kern_mutex_sem_acquire(sem, NULL, flags, timeoutMicros);
196 }
197 
198 
199 int
200 sem_post(sem_t* semaphore)
201 {
202 	status_t error;
203 	if (semaphore->type == SEM_TYPE_NAMED)
204 		error = _kern_realtime_sem_post(semaphore->u.named_sem_id);
205 	else
206 		error = unnamed_sem_post(semaphore);
207 
208 	RETURN_AND_SET_ERRNO(error);
209 }
210 
211 
212 static int
213 named_sem_timedwait(sem_t* semaphore, clockid_t clock_id,
214 	const struct timespec* timeout)
215 {
216 	if (timeout != NULL
217 		&& (timeout->tv_nsec < 0 || timeout->tv_nsec >= 1000000000)) {
218 		status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
219 			B_RELATIVE_TIMEOUT, 0);
220 		if (err == B_WOULD_BLOCK)
221 			err = EINVAL;
222 		// do nothing, return err as it is.
223 		return err;
224 	}
225 
226 	bigtime_t timeoutMicros = B_INFINITE_TIMEOUT;
227 	uint32 flags = 0;
228 	if (timeout != NULL) {
229 		timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
230 			+ timeout->tv_nsec / 1000;
231 		switch (clock_id) {
232 			case CLOCK_REALTIME:
233 				flags = B_ABSOLUTE_REAL_TIME_TIMEOUT;
234 				break;
235 			case CLOCK_MONOTONIC:
236 				flags = B_ABSOLUTE_TIMEOUT;
237 				break;
238 			default:
239 				return EINVAL;
240 		}
241 	}
242 	status_t err = _kern_realtime_sem_wait(semaphore->u.named_sem_id, flags,
243 		timeoutMicros);
244 	if (err == B_WOULD_BLOCK)
245 		err = ETIMEDOUT;
246 
247 	return err;
248 }
249 
250 
251 int
252 sem_trywait(sem_t* semaphore)
253 {
254 	status_t error;
255 	if (semaphore->type == SEM_TYPE_NAMED) {
256 		error = _kern_realtime_sem_wait(semaphore->u.named_sem_id,
257 			B_RELATIVE_TIMEOUT, 0);
258 	} else
259 		error = unnamed_sem_trywait(semaphore);
260 
261 	RETURN_AND_SET_ERRNO(error);
262 }
263 
264 
265 int
266 sem_wait(sem_t* semaphore)
267 {
268 	status_t error;
269 	if (semaphore->type == SEM_TYPE_NAMED)
270 		error = named_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
271 	else
272 		error = unnamed_sem_timedwait(semaphore, CLOCK_REALTIME, NULL);
273 
274 	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
275 }
276 
277 
278 int
279 sem_clockwait(sem_t* semaphore, clockid_t clock_id, const struct timespec* abstime)
280 {
281 	status_t error;
282 	if (semaphore->type == SEM_TYPE_NAMED)
283 		error = named_sem_timedwait(semaphore, clock_id, abstime);
284 	else
285 		error = unnamed_sem_timedwait(semaphore, clock_id, abstime);
286 
287 	RETURN_AND_SET_ERRNO_TEST_CANCEL(error);
288 }
289 
290 
291 int
292 sem_timedwait(sem_t* semaphore, const struct timespec* abstime)
293 {
294 	return sem_clockwait(semaphore, CLOCK_REALTIME, abstime);
295 }
296 
297 
298 int
299 sem_getvalue(sem_t* semaphore, int* value)
300 {
301 	if (semaphore->type == SEM_TYPE_NAMED) {
302 		RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(
303 			semaphore->u.named_sem_id, value));
304 	} else {
305 		*value = semaphore->u.unnamed_sem < 0 ? 0 : semaphore->u.unnamed_sem;
306 		return 0;
307 	}
308 }
309