thread_windows.odin 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package thread
  2. import "core:runtime"
  3. import "core:sync"
  4. import win32 "core:sys/windows"
  5. Thread_Os_Specific :: struct {
  6. win32_thread: win32.HANDLE,
  7. win32_thread_id: win32.DWORD,
  8. done: bool, // see note in `is_done`
  9. }
  10. Thread_Priority :: enum {
  11. Normal,
  12. Low,
  13. High,
  14. }
  15. _thread_priority_map := [Thread_Priority]i32{
  16. .Normal = 0,
  17. .Low = -2,
  18. .High = +2,
  19. };
  20. create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
  21. win32_thread_id: win32.DWORD;
  22. __windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
  23. t := (^Thread)(t_);
  24. context = runtime.default_context();
  25. c := context;
  26. if ic, ok := t.init_context.?; ok {
  27. c = ic;
  28. }
  29. context = c;
  30. t.procedure(t);
  31. if t.init_context == nil {
  32. if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
  33. runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data);
  34. }
  35. }
  36. sync.atomic_store(&t.done, true, .Sequentially_Consistent);
  37. return 0;
  38. }
  39. thread := new(Thread);
  40. win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
  41. if win32_thread == nil {
  42. free(thread);
  43. return nil;
  44. }
  45. thread.procedure = procedure;
  46. thread.win32_thread = win32_thread;
  47. thread.win32_thread_id = win32_thread_id;
  48. thread.init_context = context;
  49. ok := win32.SetThreadPriority(win32_thread, _thread_priority_map[priority]);
  50. assert(ok == true);
  51. return thread;
  52. }
  53. start :: proc(using thread: ^Thread) {
  54. win32.ResumeThread(win32_thread);
  55. }
  56. is_done :: proc(using thread: ^Thread) -> bool {
  57. // NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
  58. // checking if it didn't time out immediately, is not good enough,
  59. // so we do it this way instead.
  60. return sync.atomic_load(&done, .Sequentially_Consistent);
  61. }
  62. join :: proc(using thread: ^Thread) {
  63. if win32_thread != win32.INVALID_HANDLE {
  64. win32.WaitForSingleObject(win32_thread, win32.INFINITE);
  65. win32.CloseHandle(win32_thread);
  66. win32_thread = win32.INVALID_HANDLE;
  67. }
  68. }
  69. join_multiple :: proc(threads: ..^Thread) {
  70. MAXIMUM_WAIT_OBJECTS :: 64;
  71. handles: [MAXIMUM_WAIT_OBJECTS]win32.HANDLE;
  72. for k := 0; k < len(threads); k += MAXIMUM_WAIT_OBJECTS {
  73. count := min(len(threads) - k, MAXIMUM_WAIT_OBJECTS);
  74. n, j := u32(0), 0;
  75. for i in 0..<count {
  76. handle := threads[i+k].win32_thread;
  77. if handle != win32.INVALID_HANDLE {
  78. handles[j] = handle;
  79. j += 1;
  80. }
  81. }
  82. win32.WaitForMultipleObjects(n, &handles[0], true, win32.INFINITE);
  83. }
  84. for t in threads {
  85. win32.CloseHandle(t.win32_thread);
  86. t.win32_thread = win32.INVALID_HANDLE;
  87. }
  88. }
  89. destroy :: proc(thread: ^Thread) {
  90. join(thread);
  91. free(thread);
  92. }
  93. terminate :: proc(using thread : ^Thread, exit_code: u32) {
  94. win32.TerminateThread(win32_thread, exit_code);
  95. }
  96. yield :: proc() {
  97. win32.SwitchToThread();
  98. }