diff options
| author | Rich Felker <dalias@aerifal.cx> | 2011-03-04 00:45:59 -0500 | 
|---|---|---|
| committer | Rich Felker <dalias@aerifal.cx> | 2011-03-04 00:45:59 -0500 | 
| commit | 6fc5fdbdc70dd17ea8e681a361fb4dae541ee953 (patch) | |
| tree | eee28ceddc2de85e528e50340e045a875e6a8e60 | |
| parent | 03dcc3417ce4388a652bdd053cb2b6af860daf00 (diff) | |
| download | musl-6fc5fdbdc70dd17ea8e681a361fb4dae541ee953.tar.gz | |
implement POSIX semaphores
| -rw-r--r-- | src/thread/sem_close.c | 7 | ||||
| -rw-r--r-- | src/thread/sem_destroy.c | 6 | ||||
| -rw-r--r-- | src/thread/sem_getvalue.c | 8 | ||||
| -rw-r--r-- | src/thread/sem_init.c | 13 | ||||
| -rw-r--r-- | src/thread/sem_open.c | 116 | ||||
| -rw-r--r-- | src/thread/sem_post.c | 9 | ||||
| -rw-r--r-- | src/thread/sem_timedwait.c | 18 | ||||
| -rw-r--r-- | src/thread/sem_trywait.c | 11 | ||||
| -rw-r--r-- | src/thread/sem_unlink.c | 7 | ||||
| -rw-r--r-- | src/thread/sem_wait.c | 6 | 
10 files changed, 201 insertions, 0 deletions
| 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 <semaphore.h> +#include <sys/mman.h> + +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 <semaphore.h> + +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 <semaphore.h> + +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 <semaphore.h> +#include <limits.h> +#include <errno.h> + +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 <semaphore.h> +#include <sys/mman.h> +#include <limits.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> +#include <sys/stat.h> + +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 <semaphore.h> +#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 <semaphore.h> +#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 <semaphore.h> +#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 <semaphore.h> +#include <sys/mman.h> + +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 <semaphore.h> + +int sem_wait(sem_t *sem) +{ +	return sem_timedwait(sem, 0); +} | 
