about summary refs log tree commit diff
path: root/src/thread/sem_open.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/thread/sem_open.c')
-rw-r--r--src/thread/sem_open.c106
1 files changed, 80 insertions, 26 deletions
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);
+}