runner_windows.odin 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. //+private
  2. //+build windows
  3. package testing
  4. import win32 "core:sys/windows"
  5. import "core:runtime"
  6. import "core:intrinsics"
  7. Sema :: struct {
  8. count: i32,
  9. }
  10. sema_reset :: proc "contextless" (s: ^Sema) {
  11. intrinsics.atomic_store(&s.count, 0);
  12. }
  13. sema_wait :: proc "contextless" (s: ^Sema) {
  14. for {
  15. original_count := s.count;
  16. for original_count == 0 {
  17. win32.WaitOnAddress(
  18. &s.count,
  19. &original_count,
  20. size_of(original_count),
  21. win32.INFINITE,
  22. );
  23. original_count = s.count;
  24. }
  25. if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
  26. return;
  27. }
  28. }
  29. }
  30. sema_post :: proc "contextless" (s: ^Sema, count := 1) {
  31. intrinsics.atomic_add(&s.count, i32(count));
  32. if count == 1 {
  33. win32.WakeByAddressSingle(&s.count);
  34. } else {
  35. win32.WakeByAddressAll(&s.count);
  36. }
  37. }
  38. Thread_Proc :: #type proc(^Thread);
  39. MAX_USER_ARGUMENTS :: 8;
  40. Thread :: struct {
  41. using specific: Thread_Os_Specific,
  42. procedure: Thread_Proc,
  43. t: ^T,
  44. it: Internal_Test,
  45. success: bool,
  46. init_context: Maybe(runtime.Context),
  47. creation_allocator: runtime.Allocator,
  48. }
  49. Thread_Os_Specific :: struct {
  50. win32_thread: win32.HANDLE,
  51. win32_thread_id: win32.DWORD,
  52. done: bool, // see note in `is_done`
  53. }
  54. thread_create :: proc(procedure: Thread_Proc) -> ^Thread {
  55. __windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
  56. t := (^Thread)(t_);
  57. context = t.init_context.? or_else runtime.default_context();
  58. t.procedure(t);
  59. if t.init_context == nil {
  60. if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
  61. runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data);
  62. }
  63. }
  64. intrinsics.atomic_store(&t.done, true);
  65. return 0;
  66. }
  67. thread := new(Thread);
  68. if thread == nil {
  69. return nil;
  70. }
  71. thread.creation_allocator = context.allocator;
  72. win32_thread_id: win32.DWORD;
  73. win32_thread := win32.CreateThread(nil, 0, __windows_thread_entry_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
  74. if win32_thread == nil {
  75. free(thread, thread.creation_allocator);
  76. return nil;
  77. }
  78. thread.procedure = procedure;
  79. thread.win32_thread = win32_thread;
  80. thread.win32_thread_id = win32_thread_id;
  81. thread.init_context = context;
  82. return thread;
  83. }
  84. thread_start :: proc "contextless" (thread: ^Thread) {
  85. win32.ResumeThread(thread.win32_thread);
  86. }
  87. thread_join_and_destroy :: proc(thread: ^Thread) {
  88. if thread.win32_thread != win32.INVALID_HANDLE {
  89. win32.WaitForSingleObject(thread.win32_thread, win32.INFINITE);
  90. win32.CloseHandle(thread.win32_thread);
  91. thread.win32_thread = win32.INVALID_HANDLE;
  92. }
  93. free(thread, thread.creation_allocator);
  94. }
  95. thread_terminate :: proc "contextless" (thread: ^Thread, exit_code: int) {
  96. win32.TerminateThread(thread.win32_thread, u32(exit_code));
  97. }
  98. global_threaded_runner_semaphore: Sema;
  99. global_exception_handler: rawptr;
  100. global_current_thread: ^Thread;
  101. global_current_t: ^T;
  102. run_internal_test :: proc(t: ^T, it: Internal_Test) {
  103. thread := thread_create(proc(thread: ^Thread) {
  104. exception_handler_proc :: proc "stdcall" (ExceptionInfo: ^win32.EXCEPTION_POINTERS) -> win32.LONG {
  105. switch ExceptionInfo.ExceptionRecord.ExceptionCode {
  106. case
  107. win32.EXCEPTION_DATATYPE_MISALIGNMENT,
  108. win32.EXCEPTION_BREAKPOINT,
  109. win32.EXCEPTION_ACCESS_VIOLATION,
  110. win32.EXCEPTION_ILLEGAL_INSTRUCTION,
  111. win32.EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
  112. win32.EXCEPTION_STACK_OVERFLOW:
  113. sema_post(&global_threaded_runner_semaphore);
  114. return win32.EXCEPTION_EXECUTE_HANDLER;
  115. }
  116. return win32.EXCEPTION_CONTINUE_SEARCH;
  117. }
  118. global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc);
  119. context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
  120. errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc);
  121. intrinsics.trap();
  122. };
  123. thread.it.p(thread.t);
  124. thread.success = true;
  125. sema_post(&global_threaded_runner_semaphore);
  126. });
  127. sema_reset(&global_threaded_runner_semaphore);
  128. global_current_t = t;
  129. t._fail_now = proc() -> ! {
  130. intrinsics.trap();
  131. };
  132. thread.t = t;
  133. thread.it = it;
  134. thread.success = false;
  135. thread_start(thread);
  136. sema_wait(&global_threaded_runner_semaphore);
  137. thread_terminate(thread, int(!thread.success));
  138. thread_join_and_destroy(thread);
  139. win32.RemoveVectoredExceptionHandler(global_exception_handler);
  140. if !thread.success && t.error_count == 0 {
  141. t.error_count += 1;
  142. }
  143. return;
  144. }