فهرست منبع

Merge pull request #3831 from Feoramund/fix-darwin-test-runner-cancel

Let Darwin safely panic in a test
gingerBill 1 سال پیش
والد
کامیت
9113f389d8
4فایلهای تغییر یافته به همراه29 افزوده شده و 28 حذف شده
  1. 1 14
      core/c/libc/signal.odin
  2. 1 0
      core/testing/signal_handler.odin
  3. 10 0
      core/testing/signal_handler_libc.odin
  4. 17 14
      core/thread/thread_unix.odin

+ 1 - 14
core/c/libc/signal.odin

@@ -34,20 +34,7 @@ when ODIN_OS == .Windows {
 	SIGTERM :: 15
 }
 
-when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
-	SIG_ERR  :: rawptr(~uintptr(0))
-	SIG_DFL  :: rawptr(uintptr(0))
-	SIG_IGN  :: rawptr(uintptr(1)) 
-
-	SIGABRT  :: 6
-	SIGFPE   :: 8
-	SIGILL   :: 4
-	SIGINT   :: 2
-	SIGSEGV  :: 11
-	SIGTERM  :: 15
-}
-
-when ODIN_OS == .Darwin {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
 	SIG_ERR  :: rawptr(~uintptr(0))
 	SIG_DFL  :: rawptr(uintptr(0))
 	SIG_IGN  :: rawptr(uintptr(1)) 

+ 1 - 0
core/testing/signal_handler.odin

@@ -9,6 +9,7 @@ Stop_Reason :: enum {
 	Illegal_Instruction,
 	Arithmetic_Error,
 	Segmentation_Fault,
+	Unhandled_Trap,
 }
 
 test_assertion_failure_proc :: proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {

+ 10 - 0
core/testing/signal_handler_libc.odin

@@ -19,6 +19,11 @@ import "core:os"
 @(private="file", thread_local)
 local_test_index: libc.sig_atomic_t
 
+// Windows does not appear to have a SIGTRAP, so this is defined here, instead
+// of in the libc package, just so there's no confusion about it being
+// available there.
+SIGTRAP :: 5
+
 @(private="file")
 stop_runner_callback :: proc "c" (sig: libc.int) {
 	prev := intrinsics.atomic_add(&stop_runner_flag, 1)
@@ -110,6 +115,10 @@ _setup_signal_handler :: proc() {
 	// For tests:
 	// Catch asserts and panics.
 	libc.signal(libc.SIGILL, stop_test_callback)
+	when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
+		// Catch panics on Darwin and unhandled calls to `debug_trap`.
+		libc.signal(SIGTRAP, stop_test_callback)
+	}
 	// Catch arithmetic errors.
 	libc.signal(libc.SIGFPE, stop_test_callback)
 	// Catch segmentation faults (illegal memory access).
@@ -141,6 +150,7 @@ _should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool)
 		case libc.SIGFPE: reason = .Arithmetic_Error
 		case libc.SIGILL: reason = .Illegal_Instruction
 		case libc.SIGSEGV: reason = .Segmentation_Fault
+		case      SIGTRAP: reason = .Unhandled_Trap
 		}
 		ok = true
 	}

+ 17 - 14
core/thread/thread_unix.odin

@@ -23,10 +23,8 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
 	__unix_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
 		t := (^Thread)(t)
 
-		when ODIN_OS != .Darwin {
-			// We need to give the thread a moment to start up before we enable cancellation.
-			can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) == 0
-		}
+		// We need to give the thread a moment to start up before we enable cancellation.
+		can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) == 0
 
 		sync.lock(&t.mutex)
 
@@ -42,12 +40,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
 			return nil
 		}
 
-		when ODIN_OS != .Darwin {
-			// Enable thread's cancelability.
-			if can_set_thread_cancel_state {
-				unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
-				unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE,       nil)
-			}
+		// Enable thread's cancelability.
+		if can_set_thread_cancel_state {
+			unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
+			unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE,       nil)
 		}
 
 		{
@@ -169,10 +165,17 @@ _destroy :: proc(t: ^Thread) {
 }
 
 _terminate :: proc(t: ^Thread, exit_code: int) {
-	// `pthread_cancel` is unreliable on Darwin for unknown reasons.
-	when ODIN_OS != .Darwin {
-		unix.pthread_cancel(t.unix_thread)
-	}
+	// NOTE(Feoramund): For thread cancellation to succeed on BSDs and
+	// possibly Darwin systems, the thread must call one of the pthread
+	// cancelation points at some point after this.
+	//
+	// The most obvious one of these is `pthread_cancel`, but there is an
+	// entire list of functions that act as cancelation points available in the
+	// pthreads manual page.
+	//
+	// This is in contrast to behavior I have seen on Linux where the thread is
+	// just terminated.
+	unix.pthread_cancel(t.unix_thread)
 }
 
 _yield :: proc() {