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