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