about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/limits.h1
-rw-r--r--src/conf/sysconf.c10
-rw-r--r--src/thread/sem_close.c7
-rw-r--r--src/thread/sem_open.c106
4 files changed, 86 insertions, 38 deletions
diff --git a/include/limits.h b/include/limits.h
index e12819ba..55ad8231 100644
--- a/include/limits.h
+++ b/include/limits.h
@@ -30,6 +30,7 @@
 #define PTHREAD_STACK_MIN PAGE_SIZE
 #define PTHREAD_DESTRUCTOR_ITERATIONS 4
 #define SEM_VALUE_MAX 0x7fffffff
+#define SEM_NSEMS_MAX 256
 
 /* Arbitrary numbers... */
 
diff --git a/src/conf/sysconf.c b/src/conf/sysconf.c
index cdaeb2a6..a660257d 100644
--- a/src/conf/sysconf.c
+++ b/src/conf/sysconf.c
@@ -40,8 +40,8 @@ long sysconf(int name)
 		[_SC_VERSION] = VER,
 		[_SC_PAGE_SIZE] = PAGE_SIZE,
 		[_SC_RTSIG_MAX] = 63, /* ?? */
-		[_SC_SEM_NSEMS_MAX] = _POSIX_SEM_NSEMS_MAX,
-		[_SC_SEM_VALUE_MAX] = _POSIX_SEM_VALUE_MAX,
+		[_SC_SEM_NSEMS_MAX] = SEM_NSEMS_MAX,
+		[_SC_SEM_VALUE_MAX] = OFLOW,
 		[_SC_SIGQUEUE_MAX] = -1,
 		[_SC_TIMER_MAX] = -1,
 		[_SC_BC_BASE_MAX] = _POSIX2_BC_BASE_MAX,
@@ -215,8 +215,8 @@ long sysconf(int name)
 	} else if (values[name] == VER) {
 		return _POSIX_VERSION;
 	} else if (values[name] == OFLOW) {
-		return ARG_MAX;
-	} else {
-		return values[name];
+		if (name == _SC_ARG_MAX) return ARG_MAX;
+		if (name == _SC_SEM_VALUE_MAX) return SEM_VALUE_MAX;
 	}
+	return values[name];
 }
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);
+}