From 6fc5fdbdc70dd17ea8e681a361fb4dae541ee953 Mon Sep 17 00:00:00 2001 From: Rich Felker Date: Fri, 4 Mar 2011 00:45:59 -0500 Subject: implement POSIX semaphores --- src/thread/sem_close.c | 7 +++ src/thread/sem_destroy.c | 6 +++ src/thread/sem_getvalue.c | 8 ++++ src/thread/sem_init.c | 13 +++++ src/thread/sem_open.c | 116 +++++++++++++++++++++++++++++++++++++++++++++ src/thread/sem_post.c | 9 ++++ src/thread/sem_timedwait.c | 18 +++++++ src/thread/sem_trywait.c | 11 +++++ src/thread/sem_unlink.c | 7 +++ src/thread/sem_wait.c | 6 +++ 10 files changed, 201 insertions(+) create mode 100644 src/thread/sem_close.c create mode 100644 src/thread/sem_destroy.c create mode 100644 src/thread/sem_getvalue.c create mode 100644 src/thread/sem_init.c create mode 100644 src/thread/sem_open.c create mode 100644 src/thread/sem_post.c create mode 100644 src/thread/sem_timedwait.c create mode 100644 src/thread/sem_trywait.c create mode 100644 src/thread/sem_unlink.c create mode 100644 src/thread/sem_wait.c 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 +#include + +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 + +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 + +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 +#include +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#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 +#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 +#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 +#include + +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 + +int sem_wait(sem_t *sem) +{ + return sem_timedwait(sem, 0); +} -- cgit v1.2.1