d957e49948e6663e830bfa6a560ff1e216e21ae1
[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
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"
50
51 #define container_of(ptr, type, member)                         \
52 ({                                                              \
53         __typeof(((type *)0)->member) *_p = (ptr);              \
54         (type *)((char *)_p - offsetof(type, member));          \
55 })
56
57 /*
58  * Semaphore definitions.
59  */
60 struct sem {
61         u_int32_t               magic;
62         volatile umtx_t         count;
63         int                     semid;
64         int                     unused; /* pad */
65 };
66
67 #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
68
69 static char const *sem_prefix = "/var/run/sem";
70
71
72 /*
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.
78  *
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.
88  *
89  * fstat and nlink check is used to test for this race.
90  */
91
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;
99 };
100
101
102
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);
106
107
108 #define SEMID_LWP       0
109 #define SEMID_FORK      1
110 #define SEMID_NAMED     2
111
112 static void
113 sem_prefork(void)
114 {
115         _pthread_mutex_lock(&sem_lock);
116 }
117
118 static void
119 sem_postfork(void)
120 {
121         _pthread_mutex_unlock(&sem_lock);
122 }
123
124 static void
125 sem_child_postfork(void)
126 {
127         _pthread_mutex_unlock(&sem_lock);
128 }
129
130 static void
131 sem_module_init(void)
132 {
133         pthread_mutexattr_t ma;
134
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);
140 }
141
142 static inline int
143 sem_check_validity(sem_t *sem)
144 {
145
146         if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
147                 return (0);
148         } else {
149                 errno = EINVAL;
150                 return (-1);
151         }
152 }
153
154 static sem_t
155 sem_alloc(unsigned int value, int pshared)
156 {
157         sem_t sem;
158         int semid;
159
160         if (value > SEM_VALUE_MAX) {
161                 errno = EINVAL;
162                 return (NULL);
163         }
164         if (pshared) {
165                 static __thread sem_t sem_base;
166                 static __thread int sem_count;
167
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);
174                 }
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;
182         }
183         if (sem == NULL) {
184                 errno = ENOSPC;
185                 return (NULL);
186         }
187         sem->magic = SEM_MAGIC;
188         sem->count = (u_int32_t)value;
189         sem->semid = semid;
190         return (sem);
191 }
192
193 int
194 _sem_init(sem_t *sem, int pshared, unsigned int value)
195 {
196         if (sem == NULL) {
197                 errno = EINVAL;
198                 return (-1);
199         }
200
201         *sem = sem_alloc(value, pshared);
202         if (*sem == NULL)
203                 return (-1);
204         return (0);
205 }
206
207 int
208 _sem_destroy(sem_t *sem)
209 {
210         if (sem_check_validity(sem) != 0) {
211                 errno = EINVAL;
212                 return (-1);
213         }
214
215         (*sem)->magic = 0;
216
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);
227         }
228         return (0);
229 }
230
231 int
232 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
233 {
234         if (sem_check_validity(sem) != 0)
235                 return (-1);
236
237         *sval = (*sem)->count;
238         return (0);
239 }
240
241 int
242 _sem_trywait(sem_t *sem)
243 {
244         int val;
245
246         if (sem_check_validity(sem) != 0)
247                 return (-1);
248
249         while ((val = (*sem)->count) > 0) {
250                 if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
251                         return (0);
252         }
253         errno = EAGAIN;
254         return (-1);
255 }
256
257 int
258 _sem_wait(sem_t *sem)
259 {
260         struct pthread *curthread;
261         int val, oldcancel, retval;
262
263         if (sem_check_validity(sem) != 0)
264                 return (-1);
265
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);
272                 }
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);
279 }
280
281 int
282 _sem_timedwait(sem_t * __restrict sem, const struct timespec * __restrict abstime)
283 {
284         struct timespec ts, ts2;
285         struct pthread *curthread;
286         int val, oldcancel, retval;
287
288         if (sem_check_validity(sem) != 0)
289                 return (-1);
290
291         curthread = tls_get_curthread();
292
293         /*
294          * The timeout argument is only supposed to
295          * be checked if the thread would have blocked.
296          */
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);
302                 }
303                 if (abstime == NULL ||
304                                 abstime->tv_nsec >= 1000000000 || abstime->tv_nsec < 0) {
305                         errno = EINVAL;
306                         return (-1);
307                 }
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);
317 }
318
319 int
320 _sem_post(sem_t *sem)
321 {
322         int val;
323
324         if (sem_check_validity(sem) != 0)
325                 return (-1);
326
327         /*
328          * sem_post() is required to be safe to call from within
329          * signal handlers, these code should work as that.
330          */
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);
336 }
337
338 static int
339 get_path(const char *name, char *path, size_t len, char const **prefix)
340 {
341         size_t path_len;
342
343         *prefix = NULL;
344         
345         if (name[0] == '/') {
346                 *prefix = getenv("LIBTHREAD_SEM_PREFIX");
347
348                 if (*prefix == NULL)
349                         *prefix = sem_prefix;
350
351                 path_len = strlcpy(path, *prefix, len);
352
353                 if (path_len > len) {
354                         return (ENAMETOOLONG);
355                 }
356         }
357
358         path_len = strlcat(path, name, len);
359
360         if (path_len > len)
361                 return (ENAMETOOLONG);
362
363         return (0);
364 }
365
366
367 static sem_t *
368 sem_get_mapping(ino_t inode, dev_t dev)
369 {
370         struct sem_info *ni;
371         struct stat sbuf;
372
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);
383                                 }
384                         }
385                         return (SEM_FAILED);
386
387                 }
388         }
389
390         return (SEM_FAILED);
391 }
392
393
394 static sem_t *
395 sem_add_mapping(ino_t inode, dev_t dev, sem_t sem, int fd)
396 {
397         struct sem_info *ni;
398
399         ni = malloc(sizeof(struct sem_info));
400         if (ni == NULL) {
401                 errno = ENOSPC;
402                 return (SEM_FAILED);
403         }
404
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;
411
412         LIST_INSERT_HEAD(&sem_list, ni, next);
413
414         return (&ni->sem);
415 }
416
417 static int
418 sem_close_mapping(sem_t *sem)
419 {
420         struct sem_info *ni;
421
422         if ((*sem)->semid != SEMID_NAMED)
423                 return (EINVAL);
424
425         ni = container_of(sem, struct sem_info, sem);
426
427         if ( --ni->open_count > 0) {
428                 return (0);
429         } else {
430                 if (ni->inode != 0) {
431                         LIST_REMOVE(ni, next);
432                 }       
433                 munmap(ni->sem, getpagesize());
434                 __sys_close(ni->fd);
435                 free(ni);
436                 return (0);
437         }
438 }
439
440 sem_t *
441 _sem_open(const char *name, int oflag, ...)
442 {
443         char path[PATH_MAX];
444         char tmppath[PATH_MAX];
445         char const *prefix = NULL;
446         char *tmpname = NULL;
447         size_t path_len;
448         int error, fd, create;
449         sem_t *sem;
450         sem_t semtmp;
451         va_list ap;
452         mode_t mode;
453         struct stat sbuf;
454         unsigned int value = 0;
455         unsigned int retry_count;
456         
457         create = 0;
458         error = 0;
459         fd = -1;
460         sem = SEM_FAILED;
461
462         /*
463          * Bail out if invalid flags specified.
464          */
465         if (oflag & ~(O_CREAT|O_EXCL)) {
466                 errno = EINVAL;
467                 return (SEM_FAILED);
468         }
469
470         oflag |= O_RDWR;
471         oflag |= O_CLOEXEC;
472
473         if (name == NULL) {
474                 errno = EINVAL;
475                 return (SEM_FAILED);
476         }
477
478         _pthread_once(&once, sem_module_init);
479
480         _pthread_mutex_lock(&sem_lock);
481
482         error = get_path(name, path, PATH_MAX, &prefix);
483         if (error) {
484                 errno = error;
485                 goto error;
486         }
487
488 retry:
489         tmpname = NULL;
490         retry_count = 10;
491
492         fd = __sys_open(path, O_RDWR | O_CLOEXEC);
493
494         if (fd > 0) {
495                 
496                 if ((oflag & O_EXCL) == O_EXCL) {
497                         __sys_close(fd);
498                         errno = EEXIST;
499                         goto error;
500                 }
501
502                 if (_fstat(fd, &sbuf) != 0) {
503                         /* Bad things happened, like another thread closing our descriptor */
504                         __sys_close(fd);
505                         errno = EINVAL;
506                         goto error;
507                 }
508
509                 sem = sem_get_mapping(sbuf.st_ino, sbuf.st_dev);
510
511                 if (sem != SEM_FAILED) {
512                         __sys_close(fd);
513                         goto done;
514                 }
515
516                 if ((sbuf.st_mode & S_IFREG) == 0) {
517                         /* We only want regular files here */
518                         __sys_close(fd);
519                         errno = EINVAL;
520                         goto error;
521                 }
522         } else if ((oflag & O_CREAT) && errno == ENOENT) {
523
524                 va_start(ap, oflag);
525
526                 mode = (mode_t) va_arg(ap, int);
527                 value = (unsigned int) va_arg(ap, int);
528
529                 va_end(ap);
530
531                 if (value > SEM_VALUE_MAX) {
532                         errno = EINVAL;
533                         goto error;
534                 }
535
536                 strlcpy(tmppath, prefix, sizeof(tmppath));
537                 path_len = strlcat(tmppath, "/sem.XXXXXX", sizeof(tmppath));
538
539                 if (path_len > sizeof(tmppath)) {
540                         errno = ENAMETOOLONG;
541                         goto error;     
542                 }
543
544                 while (retry_count-- > 0) {
545                         tmpname = mktemp(tmppath);
546
547                         if ( tmpname == NULL) {
548                                 errno = EINVAL;
549                                 goto error;
550                         }
551
552                         fd = __sys_open(tmpname, O_RDWR | O_CLOEXEC | O_CREAT | O_EXCL, mode);
553
554                         if (fd > 0 || errno != EEXIST) {
555                                 break;
556                         }
557
558                 }
559
560                 if (retry_count == 0) {
561                         __sys_close(fd);
562                         errno = ENOSPC; /* XXX POSIX does not allow for EAGAIN */
563                         goto error;
564                 }
565
566                 create = 1;
567         }
568
569         if (fd == -1) {
570                 switch (errno) {
571                         case ENOTDIR:
572                         case EISDIR:
573                         case EMLINK:
574                         case ELOOP:
575                                 errno = EINVAL;
576                                 break;
577                         case EDQUOT:
578                         case EIO:
579                                 errno = ENOSPC;
580                                 break;
581                         case EROFS:
582                                 errno = EACCES;
583                 }
584                 goto error;
585         }
586
587         semtmp = (sem_t) mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE,
588                         MAP_NOSYNC | MAP_SHARED, fd, 0);
589
590         if (semtmp == MAP_FAILED) {
591                 if (errno != EACCES && errno != EMFILE)
592                         errno = ENOMEM;
593
594                 if (create)
595                         unlink(tmpname);
596
597                 __sys_close(fd);        
598                 goto error;
599         }
600
601         if (create) {
602                 ftruncate(fd, sizeof(struct sem));
603                 semtmp->magic = SEM_MAGIC;
604                 semtmp->count = (u_int32_t)value;
605                 semtmp->semid = SEMID_NAMED;
606
607                 if (link(tmpname, path) != 0) {
608                         munmap(semtmp, getpagesize());
609                         __sys_close(fd);
610                         unlink(tmpname);
611
612                         if (errno == EEXIST && (oflag & O_EXCL) == 0) {
613                                 goto retry;
614                         }
615
616                         goto error;
617                 }
618                 unlink(tmpname);
619
620                 if (_fstat(fd, &sbuf) != 0) {
621                         /* Bad things happened, like another thread closing our descriptor */
622                         munmap(semtmp, getpagesize());
623                         __sys_close(fd);
624                         errno = EINVAL;
625                         goto error;
626                 }
627
628         }
629         sem = sem_add_mapping(sbuf.st_ino, sbuf.st_dev, semtmp, fd);
630
631 done:
632         _pthread_mutex_unlock(&sem_lock);
633         return (sem);
634
635 error:
636         _pthread_mutex_unlock(&sem_lock);
637         return (SEM_FAILED);
638
639 }
640
641 int
642 _sem_close(sem_t *sem)
643 {
644         _pthread_once(&once, sem_module_init);
645
646         _pthread_mutex_lock(&sem_lock);
647
648         if (sem_check_validity(sem)) {
649                 _pthread_mutex_unlock(&sem_lock);
650                 errno = EINVAL;
651                 return (-1);
652         }
653
654         if (sem_close_mapping(sem)) {
655                 _pthread_mutex_unlock(&sem_lock);
656                 errno = EINVAL;
657                 return (-1);
658         }
659         _pthread_mutex_unlock(&sem_lock);
660
661         return (0);
662 }
663
664 int
665 _sem_unlink(const char *name)
666 {
667         char path[PATH_MAX];
668         const char *prefix;
669         int error;
670
671         error = get_path(name, path, PATH_MAX, &prefix);
672         if (error) {
673                 errno = error;
674                 return (-1);
675         }
676
677         error = unlink(path);
678
679         if(error) {
680                 if (errno != ENAMETOOLONG && errno != ENOENT)
681                         errno = EACCES;
682
683                 return (-1);
684         }
685
686         return (0);
687 }
688
689 __strong_reference(_sem_destroy, sem_destroy);
690 __strong_reference(_sem_getvalue, sem_getvalue);
691 __strong_reference(_sem_init, sem_init);
692 __strong_reference(_sem_trywait, sem_trywait);
693 __strong_reference(_sem_wait, sem_wait);
694 __strong_reference(_sem_timedwait, sem_timedwait);
695 __strong_reference(_sem_post, sem_post);
696 __strong_reference(_sem_open, sem_open);
697 __strong_reference(_sem_close, sem_close);
698 __strong_reference(_sem_unlink, sem_unlink);
699