diff options
Diffstat (limited to 'src/thread/sem_open.c')
| -rw-r--r-- | src/thread/sem_open.c | 106 | 
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); +}  | 
