1
0

sem_open.c 4.0 KB

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