primitives.odin 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. package sync
  2. import "base:runtime"
  3. import "core:time"
  4. current_thread_id :: proc "contextless" () -> int {
  5. return _current_thread_id()
  6. }
  7. // A Mutex is a [[mutual exclusion lock; https://en.wikipedia.org/wiki/Mutual_exclusion]]
  8. // It can be used to prevent more than one thread from executing the same piece of code,
  9. // and thus prevent access to same piece of memory by multiple threads, at the same time.
  10. //
  11. // A Mutex's zero value represents an initial, *unlocked* state.
  12. //
  13. // If another thread tries to take the lock while another thread holds it, it will pause
  14. // until the lock is released. Code or memory that is "surrounded" by a mutex lock is said
  15. // to be "guarded by a mutex".
  16. //
  17. // A Mutex must not be copied after first use (e.g., after locking it the first time).
  18. // This is because, in order to coordinate with other threads, all threads must watch
  19. // the same memory address to know when the lock has been released. Trying to use a
  20. // copy of the lock at a different memory address will result in broken and unsafe
  21. // behavior. For this reason, Mutexes are marked as `#no_copy`.
  22. Mutex :: struct #no_copy {
  23. impl: _Mutex,
  24. }
  25. // mutex_lock locks m
  26. mutex_lock :: proc "contextless" (m: ^Mutex) {
  27. _mutex_lock(m)
  28. }
  29. // mutex_unlock unlocks m
  30. mutex_unlock :: proc "contextless" (m: ^Mutex) {
  31. _mutex_unlock(m)
  32. }
  33. // mutex_try_lock tries to lock m, will return true on success, and false on failure
  34. mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
  35. return _mutex_try_lock(m)
  36. }
  37. /*
  38. Example:
  39. if mutex_guard(&m) {
  40. ...
  41. }
  42. */
  43. @(deferred_in=mutex_unlock)
  44. mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
  45. mutex_lock(m)
  46. return true
  47. }
  48. // A RW_Mutex is a reader/writer mutual exclusion lock
  49. // The lock can be held by any arbitrary number of readers or a single writer
  50. // The zero value for a RW_Mutex is an unlocked mutex
  51. //
  52. // A RW_Mutex must not be copied after first use
  53. RW_Mutex :: struct #no_copy {
  54. impl: _RW_Mutex,
  55. }
  56. // rw_mutex_lock locks rw for writing (with a single writer)
  57. // If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
  58. rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
  59. _rw_mutex_lock(rw)
  60. }
  61. // rw_mutex_unlock unlocks rw for writing (with a single writer)
  62. rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
  63. _rw_mutex_unlock(rw)
  64. }
  65. // rw_mutex_try_lock tries to lock rw for writing (with a single writer)
  66. rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
  67. return _rw_mutex_try_lock(rw)
  68. }
  69. // rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
  70. rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
  71. _rw_mutex_shared_lock(rw)
  72. }
  73. // rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
  74. rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
  75. _rw_mutex_shared_unlock(rw)
  76. }
  77. // rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
  78. rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
  79. return _rw_mutex_try_shared_lock(rw)
  80. }
  81. /*
  82. Example:
  83. if rw_mutex_guard(&m) {
  84. ...
  85. }
  86. */
  87. @(deferred_in=rw_mutex_unlock)
  88. rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
  89. rw_mutex_lock(m)
  90. return true
  91. }
  92. /*
  93. Example:
  94. if rw_mutex_shared_guard(&m) {
  95. ...
  96. }
  97. */
  98. @(deferred_in=rw_mutex_shared_unlock)
  99. rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
  100. rw_mutex_shared_lock(m)
  101. return true
  102. }
  103. // A Recursive_Mutex is a recursive mutual exclusion lock
  104. // The zero value for a Recursive_Mutex is an unlocked mutex
  105. //
  106. // A Recursive_Mutex must not be copied after first use
  107. Recursive_Mutex :: struct #no_copy {
  108. impl: _Recursive_Mutex,
  109. }
  110. recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
  111. _recursive_mutex_lock(m)
  112. }
  113. recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
  114. _recursive_mutex_unlock(m)
  115. }
  116. recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
  117. return _recursive_mutex_try_lock(m)
  118. }
  119. /*
  120. Example:
  121. if recursive_mutex_guard(&m) {
  122. ...
  123. }
  124. */
  125. @(deferred_in=recursive_mutex_unlock)
  126. recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
  127. recursive_mutex_lock(m)
  128. return true
  129. }
  130. // Cond implements a condition variable, a rendezvous point for threads
  131. // waiting for signalling the occurence of an event
  132. //
  133. // A Cond must not be copied after first use
  134. Cond :: struct #no_copy {
  135. impl: _Cond,
  136. }
  137. cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
  138. _cond_wait(c, m)
  139. }
  140. cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
  141. if duration <= 0 {
  142. return false
  143. }
  144. return _cond_wait_with_timeout(c, m, duration)
  145. }
  146. cond_signal :: proc "contextless" (c: ^Cond) {
  147. _cond_signal(c)
  148. }
  149. cond_broadcast :: proc "contextless" (c: ^Cond) {
  150. _cond_broadcast(c)
  151. }
  152. // When waited upon, blocks until the internal count is greater than zero, then subtracts one.
  153. // Posting to the semaphore increases the count by one, or the provided amount.
  154. //
  155. // A Sema must not be copied after first use
  156. Sema :: struct #no_copy {
  157. impl: _Sema,
  158. }
  159. sema_post :: proc "contextless" (s: ^Sema, count := 1) {
  160. _sema_post(s, count)
  161. }
  162. sema_wait :: proc "contextless" (s: ^Sema) {
  163. _sema_wait(s)
  164. }
  165. sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
  166. return _sema_wait_with_timeout(s, duration)
  167. }
  168. // Futex is a fast userspace mutual exclusion lock, using a 32-bit memory address as a hint
  169. //
  170. // An Futex must not be copied after first use
  171. Futex :: distinct u32
  172. futex_wait :: proc "contextless" (f: ^Futex, expected: u32) {
  173. if u32(atomic_load_explicit(f, .Acquire)) != expected {
  174. return
  175. }
  176. _assert(_futex_wait(f, expected), "futex_wait failure")
  177. }
  178. // returns true if the wait happened within the duration, false if it exceeded the time duration
  179. futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
  180. if u32(atomic_load_explicit(f, .Acquire)) != expected {
  181. return true
  182. }
  183. if duration <= 0 {
  184. return false
  185. }
  186. return _futex_wait_with_timeout(f, expected, duration)
  187. }
  188. futex_signal :: proc "contextless" (f: ^Futex) {
  189. _futex_signal(f)
  190. }
  191. futex_broadcast :: proc "contextless" (f: ^Futex) {
  192. _futex_broadcast(f)
  193. }
  194. @(private)
  195. _assert :: proc "contextless" (cond: bool, msg: string) {
  196. if !cond {
  197. _panic(msg)
  198. }
  199. }
  200. @(private)
  201. _panic :: proc "contextless" (msg: string) -> ! {
  202. runtime.print_string(msg)
  203. runtime.print_byte('\n')
  204. runtime.trap()
  205. }