Index: fusl/src/thread/sem_open.c |
diff --git a/fusl/src/thread/sem_open.c b/fusl/src/thread/sem_open.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fda0acd35c717992e44b4c5191e6f400d1190532 |
--- /dev/null |
+++ b/fusl/src/thread/sem_open.c |
@@ -0,0 +1,175 @@ |
+#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 volatile 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); |
+ |
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); |
+ |
+ /* 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; |
+ goto fail; |
+ } |
+ |
+ 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 (fstat(fd, &st) < 0 || |
+ (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { |
+ 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); |
+ e = link(tmp, name) ? errno : 0; |
+ unlink(tmp); |
+ if (!e) break; |
+ munmap(map, sizeof(sem_t)); |
+ /* 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); |
+ LOCK(lock); |
+ semtab[slot].sem = 0; |
+ UNLOCK(lock); |
+ 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; |
+} |