kernel - Implement ppoll system call with precise microseconds timeout.
[dragonfly.git] / lib / libthread_xu / thread / thr_sem.c
blob27cf1c9a26249c34370a2555790a5c2b30efdd71
1 /*
2 * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3 * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice(s), this list of conditions and the following disclaimer as
11 * the first lines of this file unmodified other than the possible
12 * addition of one or more copyright notices.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice(s), this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "namespace.h"
32 #include <machine/tls.h>
33 #include <sys/semaphore.h>
34 #include <sys/mman.h>
35 #include <sys/queue.h>
36 #include <sys/stat.h>
37 #include <sys/types.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <limits.h>
42 #include <pthread.h>
43 #include <stdarg.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47 #include <unistd.h>
48 #include "un-namespace.h"
49 #include "thr_private.h"
51 #define container_of(ptr, type, member) \
52 ({ \
53 __typeof(((type *)0)->member) *_p = (ptr); \
54 (type *)((char *)_p - offsetof(type, member)); \
58 * Semaphore definitions.
60 struct sem {
61 u_int32_t magic;
62 volatile umtx_t count;
63 int semid;
64 int unused; /* pad */
67 #define SEM_MAGIC ((u_int32_t) 0x09fa4012)
69 static char const *sem_prefix = "/var/run/sem";
73 * POSIX requires that two successive calls to sem_open return
74 * the same address if no call to unlink nor close have been
75 * done in the middle. For that, we keep a list of open semaphore
76 * and search for an existing one before remapping a semaphore.
77 * We have to keep the fd open to check for races.
79 * Example :
80 * sem_open("/test", O_CREAT | O_EXCL...) -> fork() ->
81 * parent :
82 * sem_unlink("/test") -> sem_open("/test", O_CREAT | O_EXCl ...)
83 * child :
84 * sem_open("/test", 0).
85 * We need to check that the cached mapping is the one of the most up
86 * to date file linked at this name, or child process will reopen the
87 * *old* version of the semaphore, which is wrong.
89 * fstat and nlink check is used to test for this race.
92 struct sem_info {
93 int open_count;
94 ino_t inode;
95 dev_t dev;
96 int fd;
97 sem_t sem;
98 LIST_ENTRY(sem_info) next;
103 static pthread_once_t once = PTHREAD_ONCE_INIT;
104 static pthread_mutex_t sem_lock;
105 static LIST_HEAD(,sem_info) sem_list = LIST_HEAD_INITIALIZER(sem_list);
108 #define SEMID_LWP 0
109 #define SEMID_FORK 1
110 #define SEMID_NAMED 2
112 static void
113 sem_prefork(void)
115 _pthread_mutex_lock(&sem_lock);
118 static void
119 sem_postfork(void)
121 _pthread_mutex_unlock(&sem_lock);
124 static void
125 sem_child_postfork(void)
127 _pthread_mutex_unlock(&sem_lock);
130 static void
131 sem_module_init(void)
133 pthread_mutexattr_t ma;
135 _pthread_mutexattr_init(&ma);
136 _pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_RECURSIVE);
137 _pthread_mutex_init(&sem_lock, &ma);
138 _pthread_mutexattr_destroy(&ma);
139 _pthread_atfork(sem_prefork, sem_postfork, sem_child_postfork);
142 static inline int
143 sem_check_validity(sem_t *sem)
146 if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
147 return (0);
148 } else {
149 errno = EINVAL;
150 return (-1);
154 static sem_t
155 sem_alloc(unsigned int value, int pshared)
157 sem_t sem;
158 int semid;
160 if (value > SEM_VALUE_MAX) {
161 errno = EINVAL;
162 return (NULL);
164 if (pshared) {
165 static __thread sem_t sem_base;
166 static __thread int sem_count;
168 if (sem_base == NULL) {
169 sem_base = mmap(NULL, getpagesize(),
170 PROT_READ | PROT_WRITE,
171 MAP_ANON | MAP_SHARED,
172 -1, 0);
173 sem_count = getpagesize() / sizeof(*sem);
175 sem = sem_base++;
176 if (--sem_count == 0)
177 sem_base = NULL;
178 semid = SEMID_FORK;
179 } else {
180 sem = malloc(sizeof(struct sem));
181 semid = SEMID_LWP;
183 if (sem == NULL) {
184 errno = ENOSPC;
185 return (NULL);
187 sem->magic = SEM_MAGIC;
188 sem->count = (u_int32_t)value;
189 sem->semid = semid;
190 return (sem);
194 _sem_init(sem_t *sem, int pshared, unsigned int value)
196 if (sem == NULL) {
197 errno = EINVAL;
198 return (-1);
201 *sem = sem_alloc(value, pshared);
202 if (*sem == NULL)
203 return (-1);
204 return (0);
208 _sem_destroy(sem_t *sem)
210 if (sem_check_validity(sem) != 0) {
211 errno = EINVAL;
212 return (-1);
215 (*sem)->magic = 0;
217 switch ((*sem)->semid) {
218 case SEMID_LWP:
219 free(*sem);
220 break;
221 case SEMID_FORK:
222 /* memory is left intact */
223 break;
224 default:
225 errno = EINVAL;
226 return (-1);
228 return (0);
232 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
234 if (sem_check_validity(sem) != 0)
235 return (-1);
237 *sval = (*sem)->count;
238 return (0);
242 _sem_trywait(sem_t *sem)
244 int val;
246 if (sem_check_validity(sem) != 0)
247 return (-1);
249 while ((val = (*sem)->count) > 0) {
250 if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
251 return (0);
253 errno = EAGAIN;
254 return (-1);
258 _sem_wait(sem_t *sem)
260 struct pthread *curthread;
261 int val, oldcancel, retval;
263 if (sem_check_validity(sem) != 0)
264 return (-1);
266 curthread = tls_get_curthread();
267 _pthread_testcancel();
268 do {
269 while ((val = (*sem)->count) > 0) {
270 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
271 return (0);
273 oldcancel = _thr_cancel_enter(curthread);
274 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
275 _thr_cancel_leave(curthread, oldcancel);
276 } while (retval == 0);
277 errno = retval;
278 return (-1);
282 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime)
284 struct timespec ts, ts2;
285 struct pthread *curthread;
286 int val, oldcancel, retval;
288 if (sem_check_validity(sem) != 0)
289 return (-1);
291 curthread = tls_get_curthread();
294 * The timeout argument is only supposed to
295 * be checked if the thread would have blocked.
297 _pthread_testcancel();
298 do {
299 while ((val = (*sem)->count) > 0) {
300 if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
301 return (0);
303 if (abstime == NULL ||
304 abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
305 errno = EINVAL;
306 return (-1);
308 clock_gettime(CLOCK_REALTIME, &ts);
309 TIMESPEC_SUB(&ts2, abstime, &ts);
310 oldcancel = _thr_cancel_enter(curthread);
311 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
312 CLOCK_REALTIME);
313 _thr_cancel_leave(curthread, oldcancel);
314 } while (retval == 0);
315 errno = retval;
316 return (-1);
320 _sem_post(sem_t *sem)
322 int val;
324 if (sem_check_validity(sem) != 0)
325 return (-1);
328 * sem_post() is required to be safe to call from within
329 * signal handlers, these code should work as that.
331 do {
332 val = (*sem)->count;
333 } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
334 _thr_umtx_wake(&(*sem)->count, val + 1);
335 return (0);
338 static int
339 get_path(const char *name, char *path, size_t len, char const **prefix)
341 size_t path_len;
343 *prefix = NULL;
345 if (name[0] == '/') {
346 *prefix = getenv("LIBTHREAD_SEM_PREFIX");
348 if (*prefix == NULL)
349 *prefix = sem_prefix;
351 path_len = strlcpy(path, *prefix, len);
353 if (path_len > len) {
354 return (ENAMETOOLONG);
358 path_len = strlcat(path, name, len);
360 if (path_len > len)
361 return (ENAMETOOLONG);
363 return (0);
367 static sem_t *
368 sem_get_mapping(ino_t inode, dev_t dev)
370 struct sem_info *ni;
371 struct stat sbuf;
373 LIST_FOREACH(ni, &sem_list, next) {
374 if (ni->inode == inode && ni->dev == dev) {
375 /* Check for races */
376 if(_fstat(ni->fd, &sbuf) == 0) {
377 if (sbuf.st_nlink > 0) {
378 ni->open_count++;
379 return (&ni->sem);
380 } else {
381 ni->inode = 0;
382 LIST_REMOVE(ni, next);
385 return (SEM_FAILED);
390 return (SEM_FAILED);
394 static sem_t *
395 sem_add_mapping(ino_t inode, dev_t dev, sem_t sem, int fd)
397 struct sem_info *ni;
399 ni = malloc(sizeof(struct sem_info));
400 if (ni == NULL) {
401 errno = ENOSPC;
402 return (SEM_FAILED);
405 bzero(ni, sizeof(*ni));
406 ni->open_count = 1;
407 ni->sem = sem;
408 ni->fd = fd;
409 ni->inode = inode;
410 ni->dev = dev;
412 LIST_INSERT_HEAD(&sem_list, ni, next);
414 return (&ni->sem);
417 static int
418 sem_close_mapping(sem_t *sem)
420 struct sem_info *ni;
422 if ((*sem)->semid != SEMID_NAMED)
423 return (EINVAL);
425 ni = container_of(sem, struct sem_info, sem);
427 if ( --ni->open_count > 0) {
428 return (0);
429 } else {
430 if (ni->inode != 0) {
431 LIST_REMOVE(ni, next);
433 munmap(ni->sem, getpagesize());
434 __sys_close(ni->fd);
435 free(ni);
436 return (0);
440 sem_t *
441 _sem_open(const char *name, int oflag, ...)
443 char path[PATH_MAX];
444 char tmppath[PATH_MAX];
445 char const *prefix = NULL;
446 size_t path_len;
447 int error, fd, create;
448 sem_t *sem;
449 sem_t semtmp;
450 va_list ap;
451 mode_t mode;
452 struct stat sbuf;
453 unsigned int value = 0;
455 create = 0;
456 error = 0;
457 fd = -1;
458 sem = SEM_FAILED;
461 * Bail out if invalid flags specified.
463 if (oflag & ~(O_CREAT|O_EXCL)) {
464 errno = EINVAL;
465 return (SEM_FAILED);
468 oflag |= O_RDWR;
469 oflag |= O_CLOEXEC;
471 if (name == NULL) {
472 errno = EINVAL;
473 return (SEM_FAILED);
476 _pthread_once(&once, sem_module_init);
478 _pthread_mutex_lock(&sem_lock);
480 error = get_path(name, path, PATH_MAX, &prefix);
481 if (error) {
482 errno = error;
483 goto error;
486 retry:
487 fd = __sys_open(path, O_RDWR | O_CLOEXEC);
489 if (fd > 0) {
491 if ((oflag & O_EXCL) == O_EXCL) {
492 __sys_close(fd);
493 errno = EEXIST;
494 goto error;
497 if (_fstat(fd, &sbuf) != 0) {
498 /* Bad things happened, like another thread closing our descriptor */
499 __sys_close(fd);
500 errno = EINVAL;
501 goto error;
504 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev);
506 if (sem != SEM_FAILED) {
507 __sys_close(fd);
508 goto done;
511 if ((sbuf.st_mode & S_IFREG) == 0) {
512 /* We only want regular files here */
513 __sys_close(fd);
514 errno = EINVAL;
515 goto error;
517 } else if ((oflag & O_CREAT) && errno == ENOENT) {
519 va_start(ap, oflag);
521 mode = (mode_t) va_arg(ap, int);
522 value = (unsigned int) va_arg(ap, int);
524 va_end(ap);
526 if (value > SEM_VALUE_MAX) {
527 errno = EINVAL;
528 goto error;
531 strlcpy(tmppath, prefix, sizeof(tmppath));
532 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath));
534 if (path_len > sizeof(tmppath)) {
535 errno = ENAMETOOLONG;
536 goto error;
540 fd = mkstemp(tmppath);
542 if ( fd == -1 ) {
543 errno = EINVAL;
544 goto error;
547 error = fchmod(fd, mode);
548 if ( error == -1 ) {
549 __sys_close(fd);
550 errno = EINVAL;
551 goto error;
554 error = __sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
555 if ( error == -1 ) {
556 __sys_close(fd);
557 errno = EINVAL;
558 goto error;
561 create = 1;
564 if (fd == -1) {
565 switch (errno) {
566 case ENOTDIR:
567 case EISDIR:
568 case EMLINK:
569 case ELOOP:
570 errno = EINVAL;
571 break;
572 case EDQUOT:
573 case EIO:
574 errno = ENOSPC;
575 break;
576 case EROFS:
577 errno = EACCES;
579 goto error;
582 semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
583 MAP_NOSYNC | MAP_SHARED, fd, 0);
585 if (semtmp == MAP_FAILED) {
586 if (errno != EACCES && errno != EMFILE)
587 errno = ENOMEM;
589 if (create)
590 unlink(tmppath);
592 __sys_close(fd);
593 goto error;
596 if (create) {
597 ftruncate(fd, sizeof(struct sem));
598 semtmp->magic = SEM_MAGIC;
599 semtmp->count = (u_int32_t)value;
600 semtmp->semid = SEMID_NAMED;
602 if (link(tmppath, path) != 0) {
603 munmap(semtmp, getpagesize());
604 __sys_close(fd);
605 unlink(tmppath);
607 if (errno == EEXIST && (oflag & O_EXCL) == 0) {
608 goto retry;
611 goto error;
613 unlink(tmppath);
615 if (_fstat(fd, &sbuf) != 0) {
616 /* Bad things happened, like another thread closing our descriptor */
617 munmap(semtmp, getpagesize());
618 __sys_close(fd);
619 errno = EINVAL;
620 goto error;
624 sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd);
626 done:
627 _pthread_mutex_unlock(&sem_lock);
628 return (sem);
630 error:
631 _pthread_mutex_unlock(&sem_lock);
632 return (SEM_FAILED);
637 _sem_close(sem_t *sem)
639 _pthread_once(&once, sem_module_init);
641 _pthread_mutex_lock(&sem_lock);
643 if (sem_check_validity(sem)) {
644 _pthread_mutex_unlock(&sem_lock);
645 errno = EINVAL;
646 return (-1);
649 if (sem_close_mapping(sem)) {
650 _pthread_mutex_unlock(&sem_lock);
651 errno = EINVAL;
652 return (-1);
654 _pthread_mutex_unlock(&sem_lock);
656 return (0);
660 _sem_unlink(const char *name)
662 char path[PATH_MAX];
663 const char *prefix;
664 int error;
666 error = get_path(name, path, PATH_MAX, &prefix);
667 if (error) {
668 errno = error;
669 return (-1);
672 error = unlink(path);
674 if(error) {
675 if (errno != ENAMETOOLONG && errno != ENOENT)
676 errno = EACCES;
678 return (-1);
681 return (0);
684 __strong_reference(_sem_destroy, sem_destroy);
685 __strong_reference(_sem_getvalue, sem_getvalue);
686 __strong_reference(_sem_init, sem_init);
687 __strong_reference(_sem_trywait, sem_trywait);
688 __strong_reference(_sem_wait, sem_wait);
689 __strong_reference(_sem_timedwait, sem_timedwait);
690 __strong_reference(_sem_post, sem_post);
691 __strong_reference(_sem_open, sem_open);
692 __strong_reference(_sem_close, sem_close);
693 __strong_reference(_sem_unlink, sem_unlink);