OLD | NEW |
(Empty) | |
| 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[2]; |
| 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_SHAR
ED, 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 } |
OLD | NEW |