123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- #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>
- #include <stdlib.h>
- #include <pthread.h>
- 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()
- {
- semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX);
- }
- 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, ...)
- {
- va_list ap;
- mode_t mode;
- unsigned value;
- int fd, tfd, dir;
- sem_t newsem;
- void *map;
- char tmp[64];
- struct timespec ts;
- struct stat st;
- int i;
- while (*name=='/') name++;
- if (strchr(name, '/')) {
- errno = EINVAL;
- 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;
- 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;
- pthread_spin_lock(&lock);
- 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 && 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;
- }
- }
- if (!(flags & O_CREAT)) {
- pthread_spin_unlock(&lock);
- return SEM_FAILED;
- }
- 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;
- }
- }
- 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) {
- 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);
- }
|