github.com/afumu/libc@v0.0.6/musl/src/thread/sem_open.c (about)

     1  #include <semaphore.h>
     2  #include <sys/mman.h>
     3  #include <limits.h>
     4  #include <fcntl.h>
     5  #include <unistd.h>
     6  #include <string.h>
     7  #include <stdarg.h>
     8  #include <errno.h>
     9  #include <time.h>
    10  #include <stdio.h>
    11  #include <sys/stat.h>
    12  #include <stdlib.h>
    13  #include <pthread.h>
    14  #include "lock.h"
    15  
    16  static struct {
    17  	ino_t ino;
    18  	sem_t *sem;
    19  	int refcnt;
    20  } *semtab;
    21  static volatile int lock[1];
    22  
    23  #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK)
    24  
    25  sem_t *sem_open(const char *name, int flags, ...)
    26  {
    27  	va_list ap;
    28  	mode_t mode;
    29  	unsigned value;
    30  	int fd, i, e, slot, first=1, cnt, cs;
    31  	sem_t newsem;
    32  	void *map;
    33  	char tmp[64];
    34  	struct timespec ts;
    35  	struct stat st;
    36  	char buf[NAME_MAX+10];
    37  
    38  	if (!(name = __shm_mapname(name, buf)))
    39  		return SEM_FAILED;
    40  
    41  	LOCK(lock);
    42  	/* Allocate table if we don't have one yet */
    43  	if (!semtab && !(semtab = calloc(sizeof *semtab, SEM_NSEMS_MAX))) {
    44  		UNLOCK(lock);
    45  		return SEM_FAILED;
    46  	}
    47  
    48  	/* Reserve a slot in case this semaphore is not mapped yet;
    49  	 * this is necessary because there is no way to handle
    50  	 * failures after creation of the file. */
    51  	slot = -1;
    52  	for (cnt=i=0; i<SEM_NSEMS_MAX; i++) {
    53  		cnt += semtab[i].refcnt;
    54  		if (!semtab[i].sem && slot < 0) slot = i;
    55  	}
    56  	/* Avoid possibility of overflow later */
    57  	if (cnt == INT_MAX || slot < 0) {
    58  		errno = EMFILE;
    59  		UNLOCK(lock);
    60  		return SEM_FAILED;
    61  	}
    62  	/* Dummy pointer to make a reservation */
    63  	semtab[slot].sem = (sem_t *)-1;
    64  	UNLOCK(lock);
    65  
    66  	flags &= (O_CREAT|O_EXCL);
    67  
    68  	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
    69  
    70  	/* Early failure check for exclusive open; otherwise the case
    71  	 * where the semaphore already exists is expensive. */
    72  	if (flags == (O_CREAT|O_EXCL) && access(name, F_OK) == 0) {
    73  		errno = EEXIST;
    74  		goto fail;
    75  	}
    76  
    77  	for (;;) {
    78  		/* If exclusive mode is not requested, try opening an
    79  		 * existing file first and fall back to creation. */
    80  		if (flags != (O_CREAT|O_EXCL)) {
    81  			fd = open(name, FLAGS);
    82  			if (fd >= 0) {
    83  				if (fstat(fd, &st) < 0 ||
    84  				    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
    85  					close(fd);
    86  					goto fail;
    87  				}
    88  				close(fd);
    89  				break;
    90  			}
    91  			if (errno != ENOENT)
    92  				goto fail;
    93  		}
    94  		if (!(flags & O_CREAT))
    95  			goto fail;
    96  		if (first) {
    97  			first = 0;
    98  			va_start(ap, flags);
    99  			mode = va_arg(ap, mode_t) & 0666;
   100  			value = va_arg(ap, unsigned);
   101  			va_end(ap);
   102  			if (value > SEM_VALUE_MAX) {
   103  				errno = EINVAL;
   104  				goto fail;
   105  			}
   106  			sem_init(&newsem, 1, value);
   107  		}
   108  		/* Create a temp file with the new semaphore contents
   109  		 * and attempt to atomically link it as the new name */
   110  		clock_gettime(CLOCK_REALTIME, &ts);
   111  		snprintf(tmp, sizeof(tmp), "/dev/shm/tmp-%d", (int)ts.tv_nsec);
   112  		fd = open(tmp, O_CREAT|O_EXCL|FLAGS, mode);
   113  		if (fd < 0) {
   114  			if (errno == EEXIST) continue;
   115  			goto fail;
   116  		}
   117  		if (write(fd, &newsem, sizeof newsem) != sizeof newsem || fstat(fd, &st) < 0 ||
   118  		    (map = mmap(0, sizeof(sem_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
   119  			close(fd);
   120  			unlink(tmp);
   121  			goto fail;
   122  		}
   123  		close(fd);
   124  		e = link(tmp, name) ? errno : 0;
   125  		unlink(tmp);
   126  		if (!e) break;
   127  		munmap(map, sizeof(sem_t));
   128  		/* Failure is only fatal when doing an exclusive open;
   129  		 * otherwise, next iteration will try to open the
   130  		 * existing file. */
   131  		if (e != EEXIST || flags == (O_CREAT|O_EXCL))
   132  			goto fail;
   133  	}
   134  
   135  	/* See if the newly mapped semaphore is already mapped. If
   136  	 * so, unmap the new mapping and use the existing one. Otherwise,
   137  	 * add it to the table of mapped semaphores. */
   138  	LOCK(lock);
   139  	for (i=0; i<SEM_NSEMS_MAX && semtab[i].ino != st.st_ino; i++);
   140  	if (i<SEM_NSEMS_MAX) {
   141  		munmap(map, sizeof(sem_t));
   142  		semtab[slot].sem = 0;
   143  		slot = i;
   144  		map = semtab[i].sem;
   145  	}
   146  	semtab[slot].refcnt++;
   147  	semtab[slot].sem = map;
   148  	semtab[slot].ino = st.st_ino;
   149  	UNLOCK(lock);
   150  	pthread_setcancelstate(cs, 0);
   151  	return map;
   152  
   153  fail:
   154  	pthread_setcancelstate(cs, 0);
   155  	LOCK(lock);
   156  	semtab[slot].sem = 0;
   157  	UNLOCK(lock);
   158  	return SEM_FAILED;
   159  }
   160  
   161  int sem_close(sem_t *sem)
   162  {
   163  	int i;
   164  	LOCK(lock);
   165  	for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++);
   166  	if (!--semtab[i].refcnt) {
   167  		semtab[i].sem = 0;
   168  		semtab[i].ino = 0;
   169  	}
   170  	UNLOCK(lock);
   171  	munmap(sem, sizeof *sem);
   172  	return 0;
   173  }