pthreads - Fix rtld-elf and libthread_xu
[dragonfly.git] / lib / libthread_xu / thread / thr_sem.c
1 /*
2  * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4  * All rights reserved.
5  *
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.
17  *
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.
29  */
30
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>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <pthread.h>
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <time.h>
46 #include <unistd.h>
47 #ifdef _PTHREADS_DEBUGGING
48 #include <stdio.h>
49 #endif
50 #include "un-namespace.h"
51
52 #include "thr_private.h"
53
54 #define cpu_ccfence()        __asm __volatile("" : : : "memory")
55
56 #define container_of(ptr, type, member)                         \
57 ({                                                              \
58         __typeof(((type *)0)->member) *_p = (ptr);              \
59         (type *)((char *)_p - offsetof(type, member));          \
60 })
61
62 /*
63  * Semaphore definitions.
64  */
65 struct sem {
66         volatile umtx_t         count;
67         u_int32_t               magic;
68         int                     semid;
69         int                     unused; /* pad */
70 } __cachealign;
71
72 #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
73
74 static char const *sem_prefix = "/var/run/sem";
75
76
77 /*
78  * POSIX requires that two successive calls to sem_open return
79  * the same address if no call to unlink nor close have been
80  * done in the middle. For that, we keep a list of open semaphore
81  * and search for an existing one before remapping a semaphore.
82  * We have to keep the fd open to check for races.
83  *
84  * Example :
85  * sem_open("/test", O_CREAT | O_EXCL...) -> fork() ->
86  * parent :
87  *   sem_unlink("/test") -> sem_open("/test", O_CREAT | O_EXCl ...)
88  * child :
89  *   sem_open("/test", 0).
90  * We need to check that the cached mapping is the one of the most up
91  * to date file linked at this name, or child process will reopen the
92  * *old* version of the semaphore, which is wrong.
93  *
94  * fstat and nlink check is used to test for this race.
95  */
96
97 struct sem_info {
98         int open_count;
99         ino_t inode;
100         dev_t dev;
101         int fd;
102         sem_t sem;
103         LIST_ENTRY(sem_info) next;
104 };
105
106 static pthread_mutex_t sem_lock;
107 static LIST_HEAD(,sem_info) sem_list = LIST_HEAD_INITIALIZER(sem_list);
108
109 #ifdef _PTHREADS_DEBUGGING
110
111 static
112 void
113 sem_log(const char *ctl, ...)
114 {
115         char buf[256];
116         va_list va;
117         size_t len;
118
119         va_start(va, ctl);
120         len = vsnprintf(buf, sizeof(buf), ctl, va);
121         va_end(va);
122         _thr_log(buf, len);
123 }
124
125 #else
126
127 static __inline
128 void
129 sem_log(const char *ctl __unused, ...)
130 {
131 }
132
133 #endif
134
135 #define SEMID_LWP       0
136 #define SEMID_FORK      1
137 #define SEMID_NAMED     2
138
139 static void
140 sem_prefork(void)
141 {
142         _pthread_mutex_lock(&sem_lock);
143 }
144
145 static void
146 sem_postfork(void)
147 {
148         _pthread_mutex_unlock(&sem_lock);
149 }
150
151 static void
152 sem_child_postfork(void)
153 {
154         _pthread_mutex_unlock(&sem_lock);
155 }
156
157 void
158 _thr_sem_init(void)
159 {
160         pthread_mutexattr_t ma;
161
162         _pthread_mutexattr_init(&ma);
163         _pthread_mutexattr_settype(&ma,  PTHREAD_MUTEX_RECURSIVE);
164         _pthread_mutex_init(&sem_lock, &ma);
165         _pthread_mutexattr_destroy(&ma);
166         _thr_atfork_kern(sem_prefork, sem_postfork, sem_child_postfork);
167 }
168
169 static inline int
170 sem_check_validity(sem_t *sem)
171 {
172
173         if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
174                 return (0);
175         } else {
176                 errno = EINVAL;
177                 return (-1);
178         }
179 }
180
181 static sem_t
182 sem_alloc(unsigned int value, int pshared)
183 {
184         sem_t sem;
185         int semid;
186
187         if (value > SEM_VALUE_MAX) {
188                 errno = EINVAL;
189                 return (NULL);
190         }
191         if (pshared) {
192                 static __thread sem_t sem_base;
193                 static __thread int sem_count;
194
195                 if (sem_base == NULL) {
196                         sem_base = mmap(NULL, getpagesize(),
197                                         PROT_READ | PROT_WRITE,
198                                         MAP_ANON | MAP_SHARED,
199                                         -1, 0);
200                         sem_count = getpagesize() / sizeof(*sem);
201                 }
202                 sem = sem_base++;
203                 if (--sem_count == 0)
204                         sem_base = NULL;
205                 semid = SEMID_FORK;
206         } else {
207                 sem = malloc(sizeof(struct sem));
208                 semid = SEMID_LWP;
209         }
210         if (sem == NULL) {
211                 errno = ENOSPC;
212                 return (NULL);
213         }
214         sem->magic = SEM_MAGIC;
215         sem->count = (u_int32_t)value;
216         sem->semid = semid;
217
218         sem_log("sem_alloc %p (%d)\n", sem, value);
219
220         return (sem);
221 }
222
223 int
224 _sem_init(sem_t *sem, int pshared, unsigned int value)
225 {
226         if (sem == NULL) {
227                 errno = EINVAL;
228                 return (-1);
229         }
230
231         *sem = sem_alloc(value, pshared);
232         if (*sem == NULL)
233                 return (-1);
234         return (0);
235 }
236
237 int
238 _sem_destroy(sem_t *sem)
239 {
240         if (sem_check_validity(sem) != 0) {
241                 errno = EINVAL;
242                 return (-1);
243         }
244
245         (*sem)->magic = 0;
246
247         switch ((*sem)->semid) {
248                 case SEMID_LWP:
249                         free(*sem);
250                         break;
251                 case SEMID_FORK:
252                         /* memory is left intact */
253                         break;
254                 default:
255                         errno = EINVAL;
256                         return (-1);
257         }
258         return (0);
259 }
260
261 int
262 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
263 {
264         if (sem_check_validity(sem) != 0) {
265                 errno = EINVAL;
266                 return (-1);
267         }
268         *sval = (*sem)->count;
269
270         return (0);
271 }
272
273 int
274 _sem_trywait(sem_t *sem)
275 {
276         int val;
277
278         if (sem_check_validity(sem) != 0) {
279                 errno = EINVAL;
280                 return (-1);
281         }
282
283         sem_log("sem_trywait %p %d\n", *sem, (*sem)->count);
284         while ((val = (*sem)->count) > 0) {
285                 cpu_ccfence();
286                 if (atomic_cmpset_int(&(*sem)->count, val, val - 1)) {
287                         sem_log("sem_trywait %p %d (success)\n", *sem, val - 1);
288                         return (0);
289                 }
290         }
291         errno = EAGAIN;
292         sem_log("sem_trywait %p %d (failure)\n", *sem, val);
293         return (-1);
294 }
295
296 int
297 _sem_wait(sem_t *sem)
298 {
299         struct pthread *curthread;
300         int val, oldcancel, retval;
301
302         if (sem_check_validity(sem) != 0) {
303                 errno = EINVAL;
304                 return (-1);
305         }
306
307         curthread = tls_get_curthread();
308         _pthread_testcancel();
309
310         sem_log("sem_wait %p %d (begin)\n", *sem, (*sem)->count);
311
312         do {
313                 cpu_ccfence();
314                 while ((val = (*sem)->count) > 0) {
315                         cpu_ccfence();
316                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) {
317                                 sem_log("sem_wait %p %d (success)\n",
318                                         *sem, val - 1);
319                                 return (0);
320                         }
321                 }
322                 oldcancel = _thr_cancel_enter(curthread);
323                 sem_log("sem_wait %p %d (wait)\n", *sem, val);
324                 retval = _thr_umtx_wait_intr(&(*sem)->count, 0);
325                 sem_log("sem_wait %p %d (wait return %d)\n",
326                         *sem, (*sem)->count, retval);
327                 _thr_cancel_leave(curthread, oldcancel);
328                 /* ignore retval */
329         } while (retval != EINTR);
330
331         sem_log("sem_wait %p %d (error %d)\n", *sem, retval);
332         errno = retval;
333
334         return (-1);
335 }
336
337 int
338 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime)
339 {
340         struct timespec ts, ts2;
341         struct pthread *curthread;
342         int val, oldcancel, retval;
343
344         if (sem_check_validity(sem) != 0)
345                 return (-1);
346
347         curthread = tls_get_curthread();
348         _pthread_testcancel();
349         sem_log("sem_timedwait %p %d (begin)\n", *sem, (*sem)->count);
350
351         /*
352          * The timeout argument is only supposed to
353          * be checked if the thread would have blocked.
354          */
355         do {
356                 while ((val = (*sem)->count) > 0) {
357                         cpu_ccfence();
358                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1)) {
359                                 sem_log("sem_wait %p %d (success)\n",
360                                         *sem, val - 1);
361                                 return (0);
362                         }
363                 }
364                 if (abstime == NULL ||
365                     abstime->tv_nsec >= 1000000000 ||
366                     abstime->tv_nsec < 0) {
367                         sem_log("sem_wait %p %d (bad abstime)\n", *sem, val);
368                         errno = EINVAL;
369                         return (-1);
370                 }
371                 clock_gettime(CLOCK_REALTIME, &ts);
372                 TIMESPEC_SUB(&ts2, abstime, &ts);
373                 oldcancel = _thr_cancel_enter(curthread);
374                 sem_log("sem_wait %p %d (wait)\n", *sem, val);
375                 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
376                                         CLOCK_REALTIME);
377                 sem_log("sem_wait %p %d (wait return %d)\n",
378                         *sem, (*sem)->count, retval);
379                 _thr_cancel_leave(curthread, oldcancel);
380         } while (retval != ETIMEDOUT && retval != EINTR);
381
382         sem_log("sem_wait %p %d (error %d)\n", *sem, retval);
383         errno = retval;
384
385         return (-1);
386 }
387
388 int
389 _sem_post(sem_t *sem)
390 {
391         int val;
392
393         if (sem_check_validity(sem) != 0)
394                 return (-1);
395
396         /*
397          * sem_post() is required to be safe to call from within
398          * signal handlers, these code should work as that.
399          */
400         val = atomic_fetchadd_int(&(*sem)->count, 1) + 1;
401         sem_log("sem_post %p %d\n", *sem, val);
402         _thr_umtx_wake(&(*sem)->count, 0);
403
404         return (0);
405 }
406
407 static int
408 get_path(const char *name, char *path, size_t len, char const **prefix)
409 {
410         size_t path_len;
411
412         *prefix = NULL;
413
414         if (name[0] == '/') {
415                 *prefix = getenv("LIBTHREAD_SEM_PREFIX");
416
417                 if (*prefix == NULL)
418                         *prefix = sem_prefix;
419
420                 path_len = strlcpy(path, *prefix, len);
421
422                 if (path_len > len) {
423                         return (ENAMETOOLONG);
424                 }
425         }
426
427         path_len = strlcat(path, name, len);
428
429         if (path_len > len)
430                 return (ENAMETOOLONG);
431
432         return (0);
433 }
434
435
436 static sem_t *
437 sem_get_mapping(ino_t inode, dev_t dev)
438 {
439         struct sem_info *ni;
440         struct stat sbuf;
441
442         LIST_FOREACH(ni, &sem_list, next) {
443                 if (ni->inode == inode && ni->dev == dev) {
444                         /* Check for races */
445                         if(_fstat(ni->fd, &sbuf) == 0) {
446                                 if (sbuf.st_nlink > 0) {
447                                         ni->open_count++;
448                                         return (&ni->sem);
449                                 } else {
450                                         ni->inode = 0;
451                                         LIST_REMOVE(ni, next);
452                                 }
453                         }
454                         return (SEM_FAILED);
455
456                 }
457         }
458
459         return (SEM_FAILED);
460 }
461
462
463 static sem_t *
464 sem_add_mapping(ino_t inode, dev_t dev, sem_t sem, int fd)
465 {
466         struct sem_info *ni;
467
468         ni = malloc(sizeof(struct sem_info));
469         if (ni == NULL) {
470                 errno = ENOSPC;
471                 return (SEM_FAILED);
472         }
473
474         bzero(ni, sizeof(*ni));
475         ni->open_count = 1;
476         ni->sem = sem;
477         ni->fd = fd;
478         ni->inode = inode;
479         ni->dev = dev;
480
481         LIST_INSERT_HEAD(&sem_list, ni, next);
482
483         return (&ni->sem);
484 }
485
486 static int
487 sem_close_mapping(sem_t *sem)
488 {
489         struct sem_info *ni;
490
491         if ((*sem)->semid != SEMID_NAMED)
492                 return (EINVAL);
493
494         ni = container_of(sem, struct sem_info, sem);
495
496         if ( --ni->open_count > 0) {
497                 return (0);
498         } else {
499                 if (ni->inode != 0) {
500                         LIST_REMOVE(ni, next);
501                 }
502                 munmap(ni->sem, getpagesize());
503                 __sys_close(ni->fd);
504                 free(ni);
505                 return (0);
506         }
507 }
508
509 sem_t *
510 _sem_open(const char *name, int oflag, ...)
511 {
512         char path[PATH_MAX];
513         char tmppath[PATH_MAX];
514         char const *prefix = NULL;
515         size_t path_len;
516         int error, fd, create;
517         sem_t *sem;
518         sem_t semtmp;
519         va_list ap;
520         mode_t mode;
521         struct stat sbuf;
522         unsigned int value = 0;
523
524         create = 0;
525         error = 0;
526         fd = -1;
527         sem = SEM_FAILED;
528
529         /*
530          * Bail out if invalid flags specified.
531          */
532         if (oflag & ~(O_CREAT|O_EXCL)) {
533                 errno = EINVAL;
534                 return (SEM_FAILED);
535         }
536
537         oflag |= O_RDWR;
538         oflag |= O_CLOEXEC;
539
540         if (name == NULL) {
541                 errno = EINVAL;
542                 return (SEM_FAILED);
543         }
544
545         _pthread_mutex_lock(&sem_lock);
546
547         error = get_path(name, path, PATH_MAX, &prefix);
548         if (error) {
549                 errno = error;
550                 goto error;
551         }
552
553 retry:
554         fd = __sys_open(path, O_RDWR | O_CLOEXEC);
555
556         if (fd > 0) {
557
558                 if ((oflag & O_EXCL) == O_EXCL) {
559                         __sys_close(fd);
560                         errno = EEXIST;
561                         goto error;
562                 }
563
564                 if (_fstat(fd, &sbuf) != 0) {
565                         /* Bad things happened, like another thread closing our descriptor */
566                         __sys_close(fd);
567                         errno = EINVAL;
568                         goto error;
569                 }
570
571                 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev);
572
573                 if (sem != SEM_FAILED) {
574                         __sys_close(fd);
575                         goto done;
576                 }
577
578                 if ((sbuf.st_mode & S_IFREG) == 0) {
579                         /* We only want regular files here */
580                         __sys_close(fd);
581                         errno = EINVAL;
582                         goto error;
583                 }
584         } else if ((oflag & O_CREAT) && errno == ENOENT) {
585
586                 va_start(ap, oflag);
587
588                 mode = (mode_t) va_arg(ap, int);
589                 value = (unsigned int) va_arg(ap, int);
590
591                 va_end(ap);
592
593                 if (value > SEM_VALUE_MAX) {
594                         errno = EINVAL;
595                         goto error;
596                 }
597
598                 strlcpy(tmppath, prefix, sizeof(tmppath));
599                 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath));
600
601                 if (path_len > sizeof(tmppath)) {
602                         errno = ENAMETOOLONG;
603                         goto error;
604                 }
605
606
607                 fd = mkstemp(tmppath);
608
609                 if ( fd == -1 ) {
610                         errno = EINVAL;
611                         goto error;
612                 }
613
614                 error = fchmod(fd, mode);
615                 if ( error == -1 ) {
616                         __sys_close(fd);
617                         errno = EINVAL;
618                         goto error;
619                 }
620
621                 error = __sys_fcntl(fd, F_SETFD, FD_CLOEXEC);
622                 if ( error == -1 ) {
623                         __sys_close(fd);
624                         errno = EINVAL;
625                         goto error;
626                 }
627
628                 create = 1;
629         }
630
631         if (fd == -1) {
632                 switch (errno) {
633                         case ENOTDIR:
634                         case EISDIR:
635                         case EMLINK:
636                         case ELOOP:
637                                 errno = EINVAL;
638                                 break;
639                         case EDQUOT:
640                         case EIO:
641                                 errno = ENOSPC;
642                                 break;
643                         case EROFS:
644                                 errno = EACCES;
645                 }
646                 goto error;
647         }
648
649         semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
650                         MAP_NOSYNC | MAP_SHARED, fd, 0);
651
652         if (semtmp == MAP_FAILED) {
653                 if (errno != EACCES && errno != EMFILE)
654                         errno = ENOMEM;
655
656                 if (create)
657                         unlink(tmppath);
658
659                 __sys_close(fd);
660                 goto error;
661         }
662
663         if (create) {
664                 ftruncate(fd, sizeof(struct sem));
665                 semtmp->magic = SEM_MAGIC;
666                 semtmp->count = (u_int32_t)value;
667                 semtmp->semid = SEMID_NAMED;
668
669                 if (link(tmppath, path) != 0) {
670                         munmap(semtmp, getpagesize());
671                         __sys_close(fd);
672                         unlink(tmppath);
673
674                         if (errno == EEXIST && (oflag & O_EXCL) == 0) {
675                                 goto retry;
676                         }
677
678                         goto error;
679                 }
680                 unlink(tmppath);
681
682                 if (_fstat(fd, &sbuf) != 0) {
683                         /* Bad things happened, like another thread closing our descriptor */
684                         munmap(semtmp, getpagesize());
685                         __sys_close(fd);
686                         errno = EINVAL;
687                         goto error;
688                 }
689
690         }
691         sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd);
692
693 done:
694         _pthread_mutex_unlock(&sem_lock);
695         return (sem);
696
697 error:
698         _pthread_mutex_unlock(&sem_lock);
699         return (SEM_FAILED);
700
701 }
702
703 int
704 _sem_close(sem_t *sem)
705 {
706         _pthread_mutex_lock(&sem_lock);
707
708         if (sem_check_validity(sem)) {
709                 _pthread_mutex_unlock(&sem_lock);
710                 errno = EINVAL;
711                 return (-1);
712         }
713
714         if (sem_close_mapping(sem)) {
715                 _pthread_mutex_unlock(&sem_lock);
716                 errno = EINVAL;
717                 return (-1);
718         }
719         _pthread_mutex_unlock(&sem_lock);
720
721         return (0);
722 }
723
724 int
725 _sem_unlink(const char *name)
726 {
727         char path[PATH_MAX];
728         const char *prefix;
729         int error;
730
731         error = get_path(name, path, PATH_MAX, &prefix);
732         if (error) {
733                 errno = error;
734                 return (-1);
735         }
736
737         error = unlink(path);
738
739         if(error) {
740                 if (errno != ENAMETOOLONG && errno != ENOENT)
741                         errno = EACCES;
742
743                 return (-1);
744         }
745
746         return (0);
747 }
748
749 __strong_reference(_sem_destroy, sem_destroy);
750 __strong_reference(_sem_getvalue, sem_getvalue);
751 __strong_reference(_sem_init, sem_init);
752 __strong_reference(_sem_trywait, sem_trywait);
753 __strong_reference(_sem_wait, sem_wait);
754 __strong_reference(_sem_timedwait, sem_timedwait);
755 __strong_reference(_sem_post, sem_post);
756 __strong_reference(_sem_open, sem_open);
757 __strong_reference(_sem_close, sem_close);
758 __strong_reference(_sem_unlink, sem_unlink);