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