From 4ac1218bf895a9f933f476bd3c36fe24dc7b9a88 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 17 Oct 2018 21:43:05 +0100 Subject: [PATCH] sync atomics "wrapper" procedures --- core/atomics/atomics_windows_amd64.odin | 101 ------------- core/sync/atomic.odin | 185 ++++++++++++++++++++++++ core/sync/sync_windows.odin | 1 - examples/demo/demo.odin | 1 - 4 files changed, 185 insertions(+), 103 deletions(-) delete mode 100644 core/atomics/atomics_windows_amd64.odin create mode 100644 core/sync/atomic.odin diff --git a/core/atomics/atomics_windows_amd64.odin b/core/atomics/atomics_windows_amd64.odin deleted file mode 100644 index d1f090419..000000000 --- a/core/atomics/atomics_windows_amd64.odin +++ /dev/null @@ -1,101 +0,0 @@ -package atomics - -// TODO(bill): Use assembly instead here to implement atomics -// Inline vs external file? - -import "core:sys/win32" - - -yield_thread :: proc() { win32.mm_pause(); } -mfence :: proc() { win32.read_write_barrier(); } -sfence :: proc() { win32.write_barrier(); } -lfence :: proc() { win32.read_barrier(); } - - -load_i32 :: proc(a: ^i32) -> i32 { - return a^; -} -store_i32 :: proc(a: ^i32, value: i32) { - a^ = value; -} -compare_exchange_i32 :: proc(a: ^i32, expected, desired: i32) -> i32 { - return win32.interlocked_compare_exchange(a, desired, expected); -} -exchanged_i32 :: proc(a: ^i32, desired: i32) -> i32 { - return win32.interlocked_exchange(a, desired); -} -fetch_add_i32 :: proc(a: ^i32, operand: i32) -> i32 { - return win32.interlocked_exchange_add(a, operand); - -} -fetch_and_i32 :: proc(a: ^i32, operand: i32) -> i32 { - return win32.interlocked_and(a, operand); -} -fetch_or_i32 :: proc(a: ^i32, operand: i32) -> i32 { - return win32.interlocked_or(a, operand); -} -spin_lock_i32 :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default - old_value := compare_exchange_i32(a, 1, 0); - counter := 0; - for old_value != 0 && (time_out < 0 || counter < time_out) { - counter += 1; - yield_thread(); - old_value = compare_exchange_i32(a, 1, 0); - mfence(); - } - return old_value == 0; -} -spin_unlock_i32 :: proc(a: ^i32) { - store_i32(a, 0); - mfence(); -} -try_acquire_lock_i32 :: proc(a: ^i32) -> bool { - yield_thread(); - old_value := compare_exchange_i32(a, 1, 0); - mfence(); - return old_value == 0; -} - - -load_i64 :: proc(a: ^i64) -> i64 { - return a^; -} -store_i64 :: proc(a: ^i64, value: i64) { - a^ = value; -} -compare_exchange_i64 :: proc(a: ^i64, expected, desired: i64) -> i64 { - return win32.interlocked_compare_exchange64(a, desired, expected); -} -exchanged_i64 :: proc(a: ^i64, desired: i64) -> i64 { - return win32.interlocked_exchange64(a, desired); -} -fetch_add_i64 :: proc(a: ^i64, operand: i64) -> i64 { - return win32.interlocked_exchange_add64(a, operand); -} -fetch_and_i64 :: proc(a: ^i64, operand: i64) -> i64 { - return win32.interlocked_and64(a, operand); -} -fetch_or_i64 :: proc(a: ^i64, operand: i64) -> i64 { - return win32.interlocked_or64(a, operand); -} -spin_lock_i64 :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default - old_value := compare_exchange_i64(a, 1, 0); - counter := 0; - for old_value != 0 && (time_out < 0 || counter < time_out) { - counter += 1; - yield_thread(); - old_value = compare_exchange_i64(a, 1, 0); - mfence(); - } - return old_value == 0; -} -spin_unlock_i64 :: proc(a: ^i64) { - store_i64(a, 0); - mfence(); -} -try_acquire_lock_i64 :: proc(a: ^i64) -> bool { - yield_thread(); - old_value := compare_exchange_i64(a, 1, 0); - mfence(); - return old_value == 0; -} diff --git a/core/sync/atomic.odin b/core/sync/atomic.odin new file mode 100644 index 000000000..abb3e5dc9 --- /dev/null +++ b/core/sync/atomic.odin @@ -0,0 +1,185 @@ +package sync + +Ordering :: enum { + Relaxed, // Monotonic + Release, + Acquire, + Acquire_Release, + Sequentially_Consistent, +} + +strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering { + using Ordering; + #complete switch order { + case Relaxed: return Relaxed; + case Release: return Relaxed; + case Acquire: return Acquire; + case Acquire_Release: return Acquire; + case Sequentially_Consistent: return Sequentially_Consistent; + } + return Relaxed; +} + +fence :: proc "contextless" (order: Ordering) { + using Ordering; + #complete switch order { + case Relaxed: panic("there is no such thing as a relaxed fence"); + case Release: __atomic_fence_rel(); + case Acquire: __atomic_fence_acq(); + case Acquire_Release: __atomic_fence_acqrel(); + case Sequentially_Consistent: __atomic_fence(); + case: panic("unknown order"); + } +} + + +atomic_store :: proc "contextless" (dst: ^$T, val: T, order: Ordering) { + using Ordering; + #complete switch order { + case Relaxed: __atomic_store_relaxed(dst, val); + case Release: __atomic_store_rel(dst, val); + case Sequentially_Consistent: __atomic_store(dst, val); + case Acquire: panic("there is not such thing as an acquire store"); + case Acquire_Release: panic("there is not such thing as an acquire/release store"); + case: panic("unknown order"); + } +} + +atomic_load :: proc "contextless" (dst: ^$T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_load_relaxed(dst); + case Acquire: return __atomic_load_acq(dst); + case Sequentially_Consistent: return __atomic_load(dst); + case Release: panic("there is no such thing as a release load"); + case Acquire_Release: panic("there is no such thing as an acquire/release load"); + } + panic("unknown order"); + return T{}; +} + +atomic_swap :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_xchg_relaxed(dst, val); + case Release: return __atomic_xchg_rel(dst, val); + case Acquire: return __atomic_xchg_acq(dst, val); + case Acquire_Release: return __atomic_xchg_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_xchg(dst, val); + } + panic("unknown order"); + return T{}; +} + +atomic_compare_exchange :: proc "contextless" (dst: ^$T, old, new: T, success, failure: Ordering) -> (val: T, ok: bool) { + using Ordering; + switch failure { + case Relaxed: + switch success { + case Release: return __atomic_cxchg_rel_failrelaxed(dst, old, new); + case Relaxed: return __atomic_cxchg_relaxed(dst, old, new); + case Acquire: return __atomic_cxchg_acq_failrelaxed(dst, old, new); + case Acquire_Release: return __atomic_cxchg_acqrel_failrelaxed(dst, old, new); + case Sequentially_Consistent: return __atomic_cxchg_failrelaxed(dst, old, new); + case: panic("an unknown ordering combination"); + } + case Acquire: + switch success { + case Acquire: return __atomic_cxchg_acq(dst, old, new); + case Acquire_Release: return __atomic_cxchg_acqrel_failacq(dst, old, new); + case Sequentially_Consistent: return __atomic_acqrel_failacq(dst, old, new); + case: panic("an unknown ordering combination"); + } + case Sequentially_Consistent: + switch success { + case Sequentially_Consistent: return __atomic_cxchg(dst, old, new); + case: panic("an unknown ordering combination"); + } + case Acquire_Release: + panic("there is not such thing as an acquire/release failure ordering"); + case Release: + panic("there is not such thing as an release failure ordering"); + } + + return T{}, false; +} + + +atomic_add :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_add_relaxed(dst, val); + case Release: return __atomic_add_rel(dst, val); + case Acquire: return __atomic_add_acq(dst, val); + case Acquire_Release: return __atomic_add_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_add(dst, val); + } + panic("unknown order"); + return T{}; +} + +atomic_sub :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_sub_relaxed(dst, val); + case Release: return __atomic_sub_rel(dst, val); + case Acquire: return __atomic_sub_acq(dst, val); + case Acquire_Release: return __atomic_sub_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_sub(dst, val); + } + panic("unknown order"); + return T{}; +} + +atomic_and :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_and_relaxed(dst, val); + case Release: return __atomic_and_rel(dst, val); + case Acquire: return __atomic_and_acq(dst, val); + case Acquire_Release: return __atomic_and_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_and(dst, val); + } + panic("unknown order"); + return T{}; +} + +atomic_nand :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_nand_relaxed(dst, val); + case Release: return __atomic_nand_rel(dst, val); + case Acquire: return __atomic_nand_acq(dst, val); + case Acquire_Release: return __atomic_nand_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_nand(dst, val); + } + panic("unknown order"); + return T{}; +} + +atomic_or :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_or_relaxed(dst, val); + case Release: return __atomic_or_rel(dst, val); + case Acquire: return __atomic_or_acq(dst, val); + case Acquire_Release: return __atomic_or_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_or(dst, val); + } + panic("unknown order"); + return T{}; +} + +atomic_xor :: proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T { + using Ordering; + #complete switch order { + case Relaxed: return __atomic_xor_relaxed(dst, val); + case Release: return __atomic_xor_rel(dst, val); + case Acquire: return __atomic_xor_acq(dst, val); + case Acquire_Release: return __atomic_xor_acqrel(dst, val); + case Sequentially_Consistent: return __atomic_xor(dst, val); + } + panic("unknown order"); + return T{}; +} + diff --git a/core/sync/sync_windows.odin b/core/sync/sync_windows.odin index d50d9046e..3854f6807 100644 --- a/core/sync/sync_windows.odin +++ b/core/sync/sync_windows.odin @@ -1,7 +1,6 @@ package sync import "core:sys/win32" -import "core:atomics" Semaphore :: struct { _handle: win32.Handle, diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 55ca5feb6..30c055398 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -17,7 +17,6 @@ import "core:c" import "core:runtime" when os.OS == "windows" { - import "core:atomics" import "core:sync" import "core:thread" import "core:sys/win32"