futex_haiku.odin 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. //+private
  2. package sync
  3. import "core:c"
  4. import "base:runtime"
  5. import "core:sys/haiku"
  6. import "core:sys/unix"
  7. import "core:time"
  8. @(private="file")
  9. Wait_Node :: struct {
  10. thread: unix.pthread_t,
  11. futex: ^Futex,
  12. prev, next: ^Wait_Node,
  13. }
  14. @(private="file")
  15. atomic_flag :: distinct bool
  16. @(private="file")
  17. Wait_Queue :: struct {
  18. lock: atomic_flag,
  19. list: Wait_Node,
  20. }
  21. @(private="file")
  22. waitq_lock :: proc "contextless" (waitq: ^Wait_Queue) {
  23. for cast(bool)atomic_exchange_explicit(&waitq.lock, atomic_flag(true), .Acquire) {
  24. cpu_relax() // spin...
  25. }
  26. }
  27. @(private="file")
  28. waitq_unlock :: proc "contextless" (waitq: ^Wait_Queue) {
  29. atomic_store_explicit(&waitq.lock, atomic_flag(false), .Release)
  30. }
  31. // FIXME: This approach may scale badly in the future,
  32. // possible solution - hash map (leads to deadlocks now).
  33. @(private="file")
  34. g_waitq: Wait_Queue
  35. @(init, private="file")
  36. g_waitq_init :: proc() {
  37. g_waitq = {
  38. list = {
  39. prev = &g_waitq.list,
  40. next = &g_waitq.list,
  41. },
  42. }
  43. }
  44. @(private="file")
  45. get_waitq :: #force_inline proc "contextless" (f: ^Futex) -> ^Wait_Queue {
  46. _ = f
  47. return &g_waitq
  48. }
  49. _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
  50. waitq := get_waitq(f)
  51. waitq_lock(waitq)
  52. defer waitq_unlock(waitq)
  53. head := &waitq.list
  54. waiter := Wait_Node{
  55. thread = unix.pthread_self(),
  56. futex = f,
  57. prev = head,
  58. next = head.next,
  59. }
  60. waiter.prev.next = &waiter
  61. waiter.next.prev = &waiter
  62. old_mask, mask: haiku.sigset_t
  63. haiku.sigemptyset(&mask)
  64. haiku.sigaddset(&mask, haiku.SIGCONT)
  65. unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
  66. if u32(atomic_load_explicit(f, .Acquire)) == expect {
  67. waitq_unlock(waitq)
  68. defer waitq_lock(waitq)
  69. sig: c.int
  70. haiku.sigwait(&mask, &sig)
  71. errno := haiku.errno()
  72. ok = errno == .OK
  73. }
  74. waiter.prev.next = waiter.next
  75. waiter.next.prev = waiter.prev
  76. unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
  77. // FIXME: Add error handling!
  78. return
  79. }
  80. _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) {
  81. if duration <= 0 {
  82. return false
  83. }
  84. waitq := get_waitq(f)
  85. waitq_lock(waitq)
  86. defer waitq_unlock(waitq)
  87. head := &waitq.list
  88. waiter := Wait_Node{
  89. thread = unix.pthread_self(),
  90. futex = f,
  91. prev = head,
  92. next = head.next,
  93. }
  94. waiter.prev.next = &waiter
  95. waiter.next.prev = &waiter
  96. old_mask, mask: haiku.sigset_t
  97. haiku.sigemptyset(&mask)
  98. haiku.sigaddset(&mask, haiku.SIGCONT)
  99. unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
  100. if u32(atomic_load_explicit(f, .Acquire)) == expect {
  101. waitq_unlock(waitq)
  102. defer waitq_lock(waitq)
  103. info: haiku.siginfo_t
  104. ts := unix.timespec{
  105. tv_sec = i64(duration / 1e9),
  106. tv_nsec = i64(duration % 1e9),
  107. }
  108. haiku.sigtimedwait(&mask, &info, &ts)
  109. errno := haiku.errno()
  110. ok = errno == .EAGAIN || errno == .OK
  111. }
  112. waiter.prev.next = waiter.next
  113. waiter.next.prev = waiter.prev
  114. unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
  115. // FIXME: Add error handling!
  116. return
  117. }
  118. _futex_signal :: proc "contextless" (f: ^Futex) {
  119. waitq := get_waitq(f)
  120. waitq_lock(waitq)
  121. defer waitq_unlock(waitq)
  122. head := &waitq.list
  123. for waiter := head.next; waiter != head; waiter = waiter.next {
  124. if waiter.futex == f {
  125. unix.pthread_kill(waiter.thread, haiku.SIGCONT)
  126. break
  127. }
  128. }
  129. }
  130. _futex_broadcast :: proc "contextless" (f: ^Futex) {
  131. waitq := get_waitq(f)
  132. waitq_lock(waitq)
  133. defer waitq_unlock(waitq)
  134. head := &waitq.list
  135. for waiter := head.next; waiter != head; waiter = waiter.next {
  136. if waiter.futex == f {
  137. unix.pthread_kill(waiter.thread, haiku.SIGCONT)
  138. }
  139. }
  140. }