summaryrefslogtreecommitdiff
path: root/src/thread
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-03-10 21:34:19 -0500
committerRich Felker <dalias@aerifal.cx>2011-03-10 21:34:19 -0500
commit81af503610761a69476a3adbe8341fa8b6d078aa (patch)
tree87d40059752e8c9db136c3b53f25bc4f1a3a103d /src/thread
parent5fcebcde6aeba6ae4a339790beba5331fbcd3b6e (diff)
downloadmusl-81af503610761a69476a3adbe8341fa8b6d078aa.tar.gz
fix sem_open and sem_close to obey posix semantics
multiple opens of the same named semaphore must return the same pointer, and only the last close can unmap it. thus the ugly global state keeping track of mappings. the maximum number of distinct named semaphores that can be opened is limited sufficiently small that the linear searches take trivial time, especially compared to the syscall overhead of these functions.
Diffstat (limited to 'src/thread')
-rw-r--r--src/thread/sem_close.c7
-rw-r--r--src/thread/sem_open.c106
2 files changed, 80 insertions, 33 deletions
diff --git a/src/thread/sem_close.c b/src/thread/sem_close.c
deleted file mode 100644
index 036ee540..00000000
--- a/src/thread/sem_close.c
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <semaphore.h>
-#include <sys/mman.h>
-
-int sem_close(sem_t *sem)
-{
- return munmap(sem, sizeof *sem);
-}
diff --git a/src/thread/sem_open.c b/src/thread/sem_open.c
index 55d5ef6b..6fff71a8 100644
--- a/src/thread/sem_open.c
+++ b/src/thread/sem_open.c
@@ -9,32 +9,32 @@
#include <time.h>
#include <stdio.h>
#include <sys/stat.h>
+#include <stdlib.h>
+#include <pthread.h>
-static void *find_map(int fd)
+static struct {
+ ino_t ino;
+ sem_t *sem;
+ int refcnt;
+} *semtab;
+
+static int semcnt;
+static pthread_spinlock_t lock;
+static pthread_once_t once;
+
+static void init()
{
- char c;
- struct stat st;
- FILE *f;
- void *addr;
- unsigned long long off, ino;
- char buf[100];
+ semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX);
+}
- 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;
+static sem_t *find_map(ino_t ino)
+{
+ int i;
+ for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != ino; i++);
+ if (i==SEM_NSEMS_MAX) return 0;
+ if (semtab[i].refcnt == INT_MAX) return (sem_t *)-1;
+ semtab[i].refcnt++;
+ return semtab[i].sem;
}
sem_t *sem_open(const char *name, int flags, ...)
@@ -47,6 +47,8 @@ sem_t *sem_open(const char *name, int flags, ...)
void *map;
char tmp[64];
struct timespec ts;
+ struct stat st;
+ int i;
while (*name=='/') name++;
if (strchr(name, '/')) {
@@ -54,6 +56,12 @@ sem_t *sem_open(const char *name, int flags, ...)
return SEM_FAILED;
}
+ pthread_once(&once, init);
+ if (!semtab) {
+ errno = ENOMEM;
+ return SEM_FAILED;
+ }
+
if (flags & O_CREAT) {
va_start(ap, flags);
mode = va_arg(ap, mode_t) & 0666;
@@ -81,6 +89,8 @@ sem_t *sem_open(const char *name, int flags, ...)
flags &= ~O_ACCMODE;
flags |= O_RDWR;
+ pthread_spin_lock(&lock);
+
for (;;) {
if (!(flags & O_EXCL)) {
fd = shm_open(name, flags&~O_CREAT, mode);
@@ -90,9 +100,21 @@ sem_t *sem_open(const char *name, int flags, ...)
close(tfd);
unlink(tmp);
}
- if (fd < 0) return SEM_FAILED;
- if ((map = find_map(fd)))
+ if (fstat(fd, &st) < 0) {
+ close(fd);
+ fd = -1;
+ }
+ if (fd < 0) {
+ pthread_spin_unlock(&lock);
+ return SEM_FAILED;
+ }
+ if ((map = find_map(st.st_ino))) {
+ pthread_spin_unlock(&lock);
+ close(fd);
+ if (map == (sem_t *)-1)
+ return SEM_FAILED;
return map;
+ }
break;
}
}
@@ -109,8 +131,40 @@ sem_t *sem_open(const char *name, int flags, ...)
return SEM_FAILED;
}
}
+ if (fstat(fd, &st) < 0) {
+ pthread_spin_unlock(&lock);
+ close(fd);
+ return SEM_FAILED;
+ }
+ if (semcnt == SEM_NSEMS_MAX) {
+ pthread_spin_unlock(&lock);
+ close(fd);
+ errno = EMFILE;
+ return SEM_FAILED;
+ }
+ for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem; i++);
map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
- if (map == MAP_FAILED) return SEM_FAILED;
+ if (map == MAP_FAILED) {
+ pthread_spin_unlock(&lock);
+ return SEM_FAILED;
+ }
+ semtab[i].ino = st.st_ino;
+ semtab[i].sem = map;
+ semtab[i].refcnt = 1;
+ pthread_spin_unlock(&lock);
return map;
}
+
+int sem_close(sem_t *sem)
+{
+ int i;
+ pthread_spin_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;
+ }
+ pthread_spin_unlock(&lock);
+ return munmap(sem, sizeof *sem);
+}