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