semaphore.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. /*
  2. * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
  3. * Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
  4. * Copyright (c) 2022, Idan Horowitz <idan.horowitz@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/Assertions.h>
  9. #include <AK/Atomic.h>
  10. #include <AK/HashMap.h>
  11. #include <AK/ScopeGuard.h>
  12. #include <AK/String.h>
  13. #include <AK/Types.h>
  14. #include <bits/pthread_cancel.h>
  15. #include <errno.h>
  16. #include <fcntl.h>
  17. #include <pthread.h>
  18. #include <semaphore.h>
  19. #include <serenity.h>
  20. #include <string.h>
  21. #include <sys/file.h>
  22. #include <sys/mman.h>
  23. #include <sys/stat.h>
  24. static constexpr u32 SEM_MAGIC = 0x78951230;
  25. // Whether sem_wait() or sem_post() is responsible for waking any sleeping
  26. // threads.
  27. static constexpr u32 POST_WAKES = 1 << 31;
  28. static constexpr auto sem_path_prefix = "/tmp/semaphore/"sv;
  29. static constexpr auto SEM_NAME_MAX = PATH_MAX - sem_path_prefix.length();
  30. static ErrorOr<String> sem_name_to_path(char const* name)
  31. {
  32. if (name[0] != '/')
  33. return EINVAL;
  34. ++name;
  35. auto name_length = strnlen(name, SEM_NAME_MAX);
  36. if (name[name_length])
  37. return ENAMETOOLONG;
  38. auto name_view = StringView { name, name_length };
  39. if (name_view.contains('/'))
  40. return EINVAL;
  41. StringBuilder builder;
  42. TRY(builder.try_append(sem_path_prefix));
  43. TRY(builder.try_append(name_view));
  44. return builder.build();
  45. }
  46. struct NamedSemaphore {
  47. size_t times_opened { 0 };
  48. dev_t dev { 0 };
  49. ino_t ino { 0 };
  50. sem_t* sem { nullptr };
  51. };
  52. static HashMap<String, NamedSemaphore> s_named_semaphores;
  53. static pthread_mutex_t s_sem_mutex = PTHREAD_MUTEX_INITIALIZER;
  54. static pthread_once_t s_sem_once = PTHREAD_ONCE_INIT;
  55. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html
  56. sem_t* sem_open(char const* name, int flags, ...)
  57. {
  58. auto path_or_error = sem_name_to_path(name);
  59. if (path_or_error.is_error()) {
  60. errno = path_or_error.error().code();
  61. return SEM_FAILED;
  62. }
  63. auto path = path_or_error.release_value();
  64. if (flags & ~(O_CREAT | O_EXCL)) {
  65. errno = EINVAL;
  66. return SEM_FAILED;
  67. }
  68. mode_t mode = 0;
  69. unsigned int value = 0;
  70. if (flags & O_CREAT) {
  71. va_list ap;
  72. va_start(ap, flags);
  73. mode = va_arg(ap, unsigned int);
  74. value = va_arg(ap, unsigned int);
  75. va_end(ap);
  76. }
  77. // Ensure we are not in the middle of modifying this structure while a child is being forked, which will cause the child to end up with a partially-modified entry
  78. pthread_once(&s_sem_once, []() {
  79. pthread_atfork([]() { pthread_mutex_lock(&s_sem_mutex); }, []() { pthread_mutex_unlock(&s_sem_mutex); }, []() { pthread_mutex_unlock(&s_sem_mutex); });
  80. });
  81. pthread_mutex_lock(&s_sem_mutex);
  82. ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_sem_mutex); };
  83. int fd = open(path.characters(), O_RDWR | O_CLOEXEC | flags, mode);
  84. if (fd == -1)
  85. return SEM_FAILED;
  86. ScopeGuard close_guard = [&fd] {
  87. if (fd != -1)
  88. close(fd);
  89. };
  90. if (flock(fd, LOCK_EX) == -1)
  91. return SEM_FAILED;
  92. struct stat statbuf;
  93. if (fstat(fd, &statbuf) == -1)
  94. return SEM_FAILED;
  95. auto existing_semaphore = s_named_semaphores.get(path);
  96. if (existing_semaphore.has_value()) {
  97. // If the file did not exist (aka if O_CREAT && O_EXCL but no EEXIST), or if the inode was replaced, remove the entry and start from scratch
  98. if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL) || existing_semaphore->dev != statbuf.st_dev || existing_semaphore->ino != statbuf.st_ino) {
  99. s_named_semaphores.remove(path);
  100. } else { // otherwise, this is valid pre-existing named semaphore, so just increase the count and return it
  101. existing_semaphore->times_opened++;
  102. return existing_semaphore->sem;
  103. }
  104. }
  105. // If the file is smaller than the size, it's an uninitialized semaphore, so let's write an initial value
  106. if (statbuf.st_size < (off_t)sizeof(sem_t)) {
  107. sem_t init_sem;
  108. init_sem.magic = SEM_MAGIC;
  109. init_sem.value = value;
  110. init_sem.flags = SEM_FLAG_PROCESS_SHARED | SEM_FLAG_NAMED;
  111. if (write(fd, &init_sem, sizeof(sem_t)) != sizeof(sem_t))
  112. return SEM_FAILED;
  113. }
  114. if (flock(fd, LOCK_UN) == -1)
  115. return SEM_FAILED;
  116. auto* sem = (sem_t*)mmap(nullptr, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  117. if (sem == MAP_FAILED)
  118. return SEM_FAILED;
  119. ArmedScopeGuard munmap_guard = [&sem] {
  120. munmap(sem, sizeof(sem_t));
  121. };
  122. if (sem->magic != SEM_MAGIC) {
  123. errno = EINVAL;
  124. return SEM_FAILED;
  125. }
  126. auto result = s_named_semaphores.try_set(move(path), { .times_opened = 1, .dev = statbuf.st_dev, .ino = statbuf.st_ino, .sem = sem });
  127. if (result.is_error()) {
  128. errno = result.error().code();
  129. return SEM_FAILED;
  130. }
  131. munmap_guard.disarm();
  132. return sem;
  133. }
  134. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html
  135. int sem_close(sem_t* sem)
  136. {
  137. if (sem->magic != SEM_MAGIC) {
  138. errno = EINVAL;
  139. return -1;
  140. }
  141. if ((sem->flags & SEM_FLAG_NAMED) == 0) {
  142. errno = EINVAL;
  143. return -1;
  144. }
  145. pthread_mutex_lock(&s_sem_mutex);
  146. ScopeGuard unlock_guard = [] { pthread_mutex_unlock(&s_sem_mutex); };
  147. auto it = s_named_semaphores.begin();
  148. for (; it != s_named_semaphores.end(); ++it) {
  149. if (it->value.sem != sem)
  150. continue;
  151. auto is_last = --it->value.times_opened == 0;
  152. if (is_last) {
  153. munmap(it->value.sem, sizeof(sem_t));
  154. s_named_semaphores.remove(it);
  155. }
  156. return 0;
  157. }
  158. errno = EINVAL;
  159. return -1;
  160. }
  161. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html
  162. int sem_unlink(char const* name)
  163. {
  164. auto path_or_error = sem_name_to_path(name);
  165. if (path_or_error.is_error()) {
  166. errno = path_or_error.error().code();
  167. return -1;
  168. }
  169. auto path = path_or_error.release_value();
  170. return unlink(path.characters());
  171. }
  172. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html
  173. int sem_init(sem_t* sem, int process_shared, unsigned int value)
  174. {
  175. if (value > SEM_VALUE_MAX) {
  176. errno = EINVAL;
  177. return -1;
  178. }
  179. sem->magic = SEM_MAGIC;
  180. sem->value = value;
  181. sem->flags = process_shared ? SEM_FLAG_PROCESS_SHARED : 0;
  182. return 0;
  183. }
  184. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html
  185. int sem_destroy(sem_t* sem)
  186. {
  187. if (sem->magic != SEM_MAGIC) {
  188. errno = EINVAL;
  189. return -1;
  190. }
  191. if (sem->flags & SEM_FLAG_NAMED) {
  192. errno = EINVAL;
  193. return -1;
  194. }
  195. sem->magic = 0;
  196. return 0;
  197. }
  198. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html
  199. int sem_getvalue(sem_t* sem, int* sval)
  200. {
  201. if (sem->magic != SEM_MAGIC) {
  202. errno = EINVAL;
  203. return -1;
  204. }
  205. u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
  206. *sval = value & ~POST_WAKES;
  207. return 0;
  208. }
  209. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html
  210. int sem_post(sem_t* sem)
  211. {
  212. if (sem->magic != SEM_MAGIC) {
  213. errno = EINVAL;
  214. return -1;
  215. }
  216. u32 value = AK::atomic_fetch_add(&sem->value, 1u, AK::memory_order_release);
  217. // Fast path: no need to wake.
  218. if (!(value & POST_WAKES)) [[likely]]
  219. return 0;
  220. // Pass the responsibility for waking more threads if more slots become
  221. // available later to sem_wait() in the thread we're about to wake, as
  222. // opposed to further sem_post() calls that free up those slots.
  223. value = AK::atomic_fetch_and(&sem->value, ~POST_WAKES, AK::memory_order_relaxed);
  224. // Check if another sem_post() call has handled it already.
  225. if (!(value & POST_WAKES)) [[likely]]
  226. return 0;
  227. int rc = futex_wake(&sem->value, 1, sem->flags & SEM_FLAG_PROCESS_SHARED);
  228. VERIFY(rc >= 0);
  229. return 0;
  230. }
  231. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html
  232. int sem_trywait(sem_t* sem)
  233. {
  234. if (sem->magic != SEM_MAGIC) {
  235. errno = EINVAL;
  236. return -1;
  237. }
  238. u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
  239. u32 count = value & ~POST_WAKES;
  240. if (count == 0) {
  241. errno = EAGAIN;
  242. return -1;
  243. }
  244. // Decrement the count without touching the flag.
  245. u32 desired = (count - 1) | (value & POST_WAKES);
  246. bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire);
  247. if (exchanged) [[likely]] {
  248. return 0;
  249. } else {
  250. errno = EAGAIN;
  251. return -1;
  252. }
  253. }
  254. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_wait.html
  255. int sem_wait(sem_t* sem)
  256. {
  257. if (sem->magic != SEM_MAGIC) {
  258. errno = EINVAL;
  259. return -1;
  260. }
  261. return sem_timedwait(sem, nullptr);
  262. }
  263. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html
  264. int sem_timedwait(sem_t* sem, const struct timespec* abstime)
  265. {
  266. __pthread_maybe_cancel();
  267. if (sem->magic != SEM_MAGIC) {
  268. errno = EINVAL;
  269. return -1;
  270. }
  271. u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
  272. bool responsible_for_waking = false;
  273. bool process_shared = sem->flags & SEM_FLAG_PROCESS_SHARED;
  274. while (true) {
  275. u32 count = value & ~POST_WAKES;
  276. if (count > 0) [[likely]] {
  277. // It looks like there are some free slots.
  278. u32 whether_post_wakes = value & POST_WAKES;
  279. bool going_to_wake = false;
  280. if (responsible_for_waking && !whether_post_wakes) {
  281. // If we have ourselves been woken up previously, and the
  282. // POST_WAKES flag is not set, that means some more slots might
  283. // be available now, and it's us who has to wake up additional
  284. // threads.
  285. if (count > 1) [[unlikely]]
  286. going_to_wake = true;
  287. // Pass the responsibility for waking up further threads back to
  288. // sem_post() calls. In particular, we don't want the threads
  289. // we're about to wake to try to wake anyone else.
  290. whether_post_wakes = POST_WAKES;
  291. }
  292. // Now, try to commit this.
  293. u32 desired = (count - 1) | whether_post_wakes;
  294. bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire);
  295. if (!exchanged) [[unlikely]]
  296. // Re-evaluate.
  297. continue;
  298. if (going_to_wake) [[unlikely]] {
  299. int rc = futex_wake(&sem->value, count - 1, process_shared);
  300. VERIFY(rc >= 0);
  301. }
  302. return 0;
  303. }
  304. // We're probably going to sleep, so attempt to set the flag. We do not
  305. // commit to sleeping yet, though, as setting the flag may fail and
  306. // cause us to reevaluate what we're doing.
  307. if (value == 0) {
  308. bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, POST_WAKES, AK::memory_order_relaxed);
  309. if (!exchanged) [[unlikely]]
  310. // Re-evaluate.
  311. continue;
  312. value = POST_WAKES;
  313. }
  314. // At this point, we're committed to sleeping.
  315. responsible_for_waking = true;
  316. futex_wait(&sem->value, value, abstime, CLOCK_REALTIME, process_shared);
  317. // This is the state we will probably see upon being waked:
  318. value = 1;
  319. }
  320. }