summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-03-04 00:45:59 -0500
committerRich Felker <dalias@aerifal.cx>2011-03-04 00:45:59 -0500
commit6fc5fdbdc70dd17ea8e681a361fb4dae541ee953 (patch)
treeeee28ceddc2de85e528e50340e045a875e6a8e60 /src
parent03dcc3417ce4388a652bdd053cb2b6af860daf00 (diff)
downloadmusl-6fc5fdbdc70dd17ea8e681a361fb4dae541ee953.tar.gz
implement POSIX semaphores
Diffstat (limited to 'src')
-rw-r--r--src/thread/sem_close.c7
-rw-r--r--src/thread/sem_destroy.c6
-rw-r--r--src/thread/sem_getvalue.c8
-rw-r--r--src/thread/sem_init.c13
-rw-r--r--src/thread/sem_open.c116
-rw-r--r--src/thread/sem_post.c9
-rw-r--r--src/thread/sem_timedwait.c18
-rw-r--r--src/thread/sem_trywait.c11
-rw-r--r--src/thread/sem_unlink.c7
-rw-r--r--src/thread/sem_wait.c6
10 files changed, 201 insertions, 0 deletions
diff --git a/src/thread/sem_close.c b/src/thread/sem_close.c
new file mode 100644
index 00000000..036ee540
--- /dev/null
+++ b/src/thread/sem_close.c
@@ -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
index 00000000..f4aced5d
--- /dev/null
+++ b/src/thread/sem_destroy.c
@@ -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
index 00000000..643c0968
--- /dev/null
+++ b/src/thread/sem_getvalue.c
@@ -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
index 00000000..70f06e9f
--- /dev/null
+++ b/src/thread/sem_init.c
@@ -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
index 00000000..55d5ef6b
--- /dev/null
+++ b/src/thread/sem_open.c
@@ -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
index 00000000..0bd8a462
--- /dev/null
+++ b/src/thread/sem_post.c
@@ -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
index 00000000..e6604364
--- /dev/null
+++ b/src/thread/sem_timedwait.c
@@ -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
index 00000000..d0e928ef
--- /dev/null
+++ b/src/thread/sem_trywait.c
@@ -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
index 00000000..c06134bd
--- /dev/null
+++ b/src/thread/sem_unlink.c
@@ -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
index 00000000..264194f9
--- /dev/null
+++ b/src/thread/sem_wait.c
@@ -0,0 +1,6 @@
+#include <semaphore.h>
+
+int sem_wait(sem_t *sem)
+{
+ return sem_timedwait(sem, 0);
+}