#include #include #include #include #include #include #include #include #include #include #include #include #include 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_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