From 6585f7ca3270e30919d8d6949447f720f3a91ea4 Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Wed, 30 Jan 2019 08:31:02 +0100 Subject: [PATCH 1/2] added reference cells with atomic/interlocked semantics --- ExtCore.Tests/Atomic.fs | 264 +++++++++++++++++++++++++++++ ExtCore.Tests/ExtCore.Tests.fsproj | 1 + ExtCore/Atomic.fs | 167 ++++++++++++++++++ ExtCore/ExtCore.fsproj | 123 +++++++------- 4 files changed, 494 insertions(+), 61 deletions(-) create mode 100644 ExtCore.Tests/Atomic.fs create mode 100644 ExtCore/Atomic.fs diff --git a/ExtCore.Tests/Atomic.fs b/ExtCore.Tests/Atomic.fs new file mode 100644 index 0000000..21dc2d7 --- /dev/null +++ b/ExtCore.Tests/Atomic.fs @@ -0,0 +1,264 @@ +(* + +Copyright 2019 Bartosz Sypytkowski + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*) + +namespace Tests.ExtCore + +module Atomic = + + open ExtCore + open ExtCore.Atomic.Operators + open NUnit.Framework + + (* AtomicRef<'a> tests *) + + [] + let ``Atomic ref returns init value`` () : unit = + let atom = Atomic.ref "hello" + !atom |> assertEqual "hello" + + [] + let ``Atomic ref swap returns old value`` () : unit = + let atom = Atomic.ref "hello" + let old = atom := "world" + old |> assertEqual "hello" + !atom |> assertEqual "world" + + [] + let ``Atomic ref cas swaps value is comparand is equal`` () : unit = + let value = "hello" + let atom = Atomic.ref value + let success = atom |> Atomic.cas value "world" + success |> assertTrue + !atom |> assertEqual "world" + + [] + let ``Atomic ref cas doesn't swap value is comparand is not equal`` () : unit = + let atom = Atomic.ref "hello" + let success = atom |> Atomic.cas "hi" "world" + success |> assertFalse + !atom |> assertEqual "hello" + + [] + let ``Atomic ref update replaces old value with modified one`` () : unit = + let atom = Atomic.ref "hello" + let old = atom |> Atomic.update (fun o -> o + o) + old |> assertEqual "hello" + !atom |> assertEqual "hellohello" + + (* AtomicInt tests *) + + [] + let ``Atomic int returns init value`` () : unit = + let atom = Atomic.int 1 + !atom |> assertEqual 1 + + [] + let ``Atomic int swap returns old value`` () : unit = + let atom = Atomic.int 1 + let old = atom := 2 + old |> assertEqual 1 + !atom |> assertEqual 2 + + [] + let ``Atomic int cas swaps value is comparand is equal`` () : unit = + let atom = Atomic.int 1 + let success = atom |> Atomic.cas 1 2 + success |> assertTrue + !atom |> assertEqual 2 + + [] + let ``Atomic int cas doesn't swap value is comparand is not equal`` () : unit = + let atom = Atomic.int 1 + let success = atom |> Atomic.cas 3 2 + success |> assertFalse + !atom |> assertEqual 1 + + [] + let ``Atomic int update replaces old value with modified one`` () : unit = + let atom = Atomic.int 1 + let old = atom |> Atomic.update (fun o -> o + o) + old |> assertEqual 1 + !atom |> assertEqual 2 + + [] + let ``Atomic int inc increments the counter value`` () : unit = + let atom = Atomic.int 2 + let current = Atomic.inc atom + current |> assertEqual 3 + !atom |> assertEqual 3 + + [] + let ``Atomic int dec decrements the counter value`` () : unit = + let atom = Atomic.int 2 + let current = Atomic.dec atom + current |> assertEqual 1 + !atom |> assertEqual 1 + + (* AtomicInt64 tests *) + + [] + let ``Atomic int64 returns init value`` () : unit = + let atom = Atomic.int64 1L + !atom |> assertEqual 1L + + [] + let ``Atomic int64 swap returns old value`` () : unit = + let atom = Atomic.int64 1L + let old = atom := 2L + old |> assertEqual 1L + !atom |> assertEqual 2L + + [] + let ``Atomic int64 cas swaps value is comparand is equal`` () : unit = + let atom = Atomic.int64 1L + let success = atom |> Atomic.cas 1L 2L + success |> assertTrue + !atom |> assertEqual 2L + + [] + let ``Atomic int64 cas doesn't swap value is comparand is not equal`` () : unit = + let atom = Atomic.int64 1L + let success = atom |> Atomic.cas 3L 2L + success |> assertFalse + !atom |> assertEqual 1L + + [] + let ``Atomic int64 update replaces old value with modified one`` () : unit = + let atom = Atomic.int64 1L + let old = atom |> Atomic.update (fun o -> o + o) + old |> assertEqual 1L + !atom |> assertEqual 2L + + [] + let ``Atomic int64 inc increments the counter value`` () : unit = + let atom = Atomic.int64 2L + let current = Atomic.inc atom + current |> assertEqual 3L + !atom |> assertEqual 3L + + [] + let ``Atomic int64 dec decrements the counter value`` () : unit = + let atom = Atomic.int64 2L + let current = Atomic.dec atom + current |> assertEqual 1L + !atom |> assertEqual 1L + + (* AtomicFloat tests *) + + [] + let ``Atomic float returns init value`` () : unit = + let atom = Atomic.float 1.0 + !atom |> assertEqual 1.0 + + [] + let ``Atomic float swap returns old value`` () : unit = + let atom = Atomic.float 1.0 + let old = atom := 2.0 + old |> assertEqual 1.0 + !atom |> assertEqual 2.0 + + [] + let ``Atomic float cas swaps value is comparand is equal`` () : unit = + let atom = Atomic.float 1.0 + let success = atom |> Atomic.cas 1.0 2.0 + success |> assertTrue + !atom |> assertEqual 2.0 + + [] + let ``Atomic float cas doesn't swap value is comparand is not equal`` () : unit = + let atom = Atomic.float 1.0 + let success = atom |> Atomic.cas 3.0 2.0 + success |> assertFalse + !atom |> assertEqual 1.0 + + [] + let ``Atomic float update replaces old value with modified one`` () : unit = + let atom = Atomic.float 1.0 + let old = atom |> Atomic.update (fun o -> o + o) + old |> assertEqual 1.0 + !atom |> assertEqual 2.0 + + (* AtomicFloat32 tests *) + + [] + let ``Atomic float32 returns init value`` () : unit = + let atom = Atomic.float32 1.0f + !atom |> assertEqual 1.0f + + [] + let ``Atomic float32 swap returns old value`` () : unit = + let atom = Atomic.float32 1.0f + let old = atom := 2.0f + old |> assertEqual 1.0f + !atom |> assertEqual 2.0f + + [] + let ``Atomic float32 cas swaps value is comparand is equal`` () : unit = + let atom = Atomic.float32 1.0f + let success = atom |> Atomic.cas 1.0f 2.0f + success |> assertTrue + !atom |> assertEqual 2.0f + + [] + let ``Atomic float32 cas doesn't swap value is comparand is not equal`` () : unit = + let atom = Atomic.float32 1.0f + let success = atom |> Atomic.cas 3.0f 2.0f + success |> assertFalse + !atom |> assertEqual 1.0f + + [] + let ``Atomic float32 update replaces old value with modified one`` () : unit = + let atom = Atomic.float32 1.0f + let old = atom |> Atomic.update (fun o -> o + o) + old |> assertEqual 1.0f + !atom |> assertEqual 2.0f + + (* AtomicBool tests *) + + [] + let ``Atomic bool returns init value`` () : unit = + let atom = Atomic.bool true + !atom |> assertEqual true + + [] + let ``Atomic bool swap returns old value`` () : unit = + let atom = Atomic.bool true + let old = atom := false + old |> assertEqual true + !atom |> assertEqual false + + [] + let ``Atomic bool cas swaps value is comparand is equal`` () : unit = + let atom = Atomic.bool true + let success = atom |> Atomic.cas true false + success |> assertTrue + !atom |> assertEqual false + + [] + let ``Atomic bool cas doesn't swap value is comparand is not equal`` () : unit = + let atom = Atomic.bool true + let success = atom |> Atomic.cas false true + success |> assertFalse + !atom |> assertEqual true + + [] + let ``Atomic bool update replaces old value with modified one`` () : unit = + let atom = Atomic.bool true + let old = atom |> Atomic.update (not) + old |> assertEqual true + !atom |> assertEqual false diff --git a/ExtCore.Tests/ExtCore.Tests.fsproj b/ExtCore.Tests/ExtCore.Tests.fsproj index 0987223..78de6a7 100644 --- a/ExtCore.Tests/ExtCore.Tests.fsproj +++ b/ExtCore.Tests/ExtCore.Tests.fsproj @@ -5,6 +5,7 @@ + diff --git a/ExtCore/Atomic.fs b/ExtCore/Atomic.fs new file mode 100644 index 0000000..de622fa --- /dev/null +++ b/ExtCore/Atomic.fs @@ -0,0 +1,167 @@ +(* + +Copyright 2019 Bartosz Sypytkowski + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*) + +namespace ExtCore + +open System +open System.Threading + +[] +type IAtomic<'a> = + abstract Value: 'a + abstract Swap: 'a -> 'a + abstract CompareAndSwap: 'a * 'a -> bool + +[] +type AtomicBool(initialValue: bool) = + let mutable value: int = if initialValue then 1 else 0 + member inline __.Value = Volatile.Read(&value) = 1 + member inline __.Swap (nval: bool): bool = Interlocked.Exchange(&value, if nval then 1 else 0) = 1 + member inline __.CompareAndSwap (compared: bool, nval: bool): bool = + let v = if compared then 1 else 0 + Interlocked.CompareExchange(&value, (if nval then 1 else 0), v) = v + interface IAtomic with + member atom.Value = atom.Value + member atom.Swap (nval: bool): bool = atom.Swap nval + member atom.CompareAndSwap (compared: bool, nval: bool): bool = atom.CompareAndSwap(nval, compared) + +[] +type AtomicInt(initialValue: int) = + let mutable value: int = initialValue + member inline __.Value = Volatile.Read(&value) + member inline __.Swap (nval: int): int = Interlocked.Exchange(&value, nval) + member inline __.CompareAndSwap (compared: int, nval: int): bool = Interlocked.CompareExchange(&value, nval, compared) = compared + member inline __.Increment() = Interlocked.Increment(&value) + member inline __.Decrement() = Interlocked.Decrement(&value) + interface IAtomic with + member atom.Value = atom.Value + member atom.Swap (nval: int): int = atom.Swap nval + member atom.CompareAndSwap (compared: int, nval: int): bool = atom.CompareAndSwap(nval, compared) + +[] +type AtomicInt64(initialValue: int64) = + let mutable value: int64 = initialValue + member inline __.Value = Volatile.Read(&value) + member inline __.Swap (nval: int64): int64 = Interlocked.Exchange(&value, nval) + member inline __.CompareAndSwap (compared: int64, nval: int64): bool = Interlocked.CompareExchange(&value, nval, compared) = compared + member inline __.Increment() = Interlocked.Increment(&value) + member inline __.Decrement() = Interlocked.Decrement(&value) + interface IAtomic with + member atom.Value = atom.Value + member atom.Swap (nval: int64): int64 = atom.Swap nval + member atom.CompareAndSwap (compared: int64, nval: int64): bool = atom.CompareAndSwap(nval, compared) + +[] +type AtomicFloat(initialValue: float) = + let mutable value: float = initialValue + member inline __.Value = Volatile.Read(&value) + member inline __.Swap (nval: float): float = Interlocked.Exchange(&value, nval) + member inline __.CompareAndSwap (compared: float, nval: float): bool = Interlocked.CompareExchange(&value, nval, compared) = compared + interface IAtomic with + member atom.Value = atom.Value + member atom.Swap (nval: float): float = atom.Swap nval + member atom.CompareAndSwap (compared: float, nval: float): bool = atom.CompareAndSwap(nval, compared) + +[] +type AtomicFloat32(initialValue: float32) = + let mutable value: float32 = initialValue + member inline __.Value = Volatile.Read(&value) + member inline __.Swap (nval: float32): float32 = Interlocked.Exchange(&value, nval) + member inline __.CompareAndSwap (compared: float32, nval: float32): bool = Interlocked.CompareExchange(&value, nval, compared) = compared + interface IAtomic with + member atom.Value = atom.Value + member atom.Swap (nval: float32): float32 = atom.Swap nval + member atom.CompareAndSwap (compared: float32, nval: float32): bool = atom.CompareAndSwap(nval, compared) + +[] +type AtomicRef<'a when 'a: not struct>(initialValue: 'a) = + let mutable value: 'a = initialValue + member inline __.Value = Volatile.Read<'a>(&value) + member inline __.Swap (nval: 'a): 'a = Interlocked.Exchange<'a>(&value, nval) + member inline __.CompareAndSwap (compared: 'a, nval: 'a): bool = Object.ReferenceEquals(Interlocked.CompareExchange<'a>(&value, nval, compared), compared) + member inline atom.Update (fn: 'a -> 'a) = + let rec loop fn = + let old = atom.Value + let nval = fn old + if Object.ReferenceEquals(atom.CompareAndSwap(old, nval), old) + then old + else loop fn + loop fn + interface IAtomic<'a> with + member atom.Value = atom.Value + member atom.Swap (nval: 'a): 'a = atom.Swap nval + member atom.CompareAndSwap (compared: 'a, nval: 'a): bool = atom.CompareAndSwap(nval, compared) + +[] +module Atomic = + + /// Creates an atomic cell reference for a given boolean value. + let inline bool (x: bool): AtomicBool = AtomicBool(x) + + /// Creates an atomic cell reference for a given integer number (32 bits). + let inline int (x: int): AtomicInt = AtomicInt(x) + + /// Creates an atomic cell reference for a given integer number (64 bits). + let inline int64 (x: int64): AtomicInt64 = AtomicInt64(x) + + /// Creates an atomic cell reference for a given floating point number (64 bits). + let inline float (x: float): AtomicFloat = AtomicFloat(x) + + /// Creates an atomic cell reference for a given floating point number (32 bits). + let inline float32 (x: float32): AtomicFloat32 = AtomicFloat32(x) + + /// Creates an atomic cell reference for a given object (instance of reference type). + let inline ref<'a when 'a: not struct> (x: 'a): AtomicRef<'a> = AtomicRef<'a>(x) + + /// Atomically replaces old value stored inside an atom with a new one, + /// but only if previously stored value is (referentially) equal to the + /// expected value. Returns true if managed to successfully replace the + /// stored value, false otherwise. + let inline cas (expected: 'a) (nval: 'a) (atom: #IAtomic<'a>) = + atom.CompareAndSwap(expected, nval) + + /// Atomically tries to update value stored inside an atom, by passing + /// current atom's value to modify function to get new result, which will + /// be stored instead. Returns the last value stored prior to an update. + let update (modify: 'a -> 'a) (atom: #IAtomic<'a>): 'a = + let rec loop (modify: 'a -> 'a) (atom: #IAtomic<'a>) = + let old = atom.Value + let nval = modify old + if cas old nval atom + then old + else loop modify atom + loop modify atom + + /// Atomically increments counter stored internally inside of an atom. + /// Returns an incremented value. + let inline inc (atom: ^a ): ^b when ^a : (member Increment: unit -> ^b) = + ( ^a : (member Increment: unit -> ^b) (atom)) + + /// Atomically decrements counter stored internally inside of an atom. + /// Returns an incremented value. + let inline dec (atom: ^a ): ^b when ^a : (member Decrement: unit -> ^b) = + ( ^a : (member Decrement: unit -> ^b) (atom)) + + module Operators = + + /// Unwraps the value stored inside of an atom. + let inline (!) (atom: #IAtomic<'a>) = atom.Value + + /// Atomically swaps the value stored inside of an atom with provided one. + /// Returns previously stored value. + let inline (:=) (atom: #IAtomic<'a>) (value: 'a) = atom.Swap value diff --git a/ExtCore/ExtCore.fsproj b/ExtCore/ExtCore.fsproj index 73fb972..6f5a578 100644 --- a/ExtCore/ExtCore.fsproj +++ b/ExtCore/ExtCore.fsproj @@ -21,66 +21,67 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8e8cc1ac8eb2d64057a5f336e70b482f0575cad2 Mon Sep 17 00:00:00 2001 From: Bartosz Sypytkowski Date: Wed, 30 Jan 2019 09:41:16 +0100 Subject: [PATCH 2/2] replaced inlining rules to JIT --- ExtCore.Tests/Atomic.fs | 24 ++++---- ExtCore/Atomic.fs | 123 ++++++++++++++++++++++------------------ 2 files changed, 81 insertions(+), 66 deletions(-) diff --git a/ExtCore.Tests/Atomic.fs b/ExtCore.Tests/Atomic.fs index 21dc2d7..52fe433 100644 --- a/ExtCore.Tests/Atomic.fs +++ b/ExtCore.Tests/Atomic.fs @@ -39,7 +39,7 @@ module Atomic = !atom |> assertEqual "world" [] - let ``Atomic ref cas swaps value is comparand is equal`` () : unit = + let ``Atomic ref cas swaps value if comparand is equal`` () : unit = let value = "hello" let atom = Atomic.ref value let success = atom |> Atomic.cas value "world" @@ -47,7 +47,7 @@ module Atomic = !atom |> assertEqual "world" [] - let ``Atomic ref cas doesn't swap value is comparand is not equal`` () : unit = + let ``Atomic ref cas doesn't swap value if comparand is not equal`` () : unit = let atom = Atomic.ref "hello" let success = atom |> Atomic.cas "hi" "world" success |> assertFalse @@ -75,14 +75,14 @@ module Atomic = !atom |> assertEqual 2 [] - let ``Atomic int cas swaps value is comparand is equal`` () : unit = + let ``Atomic int cas swaps value if comparand is equal`` () : unit = let atom = Atomic.int 1 let success = atom |> Atomic.cas 1 2 success |> assertTrue !atom |> assertEqual 2 [] - let ``Atomic int cas doesn't swap value is comparand is not equal`` () : unit = + let ``Atomic int cas doesn't swap value if comparand is not equal`` () : unit = let atom = Atomic.int 1 let success = atom |> Atomic.cas 3 2 success |> assertFalse @@ -124,14 +124,14 @@ module Atomic = !atom |> assertEqual 2L [] - let ``Atomic int64 cas swaps value is comparand is equal`` () : unit = + let ``Atomic int64 cas swaps value if comparand is equal`` () : unit = let atom = Atomic.int64 1L let success = atom |> Atomic.cas 1L 2L success |> assertTrue !atom |> assertEqual 2L [] - let ``Atomic int64 cas doesn't swap value is comparand is not equal`` () : unit = + let ``Atomic int64 cas doesn't swap value if comparand is not equal`` () : unit = let atom = Atomic.int64 1L let success = atom |> Atomic.cas 3L 2L success |> assertFalse @@ -173,14 +173,14 @@ module Atomic = !atom |> assertEqual 2.0 [] - let ``Atomic float cas swaps value is comparand is equal`` () : unit = + let ``Atomic float cas swaps value if comparand is equal`` () : unit = let atom = Atomic.float 1.0 let success = atom |> Atomic.cas 1.0 2.0 success |> assertTrue !atom |> assertEqual 2.0 [] - let ``Atomic float cas doesn't swap value is comparand is not equal`` () : unit = + let ``Atomic float cas doesn't swap value if comparand is not equal`` () : unit = let atom = Atomic.float 1.0 let success = atom |> Atomic.cas 3.0 2.0 success |> assertFalse @@ -208,14 +208,14 @@ module Atomic = !atom |> assertEqual 2.0f [] - let ``Atomic float32 cas swaps value is comparand is equal`` () : unit = + let ``Atomic float32 cas swaps value if comparand is equal`` () : unit = let atom = Atomic.float32 1.0f let success = atom |> Atomic.cas 1.0f 2.0f success |> assertTrue !atom |> assertEqual 2.0f [] - let ``Atomic float32 cas doesn't swap value is comparand is not equal`` () : unit = + let ``Atomic float32 cas doesn't swap value if comparand is not equal`` () : unit = let atom = Atomic.float32 1.0f let success = atom |> Atomic.cas 3.0f 2.0f success |> assertFalse @@ -243,14 +243,14 @@ module Atomic = !atom |> assertEqual false [] - let ``Atomic bool cas swaps value is comparand is equal`` () : unit = + let ``Atomic bool cas swaps value if comparand is equal`` () : unit = let atom = Atomic.bool true let success = atom |> Atomic.cas true false success |> assertTrue !atom |> assertEqual false [] - let ``Atomic bool cas doesn't swap value is comparand is not equal`` () : unit = + let ``Atomic bool cas doesn't swap value if comparand is not equal`` () : unit = let atom = Atomic.bool true let success = atom |> Atomic.cas false true success |> assertFalse diff --git a/ExtCore/Atomic.fs b/ExtCore/Atomic.fs index de622fa..42cd89d 100644 --- a/ExtCore/Atomic.fs +++ b/ExtCore/Atomic.fs @@ -20,92 +20,107 @@ namespace ExtCore open System open System.Threading +open System.Runtime.CompilerServices [] type IAtomic<'a> = - abstract Value: 'a + abstract Value: unit -> 'a abstract Swap: 'a -> 'a abstract CompareAndSwap: 'a * 'a -> bool [] type AtomicBool(initialValue: bool) = let mutable value: int = if initialValue then 1 else 0 - member inline __.Value = Volatile.Read(&value) = 1 - member inline __.Swap (nval: bool): bool = Interlocked.Exchange(&value, if nval then 1 else 0) = 1 - member inline __.CompareAndSwap (compared: bool, nval: bool): bool = - let v = if compared then 1 else 0 - Interlocked.CompareExchange(&value, (if nval then 1 else 0), v) = v interface IAtomic with - member atom.Value = atom.Value - member atom.Swap (nval: bool): bool = atom.Swap nval - member atom.CompareAndSwap (compared: bool, nval: bool): bool = atom.CompareAndSwap(nval, compared) + [] + member __.Value () = Volatile.Read(&value) = 1 + + [] + member __.Swap (nval: bool): bool = Interlocked.Exchange(&value, if nval then 1 else 0) = 1 + + [] + member __.CompareAndSwap (compared: bool, nval: bool): bool = + let v = if compared then 1 else 0 + Interlocked.CompareExchange(&value, (if nval then 1 else 0), v) = v [] type AtomicInt(initialValue: int) = let mutable value: int = initialValue - member inline __.Value = Volatile.Read(&value) - member inline __.Swap (nval: int): int = Interlocked.Exchange(&value, nval) - member inline __.CompareAndSwap (compared: int, nval: int): bool = Interlocked.CompareExchange(&value, nval, compared) = compared - member inline __.Increment() = Interlocked.Increment(&value) - member inline __.Decrement() = Interlocked.Decrement(&value) + + [] + member __.Increment() = Interlocked.Increment(&value) + + [] + member __.Decrement() = Interlocked.Decrement(&value) + interface IAtomic with - member atom.Value = atom.Value - member atom.Swap (nval: int): int = atom.Swap nval - member atom.CompareAndSwap (compared: int, nval: int): bool = atom.CompareAndSwap(nval, compared) + [] + member __.Value () = Volatile.Read(&value) + + [] + member __.Swap (nval: int): int = Interlocked.Exchange(&value, nval) + + [] + member __.CompareAndSwap (compared: int, nval: int): bool = Interlocked.CompareExchange(&value, nval, compared) = compared [] type AtomicInt64(initialValue: int64) = let mutable value: int64 = initialValue - member inline __.Value = Volatile.Read(&value) - member inline __.Swap (nval: int64): int64 = Interlocked.Exchange(&value, nval) - member inline __.CompareAndSwap (compared: int64, nval: int64): bool = Interlocked.CompareExchange(&value, nval, compared) = compared - member inline __.Increment() = Interlocked.Increment(&value) - member inline __.Decrement() = Interlocked.Decrement(&value) + + [] + member __.Increment() = Interlocked.Increment(&value) + + [] + member __.Decrement() = Interlocked.Decrement(&value) + interface IAtomic with - member atom.Value = atom.Value - member atom.Swap (nval: int64): int64 = atom.Swap nval - member atom.CompareAndSwap (compared: int64, nval: int64): bool = atom.CompareAndSwap(nval, compared) + [] + member __.Value () = Volatile.Read(&value) + + [] + member __.Swap (nval: int64): int64 = Interlocked.Exchange(&value, nval) + + [] + member __.CompareAndSwap (compared: int64, nval: int64): bool = Interlocked.CompareExchange(&value, nval, compared) = compared [] type AtomicFloat(initialValue: float) = let mutable value: float = initialValue - member inline __.Value = Volatile.Read(&value) - member inline __.Swap (nval: float): float = Interlocked.Exchange(&value, nval) - member inline __.CompareAndSwap (compared: float, nval: float): bool = Interlocked.CompareExchange(&value, nval, compared) = compared interface IAtomic with - member atom.Value = atom.Value - member atom.Swap (nval: float): float = atom.Swap nval - member atom.CompareAndSwap (compared: float, nval: float): bool = atom.CompareAndSwap(nval, compared) + [] + member __.Value () = Volatile.Read(&value) + + [] + member __.Swap (nval: float): float = Interlocked.Exchange(&value, nval) + + [] + member __.CompareAndSwap (compared: float, nval: float): bool = Interlocked.CompareExchange(&value, nval, compared) = compared [] type AtomicFloat32(initialValue: float32) = let mutable value: float32 = initialValue - member inline __.Value = Volatile.Read(&value) - member inline __.Swap (nval: float32): float32 = Interlocked.Exchange(&value, nval) - member inline __.CompareAndSwap (compared: float32, nval: float32): bool = Interlocked.CompareExchange(&value, nval, compared) = compared interface IAtomic with - member atom.Value = atom.Value - member atom.Swap (nval: float32): float32 = atom.Swap nval - member atom.CompareAndSwap (compared: float32, nval: float32): bool = atom.CompareAndSwap(nval, compared) + [] + member __.Value () = Volatile.Read(&value) + + [] + member __.Swap (nval: float32): float32 = Interlocked.Exchange(&value, nval) + + [] + member __.CompareAndSwap (compared: float32, nval: float32): bool = Interlocked.CompareExchange(&value, nval, compared) = compared [] type AtomicRef<'a when 'a: not struct>(initialValue: 'a) = let mutable value: 'a = initialValue - member inline __.Value = Volatile.Read<'a>(&value) - member inline __.Swap (nval: 'a): 'a = Interlocked.Exchange<'a>(&value, nval) - member inline __.CompareAndSwap (compared: 'a, nval: 'a): bool = Object.ReferenceEquals(Interlocked.CompareExchange<'a>(&value, nval, compared), compared) - member inline atom.Update (fn: 'a -> 'a) = - let rec loop fn = - let old = atom.Value - let nval = fn old - if Object.ReferenceEquals(atom.CompareAndSwap(old, nval), old) - then old - else loop fn - loop fn interface IAtomic<'a> with - member atom.Value = atom.Value - member atom.Swap (nval: 'a): 'a = atom.Swap nval - member atom.CompareAndSwap (compared: 'a, nval: 'a): bool = atom.CompareAndSwap(nval, compared) + [] + member __.Value () = Volatile.Read(&value) + + [] + member __.Swap (nval: 'a): 'a = Interlocked.Exchange<'a>(&value, nval) + + [] + member __.CompareAndSwap (compared: 'a, nval: 'a): bool = Object.ReferenceEquals(Interlocked.CompareExchange<'a>(&value, nval, compared), compared) [] module Atomic = @@ -140,7 +155,7 @@ module Atomic = /// be stored instead. Returns the last value stored prior to an update. let update (modify: 'a -> 'a) (atom: #IAtomic<'a>): 'a = let rec loop (modify: 'a -> 'a) (atom: #IAtomic<'a>) = - let old = atom.Value + let old = atom.Value () let nval = modify old if cas old nval atom then old @@ -160,8 +175,8 @@ module Atomic = module Operators = /// Unwraps the value stored inside of an atom. - let inline (!) (atom: #IAtomic<'a>) = atom.Value + let inline (!) (atom: #IAtomic<'a>): 'a = atom.Value () /// Atomically swaps the value stored inside of an atom with provided one. /// Returns previously stored value. - let inline (:=) (atom: #IAtomic<'a>) (value: 'a) = atom.Swap value + let inline (:=) (atom: #IAtomic<'a>) (value: 'a): 'a = atom.Swap value