Spinlock features #33

Merged
zack merged 2 commits from spinlock-features into master 2026-06-06 02:38:08 +00:00
Showing only changes of commit 7767c0a1f5 - Show all commits
+14 -16
View File
@@ -189,10 +189,10 @@ import "core:sync"
import "core:testing" import "core:testing"
import "core:thread" import "core:thread"
@(test)
test_concurrent_atomic_add_no_lost_updates :: proc(t: ^testing.T) {
// Multiple threads will each add 1.0 this many times. // Multiple threads will each add 1.0 this many times.
// If any updates are lost due to race conditions, the final sum will be wrong. // If any updates are lost due to race conditions, the final sum will be wrong.
@(test)
test_concurrent_atomic_add_no_lost_updates :: proc(t: ^testing.T) {
NUM_THREADS :: 8 NUM_THREADS :: 8
ITERATIONS_PER_THREAD :: 10_000 ITERATIONS_PER_THREAD :: 10_000
@@ -234,10 +234,10 @@ test_concurrent_atomic_add_no_lost_updates :: proc(t: ^testing.T) {
testing.expect_value(t, shared_value, expected) testing.expect_value(t, shared_value, expected)
} }
@(test)
test_concurrent_atomic_sub_no_lost_updates :: proc(t: ^testing.T) {
// Start with a known value, multiple threads subtract. // Start with a known value, multiple threads subtract.
// If any updates are lost due to race conditions, the final result will be wrong. // If any updates are lost due to race conditions, the final result will be wrong.
@(test)
test_concurrent_atomic_sub_no_lost_updates :: proc(t: ^testing.T) {
NUM_THREADS :: 8 NUM_THREADS :: 8
ITERATIONS_PER_THREAD :: 10_000 ITERATIONS_PER_THREAD :: 10_000
@@ -278,11 +278,11 @@ test_concurrent_atomic_sub_no_lost_updates :: proc(t: ^testing.T) {
testing.expect_value(t, shared_value, 0.0) testing.expect_value(t, shared_value, 0.0)
} }
@(test)
test_concurrent_atomic_mul_div_round_trip :: proc(t: ^testing.T) {
// Each thread multiplies by 2.0 then divides by 2.0. // Each thread multiplies by 2.0 then divides by 2.0.
// Since these are inverses, the final value should equal the starting value // Since these are inverses, the final value should equal the starting value
// regardless of how operations interleave. // regardless of how operations interleave.
@(test)
test_concurrent_atomic_mul_div_round_trip :: proc(t: ^testing.T) {
NUM_THREADS :: 8 NUM_THREADS :: 8
ITERATIONS_PER_THREAD :: 10_000 ITERATIONS_PER_THREAD :: 10_000
@@ -324,10 +324,10 @@ test_concurrent_atomic_mul_div_round_trip :: proc(t: ^testing.T) {
testing.expect_value(t, shared_value, 1000.0) testing.expect_value(t, shared_value, 1000.0)
} }
@(test)
test_atomic_add_with_f32 :: proc(t: ^testing.T) {
// Verify the f32 type dispatch works correctly under contention. // Verify the f32 type dispatch works correctly under contention.
// Same approach as the f64 add test but with f32. // Same approach as the f64 add test but with f32.
@(test)
test_atomic_add_with_f32 :: proc(t: ^testing.T) {
NUM_THREADS :: 8 NUM_THREADS :: 8
ITERATIONS_PER_THREAD :: 10_000 ITERATIONS_PER_THREAD :: 10_000
@@ -369,8 +369,6 @@ test_atomic_add_with_f32 :: proc(t: ^testing.T) {
testing.expect_value(t, shared_value, expected) testing.expect_value(t, shared_value, expected)
} }
@(test)
test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
// Tests that the memory order passed to atomic_float_op's CAS success condition // Tests that the memory order passed to atomic_float_op's CAS success condition
// provides full ordering guarantees for the entire float operation. // provides full ordering guarantees for the entire float operation.
// //
@@ -380,6 +378,8 @@ test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
// //
// NOTE: This test may pass even with Relaxed ordering on x86 due to its strong memory model. // NOTE: This test may pass even with Relaxed ordering on x86 due to its strong memory model.
// On ARM or other weak-memory architectures, using Relaxed here would likely cause failures. // On ARM or other weak-memory architectures, using Relaxed here would likely cause failures.
@(test)
test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
NUM_READERS :: 4 NUM_READERS :: 4
Shared_State :: struct { Shared_State :: struct {
@@ -476,8 +476,6 @@ test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
} }
} }
@(test)
test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
// Stress test for every spinlock acquisition variant: N threads contend on a // Stress test for every spinlock acquisition variant: N threads contend on a
// single lock and perform a deliberate non-atomic read-modify-write on shared // single lock and perform a deliberate non-atomic read-modify-write on shared
// data. Each iteration rotates through spinlock_try_lock, spinlock_lock, // data. Each iteration rotates through spinlock_try_lock, spinlock_lock,
@@ -490,6 +488,8 @@ test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
// //
// A multi-step RMW (read → relax → write) widens the critical section so // A multi-step RMW (read → relax → write) widens the critical section so
// any failure to exclude is virtually guaranteed to corrupt the counter. // any failure to exclude is virtually guaranteed to corrupt the counter.
@(test)
test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
NUM_THREADS :: 8 NUM_THREADS :: 8
ITERATIONS_PER_THREAD :: 50_000 ITERATIONS_PER_THREAD :: 50_000
@@ -560,13 +560,11 @@ test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
spinlock_lock(&s.lock) spinlock_lock(&s.lock)
critical_section(s) critical_section(s)
spinlock_unlock(&s.lock) spinlock_unlock(&s.lock)
case 2: case 2: // Scoped guard: unlocks automatically at the end of the block.
// Scoped guard: unlocks automatically at the end of the block.
if spinlock_guard(&s.lock) { if spinlock_guard(&s.lock) {
critical_section(s) critical_section(s)
} }
case 3: case 3: // Scoped try-guard: retry until acquired, auto-unlocks on success.
// Scoped try-guard: retry until acquired, auto-unlocks on success.
for { for {
if spinlock_tryguard(&s.lock) { if spinlock_tryguard(&s.lock) {
critical_section(s) critical_section(s)