1 |
/* |
2 |
* Copyright (C) 2010 David Xu <davidxu@freebsd.org>. |
3 |
* All rights reserved. |
4 |
* |
5 |
* Redistribution and use in source and binary forms, with or without |
6 |
* modification, are permitted provided that the following conditions |
7 |
* are met: |
8 |
* 1. Redistributions of source code must retain the above copyright |
9 |
* notice(s), this list of conditions and the following disclaimer as |
10 |
* the first lines of this file unmodified other than the possible |
11 |
* addition of one or more copyright notices. |
12 |
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
* notice(s), this list of conditions and the following disclaimer in |
14 |
* the documentation and/or other materials provided with the |
15 |
* distribution. |
16 |
* |
17 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY |
18 |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
20 |
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE |
21 |
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
22 |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
23 |
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
24 |
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
25 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
26 |
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
27 |
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 |
* |
29 |
* $FreeBSD$ |
30 |
*/ |
31 |
|
32 |
#include "namespace.h" |
33 |
#include <sys/types.h> |
34 |
#include <sys/queue.h> |
35 |
#include <sys/mman.h> |
36 |
#include <sys/stat.h> |
37 |
#include <errno.h> |
38 |
#include <machine/atomic.h> |
39 |
#include <sys/umtx.h> |
40 |
#include <limits.h> |
41 |
#include <fcntl.h> |
42 |
#include <pthread.h> |
43 |
#include <stdarg.h> |
44 |
#include <stdlib.h> |
45 |
#include <string.h> |
46 |
#include <time.h> |
47 |
#include <semaphore.h> |
48 |
#include <unistd.h> |
49 |
#include "un-namespace.h" |
50 |
#include "libc_private.h" |
51 |
|
52 |
__weak_reference(_sem_close, sem_close); |
53 |
__weak_reference(_sem_destroy, sem_destroy); |
54 |
__weak_reference(_sem_getvalue, sem_getvalue); |
55 |
__weak_reference(_sem_init, sem_init); |
56 |
__weak_reference(_sem_open, sem_open); |
57 |
__weak_reference(_sem_post, sem_post); |
58 |
__weak_reference(_sem_timedwait, sem_timedwait); |
59 |
__weak_reference(_sem_trywait, sem_trywait); |
60 |
__weak_reference(_sem_unlink, sem_unlink); |
61 |
__weak_reference(_sem_wait, sem_wait); |
62 |
|
63 |
#define SEM_PREFIX "/tmp/SEMD" |
64 |
#define SEM_MAGIC ((u_int32_t)0x73656d31) |
65 |
|
66 |
struct sem_nameinfo { |
67 |
int open_count; |
68 |
char *name; |
69 |
sem_t *sem; |
70 |
LIST_ENTRY(sem_nameinfo) next; |
71 |
}; |
72 |
|
73 |
static pthread_once_t once = PTHREAD_ONCE_INIT; |
74 |
static pthread_mutex_t sem_llock; |
75 |
static LIST_HEAD(,sem_nameinfo) sem_list = LIST_HEAD_INITIALIZER(sem_list); |
76 |
|
77 |
static void |
78 |
sem_prefork() |
79 |
{ |
80 |
|
81 |
_pthread_mutex_lock(&sem_llock); |
82 |
} |
83 |
|
84 |
static void |
85 |
sem_postfork() |
86 |
{ |
87 |
_pthread_mutex_unlock(&sem_llock); |
88 |
} |
89 |
|
90 |
static void |
91 |
sem_child_postfork() |
92 |
{ |
93 |
_pthread_mutex_unlock(&sem_llock); |
94 |
} |
95 |
|
96 |
static void |
97 |
sem_module_init(void) |
98 |
{ |
99 |
pthread_mutexattr_t ma; |
100 |
|
101 |
_pthread_mutexattr_init(&ma); |
102 |
_pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE); |
103 |
_pthread_mutex_init(&sem_llock, &ma); |
104 |
_pthread_mutexattr_destroy(&ma); |
105 |
_pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork); |
106 |
} |
107 |
|
108 |
static inline int |
109 |
sem_check_validity(sem_t *sem) |
110 |
{ |
111 |
|
112 |
if (sem->_magic == SEM_MAGIC) |
113 |
return (0); |
114 |
else { |
115 |
errno = EINVAL; |
116 |
return (-1); |
117 |
} |
118 |
} |
119 |
|
120 |
int |
121 |
_sem_init(sem_t *sem, int pshared, unsigned int value) |
122 |
{ |
123 |
|
124 |
if (value > SEM_VALUE_MAX) { |
125 |
errno = EINVAL; |
126 |
return (-1); |
127 |
} |
128 |
|
129 |
bzero(sem, sizeof(sem_t)); |
130 |
sem->_magic = SEM_MAGIC; |
131 |
sem->_kern._count = (u_int32_t)value; |
132 |
sem->_kern._has_waiters = 0; |
133 |
sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0; |
134 |
return (0); |
135 |
} |
136 |
|
137 |
sem_t * |
138 |
_sem_open(const char *name, int flags, ...) |
139 |
{ |
140 |
char path[PATH_MAX]; |
141 |
|
142 |
struct stat sb; |
143 |
va_list ap; |
144 |
struct sem_nameinfo *ni = NULL; |
145 |
sem_t *sem = NULL; |
146 |
int fd = -1, mode, len, errsave; |
147 |
int value = 0; |
148 |
|
149 |
if (name[0] != '/') { |
150 |
errno = EINVAL; |
151 |
return (SEM_FAILED); |
152 |
} |
153 |
name++; |
154 |
|
155 |
if (flags & ~(O_CREAT|O_EXCL)) { |
156 |
errno = EINVAL; |
157 |
return (SEM_FAILED); |
158 |
} |
159 |
|
160 |
_pthread_once(&once, sem_module_init); |
161 |
|
162 |
_pthread_mutex_lock(&sem_llock); |
163 |
LIST_FOREACH(ni, &sem_list, next) { |
164 |
if (strcmp(name, ni->name) == 0) { |
165 |
if ((flags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) { |
166 |
_pthread_mutex_unlock(&sem_llock); |
167 |
errno = EEXIST; |
168 |
return (SEM_FAILED); |
169 |
} else { |
170 |
ni->open_count++; |
171 |
sem = ni->sem; |
172 |
_pthread_mutex_unlock(&sem_llock); |
173 |
return (sem); |
174 |
} |
175 |
} |
176 |
} |
177 |
|
178 |
if (flags & O_CREAT) { |
179 |
va_start(ap, flags); |
180 |
mode = va_arg(ap, int); |
181 |
value = va_arg(ap, int); |
182 |
va_end(ap); |
183 |
} |
184 |
|
185 |
len = sizeof(*ni) + strlen(name) + 1; |
186 |
ni = (struct sem_nameinfo *)malloc(len); |
187 |
if (ni == NULL) { |
188 |
errno = ENOSPC; |
189 |
goto error; |
190 |
} |
191 |
|
192 |
ni->name = (char *)(ni+1); |
193 |
strcpy(ni->name, name); |
194 |
|
195 |
strcpy(path, SEM_PREFIX); |
196 |
if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { |
197 |
errno = ENAMETOOLONG; |
198 |
goto error; |
199 |
} |
200 |
|
201 |
fd = _open(path, flags|O_RDWR, mode); |
202 |
if (fd == -1) |
203 |
goto error; |
204 |
if (flock(fd, LOCK_EX) == -1) |
205 |
goto error; |
206 |
if (_fstat(fd, &sb)) { |
207 |
flock(fd, LOCK_UN); |
208 |
goto error; |
209 |
} |
210 |
if (sb.st_size < sizeof(sem_t)) { |
211 |
sem_t tmp; |
212 |
|
213 |
tmp._magic = SEM_MAGIC; |
214 |
tmp._kern._has_waiters = 0; |
215 |
tmp._kern._count = value; |
216 |
tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED; |
217 |
if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { |
218 |
flock(fd, LOCK_UN); |
219 |
goto error; |
220 |
} |
221 |
} |
222 |
flock(fd, LOCK_UN); |
223 |
sem = (sem_t *)mmap(NULL, sizeof(sem_t), PROT_READ|PROT_WRITE, |
224 |
MAP_SHARED|MAP_NOSYNC, fd, 0); |
225 |
if (sem == MAP_FAILED) { |
226 |
sem = NULL; |
227 |
if (errno == ENOMEM) |
228 |
errno = ENOSPC; |
229 |
goto error; |
230 |
} |
231 |
if (sem->_magic != SEM_MAGIC) { |
232 |
errno = EINVAL; |
233 |
goto error; |
234 |
} |
235 |
ni->open_count = 1; |
236 |
ni->sem = sem; |
237 |
LIST_INSERT_HEAD(&sem_list, ni, next); |
238 |
_pthread_mutex_unlock(&sem_llock); |
239 |
_close(fd); |
240 |
return (sem); |
241 |
|
242 |
error: |
243 |
errsave = errno; |
244 |
_pthread_mutex_unlock(&sem_llock); |
245 |
if (fd != -1) |
246 |
_close(fd); |
247 |
if (sem != NULL) |
248 |
munmap(sem, sizeof(sem_t)); |
249 |
free(ni); |
250 |
errno = errsave; |
251 |
return (SEM_FAILED); |
252 |
} |
253 |
|
254 |
int |
255 |
_sem_close(sem_t *sem) |
256 |
{ |
257 |
struct sem_nameinfo *ni; |
258 |
|
259 |
if (sem_check_validity(sem) != 0) |
260 |
return (-1); |
261 |
|
262 |
if (!(sem->_kern._flags & SEM_NAMED)) { |
263 |
errno = EINVAL; |
264 |
return (-1); |
265 |
} |
266 |
|
267 |
_pthread_once(&once, sem_module_init); |
268 |
|
269 |
_pthread_mutex_lock(&sem_llock); |
270 |
LIST_FOREACH(ni, &sem_list, next) { |
271 |
if (sem == ni->sem) { |
272 |
if (--ni->open_count > 0) { |
273 |
_pthread_mutex_unlock(&sem_llock); |
274 |
return (0); |
275 |
} |
276 |
else |
277 |
break; |
278 |
} |
279 |
} |
280 |
|
281 |
if (ni) { |
282 |
LIST_REMOVE(ni, next); |
283 |
_pthread_mutex_unlock(&sem_llock); |
284 |
munmap(sem, sizeof(*sem)); |
285 |
free(ni); |
286 |
return (0); |
287 |
} |
288 |
_pthread_mutex_unlock(&sem_llock); |
289 |
errno = EINVAL; |
290 |
return (-1); |
291 |
} |
292 |
|
293 |
int |
294 |
_sem_unlink(const char *name) |
295 |
{ |
296 |
char path[PATH_MAX]; |
297 |
|
298 |
if (name[0] != '/') { |
299 |
errno = ENOENT; |
300 |
return -1; |
301 |
} |
302 |
name++; |
303 |
|
304 |
strcpy(path, SEM_PREFIX); |
305 |
if (strlcat(path, name, sizeof(path)) >= sizeof(path)) { |
306 |
errno = ENAMETOOLONG; |
307 |
return (-1); |
308 |
} |
309 |
return unlink(path); |
310 |
} |
311 |
|
312 |
int |
313 |
_sem_destroy(sem_t *sem) |
314 |
{ |
315 |
|
316 |
if (sem_check_validity(sem) != 0) |
317 |
return (-1); |
318 |
|
319 |
if (sem->_kern._flags & SEM_NAMED) { |
320 |
errno = EINVAL; |
321 |
return (-1); |
322 |
} |
323 |
sem->_magic = 0; |
324 |
return (0); |
325 |
} |
326 |
|
327 |
int |
328 |
_sem_getvalue(sem_t * __restrict sem, int * __restrict sval) |
329 |
{ |
330 |
|
331 |
if (sem_check_validity(sem) != 0) |
332 |
return (-1); |
333 |
|
334 |
*sval = (int)sem->_kern._count; |
335 |
return (0); |
336 |
} |
337 |
|
338 |
static __inline int |
339 |
usem_wake(struct _usem *sem) |
340 |
{ |
341 |
if (!sem->_has_waiters) |
342 |
return (0); |
343 |
return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL); |
344 |
} |
345 |
|
346 |
static __inline int |
347 |
usem_wait(struct _usem *sem, const struct timespec *timeout) |
348 |
{ |
349 |
if (timeout && (timeout->tv_sec < 0 || (timeout->tv_sec == 0 && |
350 |
timeout->tv_nsec <= 0))) { |
351 |
errno = ETIMEDOUT; |
352 |
return (-1); |
353 |
} |
354 |
return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0, NULL, |
355 |
__DECONST(void*, timeout)); |
356 |
} |
357 |
|
358 |
int |
359 |
_sem_trywait(sem_t *sem) |
360 |
{ |
361 |
int val; |
362 |
|
363 |
if (sem_check_validity(sem) != 0) |
364 |
return (-1); |
365 |
|
366 |
while ((val = sem->_kern._count) > 0) { |
367 |
if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) |
368 |
return (0); |
369 |
} |
370 |
errno = EAGAIN; |
371 |
return (-1); |
372 |
} |
373 |
|
374 |
#define TIMESPEC_SUB(dst, src, val) \ |
375 |
do { \ |
376 |
(dst)->tv_sec = (src)->tv_sec - (val)->tv_sec; \ |
377 |
(dst)->tv_nsec = (src)->tv_nsec - (val)->tv_nsec; \ |
378 |
if ((dst)->tv_nsec < 0) { \ |
379 |
(dst)->tv_sec--; \ |
380 |
(dst)->tv_nsec += 1000000000; \ |
381 |
} \ |
382 |
} while (0) |
383 |
|
384 |
|
385 |
int |
386 |
_sem_timedwait(sem_t * __restrict sem, |
387 |
const struct timespec * __restrict abstime) |
388 |
{ |
389 |
struct timespec ts, ts2; |
390 |
int val, retval; |
391 |
|
392 |
if (sem_check_validity(sem) != 0) |
393 |
return (-1); |
394 |
|
395 |
retval = 0; |
396 |
for (;;) { |
397 |
while ((val = sem->_kern._count) > 0) { |
398 |
if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1)) |
399 |
return (0); |
400 |
} |
401 |
|
402 |
if (retval) { |
403 |
_pthread_testcancel(); |
404 |
break; |
405 |
} |
406 |
|
407 |
/* |
408 |
* The timeout argument is only supposed to |
409 |
* be checked if the thread would have blocked. |
410 |
*/ |
411 |
if (abstime != NULL) { |
412 |
if (abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) { |
413 |
errno = EINVAL; |
414 |
return (-1); |
415 |
} |
416 |
clock_gettime(CLOCK_REALTIME, &ts); |
417 |
TIMESPEC_SUB(&ts2, abstime, &ts); |
418 |
} |
419 |
_pthread_cancel_enter(1); |
420 |
retval = usem_wait(&sem->_kern, abstime ? &ts2 : NULL); |
421 |
_pthread_cancel_leave(0); |
422 |
} |
423 |
return (retval); |
424 |
} |
425 |
|
426 |
int |
427 |
_sem_wait(sem_t *sem) |
428 |
{ |
429 |
return _sem_timedwait(sem, NULL); |
430 |
} |
431 |
|
432 |
/* |
433 |
* POSIX: |
434 |
* The sem_post() interface is reentrant with respect to signals and may be |
435 |
* invoked from a signal-catching function. |
436 |
* The implementation does not use lock, so it should be safe. |
437 |
*/ |
438 |
int |
439 |
_sem_post(sem_t *sem) |
440 |
{ |
441 |
|
442 |
if (sem_check_validity(sem) != 0) |
443 |
return (-1); |
444 |
|
445 |
atomic_add_rel_int(&sem->_kern._count, 1); |
446 |
return usem_wake(&sem->_kern); |
447 |
} |