convert malloc use under libc-internal locks to use internal allocator
[musl] / src / thread / sem_open.c
1 #include <semaphore.h>
2 #include <sys/mman.h>
3 #include <limits.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <stdarg.h>
8 #include <errno.h>
9 #include <time.h>
10 #include <stdio.h>
11 #include <sys/stat.h>
12 #include <stdlib.h>
13 #include <pthread.h>
14 #include "lock.h"
15
16 #define malloc __libc_malloc
17 #define calloc __libc_calloc
18 #define realloc undef
19 #define free undef
20
21 static struct {
22         ino_t ino;
23         sem_t *sem;
24         int refcnt;
25 } *semtab;
26 static volatile int lock[1];
27
28 #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
29
30 sem_t *sem_open(const char *name, int flags, ...)
31 {
32         va_list ap;
33         mode_t mode;
34         unsigned value;
35         int fd, i, e, slot, first=1, cnt, cs;
36         sem_t newsem;
37         void *map;
38         char tmp[64];
39         struct timespec ts;
40         struct stat st;
41         char buf[NAME_MAX+10];
42
43         if (!(name = __shm_mapname(name, buf)))
44                 return SEM_FAILED;
45
46         LOCK(lock);
47         /* Allocate table if we don't have one yet */
48         if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
49                 UNLOCK(lock);
50                 return SEM_FAILED;
51         }
52
53         /* Reserve a slot in case this semaphore is not mapped yet;
54          * this is necessary because there is no way to handle
55          * failures after creation of the file. */
56         slot = -1;
57         for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
58                 cnt += semtab[i].refcnt;
59                 if (!semtab[i].sem && slot < 0) slot = i;
60         }
61         /* Avoid possibility of overflow later */
62         if (cnt == INT_MAX || slot < 0) {
63                 errno = EMFILE;
64                 UNLOCK(lock);
65                 return SEM_FAILED;
66         }
67         /* Dummy pointer to make a reservation */
68         semtab[slot].sem = (sem_t *)-1;
69         UNLOCK(lock);
70
71         flags &= (O_CREAT|O_EXCL);
72
73         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
74
75         /* Early failure check for exclusive open; otherwise the case
76          * where the semaphore already exists is expensive. */
77         if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
78                 errno = EEXIST;
79                 goto fail;
80         }
81
82         for (;;) {
83                 /* If exclusive mode is not requested, try opening an
84                  * existing file first and fall back to creation. */
85                 if (flags != (O_CREAT|O_EXCL)) {
86                         fd = open(name, FLAGS);
87                         if (fd >= 0) {
88                                 if (fstat(fd, &st) < 0 ||
89                                     (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
90                                         close(fd);
91                                         goto fail;
92                                 }
93                                 close(fd);
94                                 break;
95                         }
96                         if (errno != ENOENT)
97                                 goto fail;
98                 }
99                 if (!(flags & O_CREAT))
100                         goto fail;
101                 if (first) {
102                         first = 0;
103                         va_start(ap, flags);
104                         mode = va_arg(ap, mode_t) & 0666;
105                         value = va_arg(ap, unsigned);
106                         va_end(ap);
107                         if (value > SEM_VALUE_MAX) {
108                                 errno = EINVAL;
109                                 goto fail;
110                         }
111                         sem_init(&newsem, 1, value);
112                 }
113                 /* Create a temp file with the new semaphore contents
114                  * and attempt to atomically link it as the new name */
115                 clock_gettime(CLOCK_REALTIME, &ts);
116                 snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
117                 fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
118                 if (fd < 0) {
119                         if (errno == EEXIST) continue;
120                         goto fail;
121                 }
122                 if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
123                     (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
124                         close(fd);
125                         unlink(tmp);
126                         goto fail;
127                 }
128                 close(fd);
129                 e = link(tmp, name) ? errno : 0;
130                 unlink(tmp);
131                 if (!e) break;
132                 munmap(map, sizeof(sem_t));
133                 /* Failure is only fatal when doing an exclusive open;
134                  * otherwise, next iteration will try to open the
135                  * existing file. */
136                 if (e != EEXIST || flags == (O_CREAT|O_EXCL))
137                         goto fail;
138         }
139
140         /* See if the newly mapped semaphore is already mapped. If
141          * so, unmap the new mapping and use the existing one. Otherwise,
142          * add it to the table of mapped semaphores. */
143         LOCK(lock);
144         for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
145         if (i<SEM_NSEMS_MAX) {
146                 munmap(map, sizeof(sem_t));
147                 semtab[slot].sem = 0;
148                 slot = i;
149                 map = semtab[i].sem;
150         }
151         semtab[slot].refcnt++;
152         semtab[slot].sem = map;
153         semtab[slot].ino = st.st_ino;
154         UNLOCK(lock);
155         pthread_setcancelstate(cs, 0);
156         return map;
157
158 fail:
159         pthread_setcancelstate(cs, 0);
160         LOCK(lock);
161         semtab[slot].sem = 0;
162         UNLOCK(lock);
163         return SEM_FAILED;
164 }
165
166 int sem_close(sem_t *sem)
167 {
168         int i;
169         LOCK(lock);
170         for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
171         if (--semtab[i].refcnt) {
172                 UNLOCK(lock);
173                 return 0;
174         }
175         semtab[i].sem = 0;
176         semtab[i].ino = 0;
177         UNLOCK(lock);
178         munmap(sem, sizeof *sem);
179         return 0;
180 }