implement POSIX semaphores
authorRich Felker <dalias@aerifal.cx>
Fri, 4 Mar 2011 05:45:59 +0000 (00:45 -0500)
committerRich Felker <dalias@aerifal.cx>
Fri, 4 Mar 2011 05:45:59 +0000 (00:45 -0500)
src/thread/sem_close.c [new file with mode: 0644]
src/thread/sem_destroy.c [new file with mode: 0644]
src/thread/sem_getvalue.c [new file with mode: 0644]
src/thread/sem_init.c [new file with mode: 0644]
src/thread/sem_open.c [new file with mode: 0644]
src/thread/sem_post.c [new file with mode: 0644]
src/thread/sem_timedwait.c [new file with mode: 0644]
src/thread/sem_trywait.c [new file with mode: 0644]
src/thread/sem_unlink.c [new file with mode: 0644]
src/thread/sem_wait.c [new file with mode: 0644]

diff --git a/src/thread/sem_close.c b/src/thread/sem_close.c
new file mode 100644 (file)
index 0000000..036ee54
--- /dev/null
@@ -0,0 +1,7 @@
+#include <semaphore.h>
+#include <sys/mman.h>
+
+int sem_close(sem_t *sem)
+{
+       return munmap(sem, sizeof *sem);
+}
diff --git a/src/thread/sem_destroy.c b/src/thread/sem_destroy.c
new file mode 100644 (file)
index 0000000..f4aced5
--- /dev/null
@@ -0,0 +1,6 @@
+#include <semaphore.h>
+
+int sem_destroy(sem_t *sem)
+{
+       return 0;
+}
diff --git a/src/thread/sem_getvalue.c b/src/thread/sem_getvalue.c
new file mode 100644 (file)
index 0000000..643c096
--- /dev/null
@@ -0,0 +1,8 @@
+#include <semaphore.h>
+
+int sem_getvalue(sem_t *sem, int *valp)
+{
+       int val = sem->__val[0];
+       *valp = val < 0 ? 0 : val;
+       return 0;
+}
diff --git a/src/thread/sem_init.c b/src/thread/sem_init.c
new file mode 100644 (file)
index 0000000..70f06e9
--- /dev/null
@@ -0,0 +1,13 @@
+#include <semaphore.h>
+#include <limits.h>
+#include <errno.h>
+
+int sem_init(sem_t *sem, int pshared, unsigned value)
+{
+       if (value > SEM_VALUE_MAX) {
+               errno = EINVAL;
+               return -1;
+       }
+       sem->__val[0] = value;
+       return 0;
+}
diff --git a/src/thread/sem_open.c b/src/thread/sem_open.c
new file mode 100644 (file)
index 0000000..55d5ef6
--- /dev/null
@@ -0,0 +1,116 @@
+#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>
+
+static void *find_map(int fd)
+{
+       char c;
+       struct stat st;
+       FILE *f;
+       void *addr;
+       unsigned long long off, ino;
+       char buf[100];
+
+       if (fstat(fd, &st) < 0) return 0;
+       if (!(f = fopen("/proc/self/maps", "rb"))) return 0;
+       
+       while (fgets(buf, sizeof buf, f)) {
+               sscanf(buf, "%lx-%*lx %*s %llx %*x:%*x %llu /dev/shm%c",
+                       (long *)&addr, &off, &ino, &c);
+               while (!strchr(buf, '\n') && fgets(buf, sizeof buf, f));
+               if (c!='/') continue;
+               c = 0;
+               if (!off && st.st_ino == ino) {
+                       fclose(f);
+                       return addr;
+               }
+       }
+       fclose(f);
+       return 0;
+}
+
+sem_t *sem_open(const char *name, int flags, ...)
+{
+       va_list ap;
+       mode_t mode;
+       unsigned value;
+       int fd, tfd, dir;
+       sem_t newsem;
+       void *map;
+       char tmp[64];
+       struct timespec ts;
+
+       while (*name=='/') name++;
+       if (strchr(name, '/')) {
+               errno = EINVAL;
+               return SEM_FAILED;
+       }
+
+       if (flags & O_CREAT) {
+               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;
+                       return SEM_FAILED;
+               }
+               sem_init(&newsem, 0, value);
+               clock_gettime(CLOCK_REALTIME, &ts);
+               snprintf(tmp, sizeof(tmp), "/dev/shm/%p-%p-%d-%d",
+                       &name, name, (int)getpid(), (int)ts.tv_nsec);
+               tfd = open(tmp, O_CREAT|O_EXCL|O_RDWR, mode);
+               if (tfd<0) return SEM_FAILED;
+               dir = open("/dev/shm", O_DIRECTORY|O_RDONLY);
+               if (dir<0 || write(tfd,&newsem,sizeof newsem)!=sizeof newsem) {
+                       if (dir >= 0) close(dir);
+                       close(tfd);
+                       unlink(tmp);
+                       return SEM_FAILED;
+               }
+       }
+
+       flags &= ~O_ACCMODE;
+       flags |= O_RDWR;
+
+       for (;;) {
+               if (!(flags & O_EXCL)) {
+                       fd = shm_open(name, flags&~O_CREAT, mode);
+                       if (fd >= 0 || errno != ENOENT) {
+                               if (flags & O_CREAT) {
+                                       close(dir);
+                                       close(tfd);
+                                       unlink(tmp);
+                               }
+                               if (fd < 0) return SEM_FAILED;
+                               if ((map = find_map(fd)))
+                                       return map;
+                               break;
+                       }
+               }
+               if (!linkat(AT_FDCWD, tmp, dir, name, 0)) {
+                       fd = tfd;
+                       close(dir);
+                       unlink(tmp);
+                       break;
+               }
+               if ((flags & O_EXCL) || errno != EEXIST) {
+                       close(dir);
+                       close(tfd);
+                       unlink(tmp);
+                       return SEM_FAILED;
+               }
+       }
+       map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+       close(fd);
+       if (map == MAP_FAILED) return SEM_FAILED;
+       return map;
+}
diff --git a/src/thread/sem_post.c b/src/thread/sem_post.c
new file mode 100644 (file)
index 0000000..0bd8a46
--- /dev/null
@@ -0,0 +1,9 @@
+#include <semaphore.h>
+#include "pthread_impl.h"
+
+int sem_post(sem_t *sem)
+{
+       if (!a_fetch_add(sem->__val, 1))
+               __wake(sem->__val, 1, 0);
+       return 0;
+}
diff --git a/src/thread/sem_timedwait.c b/src/thread/sem_timedwait.c
new file mode 100644 (file)
index 0000000..e660436
--- /dev/null
@@ -0,0 +1,18 @@
+#include <semaphore.h>
+#include "pthread_impl.h"
+
+int sem_timedwait(sem_t *sem, const struct timespec *at)
+{
+       int val;
+
+       for (;;) {
+               if (a_fetch_add(sem->__val, -1) > 0) return 0;
+               val = a_fetch_add(sem->__val, 1);
+               CANCELPT_BEGIN;
+               if (val <= 0 && __timedwait(sem->__val, val, CLOCK_REALTIME, at, 0) == ETIMEDOUT) {
+                       errno = ETIMEDOUT;
+                       return -1;
+               }
+               CANCELPT_END;
+       }
+}
diff --git a/src/thread/sem_trywait.c b/src/thread/sem_trywait.c
new file mode 100644 (file)
index 0000000..d0e928e
--- /dev/null
@@ -0,0 +1,11 @@
+#include <semaphore.h>
+#include "pthread_impl.h"
+
+int sem_trywait(sem_t *sem)
+{
+       int val = a_fetch_add(sem->__val, -1);
+       if (val > 0) return 0;
+       a_inc(sem->__val);
+       errno = EAGAIN;
+       return -1;
+}
diff --git a/src/thread/sem_unlink.c b/src/thread/sem_unlink.c
new file mode 100644 (file)
index 0000000..c06134b
--- /dev/null
@@ -0,0 +1,7 @@
+#include <semaphore.h>
+#include <sys/mman.h>
+
+int sem_unlink(const char *name)
+{
+       return shm_unlink(name);
+}
diff --git a/src/thread/sem_wait.c b/src/thread/sem_wait.c
new file mode 100644 (file)
index 0000000..264194f
--- /dev/null
@@ -0,0 +1,6 @@
+#include <semaphore.h>
+
+int sem_wait(sem_t *sem)
+{
+       return sem_timedwait(sem, 0);
+}