OSDN Git Service

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