thread_unix.odin 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. // +build linux, darwin, freebsd, openbsd
  2. // +private
  3. package thread
  4. import "core:runtime"
  5. import "core:intrinsics"
  6. import "core:sync"
  7. import "core:sys/unix"
  8. CAS :: intrinsics.atomic_compare_exchange_strong
  9. Thread_State :: enum u8 {
  10. Started,
  11. Joined,
  12. Done,
  13. }
  14. // NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
  15. // Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
  16. Thread_Os_Specific :: struct #align 16 {
  17. unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
  18. cond: sync.Cond,
  19. mutex: sync.Mutex,
  20. flags: bit_set[Thread_State; u8],
  21. }
  22. //
  23. // Creates a thread which will run the given procedure.
  24. // It then waits for `start` to be called.
  25. //
  26. _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
  27. __linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
  28. t := (^Thread)(t)
  29. when ODIN_OS != .Darwin {
  30. // We need to give the thread a moment to start up before we enable cancellation.
  31. can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil) == 0
  32. }
  33. context = runtime.default_context()
  34. sync.lock(&t.mutex)
  35. t.id = sync.current_thread_id()
  36. for (.Started not_in t.flags) {
  37. sync.wait(&t.cond, &t.mutex)
  38. }
  39. init_context := t.init_context
  40. context = init_context.? or_else runtime.default_context()
  41. when ODIN_OS != .Darwin {
  42. // Enable thread's cancelability.
  43. if can_set_thread_cancel_state {
  44. unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
  45. unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil)
  46. }
  47. }
  48. t.procedure(t)
  49. intrinsics.atomic_store(&t.flags, t.flags + { .Done })
  50. sync.unlock(&t.mutex)
  51. if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
  52. runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
  53. }
  54. return nil
  55. }
  56. attrs: unix.pthread_attr_t
  57. if unix.pthread_attr_init(&attrs) != 0 {
  58. return nil // NOTE(tetra, 2019-11-01): POSIX OOM.
  59. }
  60. defer unix.pthread_attr_destroy(&attrs)
  61. // NOTE(tetra, 2019-11-01): These only fail if their argument is invalid.
  62. assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0)
  63. assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0)
  64. thread := new(Thread)
  65. if thread == nil {
  66. return nil
  67. }
  68. thread.creation_allocator = context.allocator
  69. // Set thread priority.
  70. policy: i32
  71. res := unix.pthread_attr_getschedpolicy(&attrs, &policy)
  72. assert(res == 0)
  73. params: unix.sched_param
  74. res = unix.pthread_attr_getschedparam(&attrs, &params)
  75. assert(res == 0)
  76. low := unix.sched_get_priority_min(policy)
  77. high := unix.sched_get_priority_max(policy)
  78. switch priority {
  79. case .Normal: // Okay
  80. case .Low: params.sched_priority = low + 1
  81. case .High: params.sched_priority = high
  82. }
  83. res = unix.pthread_attr_setschedparam(&attrs, &params)
  84. assert(res == 0)
  85. thread.procedure = procedure
  86. if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
  87. free(thread, thread.creation_allocator)
  88. return nil
  89. }
  90. return thread
  91. }
  92. _start :: proc(t: ^Thread) {
  93. // sync.guard(&t.mutex)
  94. t.flags += { .Started }
  95. sync.signal(&t.cond)
  96. }
  97. _is_done :: proc(t: ^Thread) -> bool {
  98. return .Done in intrinsics.atomic_load(&t.flags)
  99. }
  100. _join :: proc(t: ^Thread) {
  101. // sync.guard(&t.mutex)
  102. if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
  103. return
  104. }
  105. // Preserve other flags besides `.Joined`, like `.Started`.
  106. unjoined := intrinsics.atomic_load(&t.flags) - {.Joined}
  107. joined := unjoined + {.Joined}
  108. // Try to set `t.flags` from unjoined to joined. If it returns joined,
  109. // it means the previous value had that flag set and we can return.
  110. if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok {
  111. return
  112. }
  113. unix.pthread_join(t.unix_thread, nil)
  114. }
  115. _join_multiple :: proc(threads: ..^Thread) {
  116. for t in threads {
  117. _join(t)
  118. }
  119. }
  120. _destroy :: proc(t: ^Thread) {
  121. _join(t)
  122. t.unix_thread = {}
  123. free(t, t.creation_allocator)
  124. }
  125. _terminate :: proc(t: ^Thread, exit_code: int) {
  126. // `pthread_cancel` is unreliable on Darwin for unknown reasons.
  127. when ODIN_OS != .Darwin {
  128. unix.pthread_cancel(t.unix_thread)
  129. }
  130. }
  131. _yield :: proc() {
  132. unix.sched_yield()
  133. }