diff --git a/F#/Lazy.Test/Lazy.Test.fsproj b/F#/Lazy.Test/Lazy.Test.fsproj new file mode 100644 index 0000000..4b4c995 --- /dev/null +++ b/F#/Lazy.Test/Lazy.Test.fsproj @@ -0,0 +1,29 @@ + + + + net8.0 + + false + true + true + + + + + + + + + + + + + + + + + + + + + diff --git a/F#/Lazy.Test/LazyTest.fs b/F#/Lazy.Test/LazyTest.fs new file mode 100644 index 0000000..a744f1e --- /dev/null +++ b/F#/Lazy.Test/LazyTest.fs @@ -0,0 +1,67 @@ +module Lazy.Test + +open NUnit.Framework +open FsUnit + +type Counter () = + let mutable callCounter = 0 + member this.Increment () = + System.Threading.Interlocked.Increment(&callCounter) + member this.Counter = callCounter + +let lazyImplementations = + [ + (fun func -> S.Lazy func :> ILazy.ILazy) + (fun func -> M.Lazy func) + (fun func -> LF.Lazy func) + ] |> List.map (fun x -> TestCaseData(x)) + +[] +[] +let getTest (genLazy) = + let l : ILazy.ILazy = genLazy (fun () -> 1) + l.Get() |> should equal 1 + +[] +let singleThreadCountTest () = + let counter = Counter() + let l : ILazy.ILazy = S.Lazy(counter.Increment) + l.Get() |> ignore + l.Get() |> ignore + l.Get() |> ignore + counter.Counter |> should equal 1 + +[] +let simpleMultiThrCountTest () = + let counter = Counter() + let l : ILazy.ILazy = M.Lazy(counter.Increment) + l.Get() |> ignore + l.Get() |> ignore + l.Get() |> ignore + counter.Counter |> should equal 1 + +[] +let complexMultiThrCountTest () = + let counter = Counter() + let l : ILazy.ILazy = M.Lazy(counter.Increment) + let getTask = task { return l.Get() } + let results = (fun _ -> getTask |> Async.AwaitTask) |> Seq.initInfinite |> Seq.take 10 |> Async.Parallel |> Async.RunSynchronously + counter.Counter |> should equal 1 + Seq.iter (fun x -> (x = (Seq.last results)) |> should equal true) results + +[] +let simpleLockFreeTest () = + let counter = Counter() + let l : ILazy.ILazy = LF.Lazy(counter.Increment) + l.Get() |> ignore + l.Get() |> ignore + l.Get() |> ignore + counter.Counter |> should equal 1 + +[] +let complexLockFreeTest () = + let counter = Counter() + let l : ILazy.ILazy = LF.Lazy(counter.Increment) + let getTask = task { return l.Get() } + let results = (fun _ -> getTask |> Async.AwaitTask) |> Seq.initInfinite |> Seq.take 10 |> Async.Parallel |> Async.RunSynchronously + Seq.iter (fun x -> (x = (Seq.last results)) |> should equal true) results diff --git a/F#/Lazy/ILazy.fs b/F#/Lazy/ILazy.fs new file mode 100644 index 0000000..ed49d25 --- /dev/null +++ b/F#/Lazy/ILazy.fs @@ -0,0 +1,12 @@ +module Lazy.ILazy + +/// +/// Interface for lazy function calculation +/// +/// Function return value +type ILazy<'a> = + /// + /// Lazily calculates given function + /// + /// Function result + abstract member Get: unit -> 'a diff --git a/F#/Lazy/Lazy.fsproj b/F#/Lazy/Lazy.fsproj new file mode 100644 index 0000000..03d5967 --- /dev/null +++ b/F#/Lazy/Lazy.fsproj @@ -0,0 +1,15 @@ + + + + net8.0 + true + + + + + + + + + + diff --git a/F#/Lazy/LockFreeLazy.fs b/F#/Lazy/LockFreeLazy.fs new file mode 100644 index 0000000..821e2c6 --- /dev/null +++ b/F#/Lazy/LockFreeLazy.fs @@ -0,0 +1,14 @@ +module Lazy.LF + +/// +/// Lock free multi threaded implementation of iLazy interface +/// +/// Result type +type Lazy<'a>(func) = + let mutable value : Option<'a> = None + interface ILazy.ILazy<'a> with + member this.Get() = + if value.IsNone then + let localValue = func() + System.Threading.Interlocked.CompareExchange(&value, Some(localValue), None) |> ignore + value.Value diff --git a/F#/Lazy/MultiThreadedLazy.fs b/F#/Lazy/MultiThreadedLazy.fs new file mode 100644 index 0000000..5065ccf --- /dev/null +++ b/F#/Lazy/MultiThreadedLazy.fs @@ -0,0 +1,17 @@ +module Lazy.M + +/// +/// Multi threaded implementation of iLazy interface +/// +/// Result type +type Lazy<'a>(func) = + let mutable value : Option<'a> = None + let valueLock = obj + interface ILazy.ILazy<'a> with + member this.Get() = + if value.IsNone then + lock valueLock (fun () -> + if value.IsNone then + value <- Some(func()) + ) + value.Value diff --git a/F#/Lazy/SingleThreadedLazy.fs b/F#/Lazy/SingleThreadedLazy.fs new file mode 100644 index 0000000..2cc1f15 --- /dev/null +++ b/F#/Lazy/SingleThreadedLazy.fs @@ -0,0 +1,13 @@ +module Lazy.S + +/// +/// Single threaded implementation of iLazy interface +/// +/// Result type +type Lazy<'a>(f) = + let mutable value : Option<'a> = None + interface ILazy.ILazy<'a> with + member this.Get() = + if value.IsNone then + value <- Some(f()) + value.Value diff --git a/F#/forSpbu.sln b/F#/forSpbu.sln index 9d54d03..b38709a 100644 --- a/F#/forSpbu.sln +++ b/F#/forSpbu.sln @@ -21,6 +21,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "07.03", "07.03", "{5CA9053B EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Homework1", "Homework1\Homework1.fsproj", "{04B15EE4-079A-42ED-ACC8-E2DCD25281C6}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lazy", "Lazy\Lazy.fsproj", "{31761996-9105-4FCD-A2BE-87F64952A72A}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lazy.Test", "Lazy.Test\Lazy.Test.fsproj", "{F290B1F9-5753-4585-881D-25641368A803}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,6 +62,14 @@ Global {89A935E8-B5F3-435D-ACC3-A99DD7C66178}.Debug|Any CPU.Build.0 = Debug|Any CPU {89A935E8-B5F3-435D-ACC3-A99DD7C66178}.Release|Any CPU.ActiveCfg = Release|Any CPU {89A935E8-B5F3-435D-ACC3-A99DD7C66178}.Release|Any CPU.Build.0 = Release|Any CPU + {31761996-9105-4FCD-A2BE-87F64952A72A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31761996-9105-4FCD-A2BE-87F64952A72A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31761996-9105-4FCD-A2BE-87F64952A72A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31761996-9105-4FCD-A2BE-87F64952A72A}.Release|Any CPU.Build.0 = Release|Any CPU + {F290B1F9-5753-4585-881D-25641368A803}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F290B1F9-5753-4585-881D-25641368A803}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F290B1F9-5753-4585-881D-25641368A803}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F290B1F9-5753-4585-881D-25641368A803}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {7937CDA8-8285-4E23-AD1A-FC0F04FEEFE6} = {91E3BDA2-0836-46C2-95F0-02513FD7F13F}