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