123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- #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>
- #include "libc.h"
- char *__shm_mapname(const char *, char *);
- static struct {
- ino_t ino;
- sem_t *sem;
- int refcnt;
- } *semtab;
- static int lock[2];
- #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
- sem_t *sem_open(const char *name, int flags, ...)
- {
- va_list ap;
- mode_t mode;
- unsigned value;
- int fd, i, e, slot, first=1, cnt, cs;
- sem_t newsem;
- void *map;
- char tmp[64];
- struct timespec ts;
- struct stat st;
- char buf[NAME_MAX+10];
- if (!(name = __shm_mapname(name, buf)))
- return SEM_FAILED;
- LOCK(lock);
- /* Allocate table if we don't have one yet */
- if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
- UNLOCK(lock);
- return SEM_FAILED;
- }
- /* Reserve a slot in case this semaphore is not mapped yet;
- * this is necessary because there is no way to handle
- * failures after creation of the file. */
- slot = -1;
- for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
- cnt += semtab[i].refcnt;
- if (!semtab[i].sem && slot < 0) slot = i;
- }
- /* Avoid possibility of overflow later */
- if (cnt == INT_MAX || slot < 0) {
- errno = EMFILE;
- UNLOCK(lock);
- return SEM_FAILED;
- }
- /* Dummy pointer to make a reservation */
- semtab[slot].sem = (sem_t *)-1;
- UNLOCK(lock);
- flags &= (O_CREAT|O_EXCL);
- /* Early failure check for exclusive open; otherwise the case
- * where the semaphore already exists is expensive. */
- if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
- errno = EEXIST;
- return SEM_FAILED;
- }
- pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
- for (;;) {
- /* If exclusive mode is not requested, try opening an
- * existing file first and fall back to creation. */
- if (flags != (O_CREAT|O_EXCL)) {
- fd = open(name, FLAGS);
- if (fd >= 0) {
- if (fstat(fd, &st) < 0 ||
- (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
- close(fd);
- goto fail;
- }
- close(fd);
- break;
- }
- if (errno != ENOENT)
- goto fail;
- }
- if (!(flags & O_CREAT))
- goto fail;
- if (first) {
- first = 0;
- 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;
- goto fail;
- }
- sem_init(&newsem, 1, value);
- }
- /* Create a temp file with the new semaphore contents
- * and attempt to atomically link it as the new name */
- clock_gettime(CLOCK_REALTIME, &ts);
- snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
- fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
- if (fd < 0) {
- if (errno == EEXIST) continue;
- goto fail;
- }
- if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
- (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
- close(fd);
- unlink(tmp);
- goto fail;
- }
- close(fd);
- if (link(tmp, name) == 0) break;
- e = errno;
- unlink(tmp);
- /* Failure is only fatal when doing an exclusive open;
- * otherwise, next iteration will try to open the
- * existing file. */
- if (e != EEXIST || flags == (O_CREAT|O_EXCL))
- goto fail;
- }
- /* See if the newly mapped semaphore is already mapped. If
- * so, unmap the new mapping and use the existing one. Otherwise,
- * add it to the table of mapped semaphores. */
- LOCK(lock);
- for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
- if (i<SEM_NSEMS_MAX) {
- munmap(map, sizeof(sem_t));
- semtab[slot].sem = 0;
- slot = i;
- map = semtab[i].sem;
- }
- semtab[slot].refcnt++;
- semtab[slot].sem = map;
- semtab[slot].ino = st.st_ino;
- UNLOCK(lock);
- pthread_setcancelstate(cs, 0);
- return map;
- fail:
- pthread_setcancelstate(cs, 0);
- return SEM_FAILED;
- }
- int sem_close(sem_t *sem)
- {
- int i;
- 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;
- }
- UNLOCK(lock);
- munmap(sem, sizeof *sem);
- return 0;
- }
|