primitives_pthreads.odin 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. //+build linux, freebsd
  2. //+private
  3. package sync2
  4. when #config(ODIN_SYNC_USE_PTHREADS, true) {
  5. import "core:time"
  6. import "core:runtime"
  7. import "core:sys/unix"
  8. _Mutex_State :: enum i32 {
  9. Unlocked = 0,
  10. Locked = 1,
  11. Waiting = 2,
  12. }
  13. _Mutex :: struct {
  14. pthread_mutex: unix.pthread_mutex_t,
  15. }
  16. _mutex_lock :: proc(m: ^Mutex) {
  17. err := unix.pthread_mutex_lock(&m.impl.pthread_mutex);
  18. assert(err == 0);
  19. }
  20. _mutex_unlock :: proc(m: ^Mutex) {
  21. err := unix.pthread_mutex_unlock(&m.impl.pthread_mutex);
  22. assert(err == 0);
  23. }
  24. _mutex_try_lock :: proc(m: ^Mutex) -> bool {
  25. err := unix.pthread_mutex_trylock(&m.impl.pthread_mutex);
  26. return err == 0;
  27. }
  28. RW_Mutex_State :: distinct uint;
  29. RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2;
  30. RW_Mutex_State_Is_Writing :: RW_Mutex_State(1);
  31. RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1;
  32. RW_Mutex_State_Reader :: RW_Mutex_State(1)<<RW_Mutex_State_Half_Width;
  33. RW_Mutex_State_Writer_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << 1;
  34. RW_Mutex_State_Reader_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << RW_Mutex_State_Half_Width;
  35. _RW_Mutex :: struct {
  36. // NOTE(bill): pthread_rwlock_t cannot be used since pthread_rwlock_destroy is required on some platforms
  37. // TODO(bill): Can we determine which platforms exactly?
  38. state: RW_Mutex_State,
  39. mutex: Mutex,
  40. sema: Sema,
  41. }
  42. _rw_mutex_lock :: proc(rw: ^RW_Mutex) {
  43. _ = atomic_add(&rw.impl.state, RW_Mutex_State_Writer);
  44. mutex_lock(&rw.impl.mutex);
  45. state := atomic_or(&rw.impl.state, RW_Mutex_State_Writer);
  46. if state & RW_Mutex_State_Reader_Mask != 0 {
  47. sema_wait(&rw.impl.sema);
  48. }
  49. }
  50. _rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
  51. _ = atomic_and(&rw.impl.state, ~RW_Mutex_State_Is_Writing);
  52. mutex_unlock(&rw.impl.mutex);
  53. }
  54. _rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
  55. if mutex_try_lock(&rw.impl.mutex) {
  56. state := atomic_load(&rw.impl.state);
  57. if state & RW_Mutex_State_Reader_Mask == 0 {
  58. _ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing);
  59. return true;
  60. }
  61. mutex_unlock(&rw.impl.mutex);
  62. }
  63. return false;
  64. }
  65. _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
  66. state := atomic_load(&rw.impl.state);
  67. for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
  68. ok: bool;
  69. state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader);
  70. if ok {
  71. return;
  72. }
  73. }
  74. mutex_lock(&rw.impl.mutex);
  75. _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader);
  76. mutex_unlock(&rw.impl.mutex);
  77. }
  78. _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
  79. state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader);
  80. if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) &&
  81. (state & RW_Mutex_State_Is_Writing != 0) {
  82. sema_post(&rw.impl.sema);
  83. }
  84. }
  85. _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
  86. state := atomic_load(&rw.impl.state);
  87. if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
  88. _, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader);
  89. if ok {
  90. return true;
  91. }
  92. }
  93. if mutex_try_lock(&rw.impl.mutex) {
  94. _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader);
  95. mutex_unlock(&rw.impl.mutex);
  96. return true;
  97. }
  98. return false;
  99. }
  100. _Recursive_Mutex :: struct {
  101. owner: int,
  102. recursion: int,
  103. mutex: Mutex,
  104. }
  105. _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
  106. tid := _current_thread_id();
  107. if tid != m.impl.owner {
  108. mutex_lock(&m.impl.mutex);
  109. }
  110. // inside the lock
  111. m.impl.owner = tid;
  112. m.impl.recursion += 1;
  113. }
  114. _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
  115. tid := _current_thread_id();
  116. assert(tid == m.impl.owner);
  117. m.impl.recursion -= 1;
  118. recursion := m.impl.recursion;
  119. if recursion == 0 {
  120. m.impl.owner = 0;
  121. }
  122. if recursion == 0 {
  123. mutex_unlock(&m.impl.mutex);
  124. }
  125. // outside the lock
  126. }
  127. _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
  128. tid := _current_thread_id();
  129. if m.impl.owner == tid {
  130. return mutex_try_lock(&m.impl.mutex);
  131. }
  132. if !mutex_try_lock(&m.impl.mutex) {
  133. return false;
  134. }
  135. // inside the lock
  136. m.impl.owner = tid;
  137. m.impl.recursion += 1;
  138. return true;
  139. }
  140. _Cond :: struct {
  141. pthread_cond: unix.pthread_cond_t,
  142. }
  143. _cond_wait :: proc(c: ^Cond, m: ^Mutex) {
  144. err := unix.pthread_cond_wait(&c.impl.pthread_cond, &m.impl.pthread_mutex);
  145. assert(err == 0);
  146. }
  147. _cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, timeout: time.Duration) -> bool {
  148. ns := time.duration_nanoseconds(timeout);
  149. timeout_timespec := &time.TimeSpec{
  150. tv_sec = ns / 1e9,
  151. tv_nsec = ns % 1e9,
  152. };
  153. err := unix.pthread_cond_timedwait(&c.impl.pthread_cond, &m.impl.pthread_mutex, timeout_timespec);
  154. // TODO(bill):
  155. return err == 0;
  156. }
  157. _cond_signal :: proc(c: ^Cond) {
  158. err := unix.pthread_cond_signal(&c.impl.pthread_cond);
  159. assert(err == 0);
  160. }
  161. _cond_broadcast :: proc(c: ^Cond) {
  162. err := unix.pthread_cond_broadcast(&c.impl.pthread_cond);
  163. assert(err == 0);
  164. }
  165. _Sema :: struct {
  166. mutex: Mutex,
  167. cond: Cond,
  168. count: int,
  169. }
  170. _sema_wait :: proc(s: ^Sema) {
  171. mutex_lock(&s.impl.mutex);
  172. defer mutex_unlock(&s.impl.mutex);
  173. for s.impl.count == 0 {
  174. cond_wait(&s.impl.cond, &s.impl.mutex);
  175. }
  176. s.impl.count -= 1;
  177. if s.impl.count > 0 {
  178. cond_signal(&s.impl.cond);
  179. }
  180. }
  181. _sema_post :: proc(s: ^Sema, count := 1) {
  182. mutex_lock(&s.impl.mutex);
  183. defer mutex_unlock(&s.impl.mutex);
  184. s.impl.count += count;
  185. cond_signal(&s.impl.cond);
  186. }
  187. } // ODIN_SYNC_USE_PTHREADS