Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support testing.set_fail_timeout on UNIX-likes #3538

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/testing/runner_other.odin
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
//+private
//+build !windows
//+build !linux
//+build !darwin
//+build !freebsd
//+build !openbsd
//+build !haiku
package testing

import "core:time"
Expand Down
83 changes: 83 additions & 0 deletions core/testing/runner_unix.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
//+private
//+build linux, darwin, freebsd, openbsd, haiku
package testing

import "base:runtime"
import "core:sync"
import "core:thread"
import "core:time"

Runner_Thread_Data :: struct {
t: ^T,
it: Internal_Test,
}

Watcher_Thread_Data :: struct {
t: ^T,
duration: time.Duration,
loc: runtime.Source_Code_Location,
}

global_timeout_futex: sync.Futex

run_internal_test :: proc(t: ^T, it: Internal_Test) {
runner := thread.create(proc(runner: ^thread.Thread) {
data := cast(^Runner_Thread_Data)runner.data

// Run the test.
data.it.p(data.t)

// Test completed; tell the main and watcher thread to continue.
sync.futex_broadcast(&global_timeout_futex)
})

// This data can live on the stack, since it will outlive the runner
// thread.
data: Runner_Thread_Data = {t, it}
runner.data = &data

thread.start(runner)

// Wait for either the runner thread to finish or the watcher thread to
// broadcast a failure.
sync.futex_wait(&global_timeout_futex, 0)

if !thread.is_done(runner) {
// The runner thread is still going, which means it's been timed out;
// forcibly terminate it.
thread.terminate(runner, 1)
}

// Destroy the runner thread and join it to this thread.
thread.destroy(runner)
}

_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
watcher := thread.create(proc(watcher: ^thread.Thread) {
data := cast(^Watcher_Thread_Data)watcher.data

// Wait for either a timeout or the runner thread to clear us.
ok := sync.futex_wait_with_timeout(&global_timeout_futex, 0, data.duration)

if !ok {
// If the wait went beyond the duration, we tell the test suite
// that the test failed.
fail_now(data.t, "TIMEOUT", data.loc)
}

// Tell the main thread to resume operation.
sync.futex_broadcast(&global_timeout_futex)

// Cleanup allocations.
free(watcher.data)
thread.destroy(watcher)
})

// Allocate a new struct for this data, because it will live longer than
// the scope of this procedure.
ftd := new(Watcher_Thread_Data)
ftd^ = {t, duration, loc}
watcher.data = ftd

thread.start(watcher)
}