semaphore.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org>
  3. * Copyright (c) 2021, Sergey Bugaev <bugaevc@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Assertions.h>
  8. #include <AK/Atomic.h>
  9. #include <AK/Types.h>
  10. #include <errno.h>
  11. #include <semaphore.h>
  12. #include <serenity.h>
  13. // Whether sem_wait() or sem_post() is responsible for waking any sleeping
  14. // threads.
  15. static constexpr u32 POST_WAKES = 1 << 31;
  16. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_open.html
  17. sem_t* sem_open(char const*, int, ...)
  18. {
  19. errno = ENOSYS;
  20. return nullptr;
  21. }
  22. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_close.html
  23. int sem_close(sem_t*)
  24. {
  25. errno = ENOSYS;
  26. return -1;
  27. }
  28. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_unlink.html
  29. int sem_unlink(char const*)
  30. {
  31. errno = ENOSYS;
  32. return -1;
  33. }
  34. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_init.html
  35. int sem_init(sem_t* sem, int shared, unsigned int value)
  36. {
  37. if (shared) {
  38. errno = ENOSYS;
  39. return -1;
  40. }
  41. if (value > SEM_VALUE_MAX) {
  42. errno = EINVAL;
  43. return -1;
  44. }
  45. sem->value = value;
  46. return 0;
  47. }
  48. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_destroy.html
  49. int sem_destroy(sem_t*)
  50. {
  51. return 0;
  52. }
  53. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_getvalue.html
  54. int sem_getvalue(sem_t* sem, int* sval)
  55. {
  56. u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
  57. *sval = value & ~POST_WAKES;
  58. return 0;
  59. }
  60. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_post.html
  61. int sem_post(sem_t* sem)
  62. {
  63. u32 value = AK::atomic_fetch_add(&sem->value, 1u, AK::memory_order_release);
  64. // Fast path: no need to wake.
  65. if (!(value & POST_WAKES)) [[likely]]
  66. return 0;
  67. // Pass the responsibility for waking more threads if more slots become
  68. // available later to sem_wait() in the thread we're about to wake, as
  69. // opposed to further sem_post() calls that free up those slots.
  70. value = AK::atomic_fetch_and(&sem->value, ~POST_WAKES, AK::memory_order_relaxed);
  71. // Check if another sem_post() call has handled it already.
  72. if (!(value & POST_WAKES)) [[likely]]
  73. return 0;
  74. int rc = futex_wake(&sem->value, 1);
  75. VERIFY(rc >= 0);
  76. return 0;
  77. }
  78. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_trywait.html
  79. int sem_trywait(sem_t* sem)
  80. {
  81. u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
  82. u32 count = value & ~POST_WAKES;
  83. if (count == 0)
  84. return EAGAIN;
  85. // Decrement the count without touching the flag.
  86. u32 desired = (count - 1) | (value & POST_WAKES);
  87. bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire);
  88. if (exchanged) [[likely]]
  89. return 0;
  90. else
  91. return EAGAIN;
  92. }
  93. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_wait.html
  94. int sem_wait(sem_t* sem)
  95. {
  96. return sem_timedwait(sem, nullptr);
  97. }
  98. // https://pubs.opengroup.org/onlinepubs/9699919799/functions/sem_timedwait.html
  99. int sem_timedwait(sem_t* sem, const struct timespec* abstime)
  100. {
  101. u32 value = AK::atomic_load(&sem->value, AK::memory_order_relaxed);
  102. bool responsible_for_waking = false;
  103. while (true) {
  104. u32 count = value & ~POST_WAKES;
  105. if (count > 0) [[likely]] {
  106. // It looks like there are some free slots.
  107. u32 whether_post_wakes = value & POST_WAKES;
  108. bool going_to_wake = false;
  109. if (responsible_for_waking && !whether_post_wakes) {
  110. // If we have ourselves been woken up previously, and the
  111. // POST_WAKES flag is not set, that means some more slots might
  112. // be available now, and it's us who has to wake up additional
  113. // threads.
  114. if (count > 1) [[unlikely]]
  115. going_to_wake = true;
  116. // Pass the responsibility for waking up further threads back to
  117. // sem_post() calls. In particular, we don't want the threads
  118. // we're about to wake to try to wake anyone else.
  119. whether_post_wakes = POST_WAKES;
  120. }
  121. // Now, try to commit this.
  122. u32 desired = (count - 1) | whether_post_wakes;
  123. bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, desired, AK::memory_order_acquire);
  124. if (!exchanged) [[unlikely]]
  125. // Re-evaluate.
  126. continue;
  127. if (going_to_wake) [[unlikely]] {
  128. int rc = futex_wake(&sem->value, count - 1);
  129. VERIFY(rc >= 0);
  130. }
  131. return 0;
  132. }
  133. // We're probably going to sleep, so attempt to set the flag. We do not
  134. // commit to sleeping yet, though, as setting the flag may fail and
  135. // cause us to reevaluate what we're doing.
  136. if (value == 0) {
  137. bool exchanged = AK::atomic_compare_exchange_strong(&sem->value, value, POST_WAKES, AK::memory_order_relaxed);
  138. if (!exchanged) [[unlikely]]
  139. // Re-evaluate.
  140. continue;
  141. value = POST_WAKES;
  142. }
  143. // At this point, we're committed to sleeping.
  144. responsible_for_waking = true;
  145. futex_wait(&sem->value, value, abstime, CLOCK_REALTIME);
  146. // This is the state we will probably see upon being waked:
  147. value = 1;
  148. }
  149. }