From 5a7cd3ae1ec09cd824ddb1f2513a0911c4d9ce30 Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 29 Aug 2020 16:59:44 +0300 Subject: [PATCH 1/4] Add all files --- semester4/Lazy/Lazy.Tests/Lazy.Tests.fsproj | 26 +++++++ semester4/Lazy/Lazy.Tests/LazyTests.fs | 75 +++++++++++++++++++++ semester4/Lazy/Lazy.sln | 22 ++++++ semester4/Lazy/Lazy/ILazy.fs | 7 ++ semester4/Lazy/Lazy/Lazy.fs | 17 +++++ semester4/Lazy/Lazy/Lazy.fsproj | 15 +++++ semester4/Lazy/Lazy/LazyFactory.fs | 11 +++ semester4/Lazy/Lazy/LockFreeLazy.fs | 21 ++++++ semester4/Lazy/Lazy/LockMultithreadLazy.fs | 23 +++++++ 9 files changed, 217 insertions(+) create mode 100644 semester4/Lazy/Lazy.Tests/Lazy.Tests.fsproj create mode 100644 semester4/Lazy/Lazy.Tests/LazyTests.fs create mode 100644 semester4/Lazy/Lazy.sln create mode 100644 semester4/Lazy/Lazy/ILazy.fs create mode 100644 semester4/Lazy/Lazy/Lazy.fs create mode 100644 semester4/Lazy/Lazy/Lazy.fsproj create mode 100644 semester4/Lazy/Lazy/LazyFactory.fs create mode 100644 semester4/Lazy/Lazy/LockFreeLazy.fs create mode 100644 semester4/Lazy/Lazy/LockMultithreadLazy.fs diff --git a/semester4/Lazy/Lazy.Tests/Lazy.Tests.fsproj b/semester4/Lazy/Lazy.Tests/Lazy.Tests.fsproj new file mode 100644 index 0000000..b266781 --- /dev/null +++ b/semester4/Lazy/Lazy.Tests/Lazy.Tests.fsproj @@ -0,0 +1,26 @@ + + + + netcoreapp3.1 + + true + true + + + + + + + + + + + + + + + + + + + diff --git a/semester4/Lazy/Lazy.Tests/LazyTests.fs b/semester4/Lazy/Lazy.Tests/LazyTests.fs new file mode 100644 index 0000000..7e7a0df --- /dev/null +++ b/semester4/Lazy/Lazy.Tests/LazyTests.fs @@ -0,0 +1,75 @@ +module Lazy.Tests + +open System.Threading +open NUnit.Framework +open FsUnit +open LazyFactory + +[] +let ``simple int single thread lazy test`` () = + let holder = LazyFactory.CreateSingleThreadLazy <| fun () -> 1 + holder.Get() |> should equal 1 + +[] +let ``simple string single thread lazy test`` () = + let holder = LazyFactory.CreateSingleThreadLazy <| fun () -> "h" + "e" + "l" + "l" + "o" + holder.Get() |> should equal "hello" + +[] +let ``array single thread lazy test`` () = + let holder = + LazyFactory> + .CreateSingleThreadLazy(fun () -> + [ 2; 4; 1; 3; 4; 52; 9; 17; 33; 36 ] + |> List.map (fun x -> x * 1000) + |> List.filter (fun x -> x % 3 = 0)) + + holder.Get() |> should equal [ 3000; 9000; 33000; 36000 ] + +[] +let ``called once single thread test`` () = + let mutable counter = 0 + let holder = LazyFactory.CreateSingleThreadLazy(fun () -> counter <- counter + 1) + for i in 1..100 do + holder.Get() + counter |> should equal 1 + +[] +let ``called once test multi thread with lock`` () = + let mutable counter = ref 0 + let holder = LazyFactory.LockMultiThreadLazy(fun () -> Interlocked.Increment(counter)) + for i in 1..100 do + holder.Get() |> ignore + counter.contents |> should equal 1 + +[] +let ``called once test lock free with lock`` () = + let mutable counter = ref 0 + let holder = LazyFactory.LockFreeLazy(fun () -> Interlocked.Increment(counter)) + for i in 1..100 do + holder.Get() |> ignore + counter.contents |> should equal 1 + +[] +let ``lot of thread multi thread lock test`` () = + let holder = LazyFactory.LockMultiThreadLazy(fun () -> obj()) + let toCompare = holder.Get() + for i in 1..100500 do + holder.Get() |> should equal toCompare + +[] +let ``lot of thread lock free test`` () = + let holder = LazyFactory.LockFreeLazy(fun () -> obj()) + let toCompare = holder.Get() + for i in 1..100500 do + holder.Get() |> should equal toCompare + +[] +let ``simple work lock free test`` () = + let holder = LazyFactory.LockFreeLazy(fun () -> 1) + holder.Get() |> should equal 1 + +[] +let ``simple work multi thread lock test`` () = + let holder = LazyFactory.LockMultiThreadLazy(fun () -> 1) + holder.Get() |> should equal 1 \ No newline at end of file diff --git a/semester4/Lazy/Lazy.sln b/semester4/Lazy/Lazy.sln new file mode 100644 index 0000000..2396cd1 --- /dev/null +++ b/semester4/Lazy/Lazy.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lazy", "Lazy\Lazy.fsproj", "{F2726A51-E1AF-4B6D-8F33-91D669445C02}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Lazy.Tests", "Lazy.Tests\Lazy.Tests.fsproj", "{41A66F38-8DB3-473C-B876-9E7C87CAF01F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F2726A51-E1AF-4B6D-8F33-91D669445C02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F2726A51-E1AF-4B6D-8F33-91D669445C02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F2726A51-E1AF-4B6D-8F33-91D669445C02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F2726A51-E1AF-4B6D-8F33-91D669445C02}.Release|Any CPU.Build.0 = Release|Any CPU + {41A66F38-8DB3-473C-B876-9E7C87CAF01F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41A66F38-8DB3-473C-B876-9E7C87CAF01F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41A66F38-8DB3-473C-B876-9E7C87CAF01F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41A66F38-8DB3-473C-B876-9E7C87CAF01F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/semester4/Lazy/Lazy/ILazy.fs b/semester4/Lazy/Lazy/ILazy.fs new file mode 100644 index 0000000..5b8cec2 --- /dev/null +++ b/semester4/Lazy/Lazy/ILazy.fs @@ -0,0 +1,7 @@ +module ILazy + +/// Interface that describes lazy computations +type ILazy<'a> = + /// If Get() called first time, calculates func and returns result, + /// otherwise, returns already computed function + abstract member Get: unit -> 'a \ No newline at end of file diff --git a/semester4/Lazy/Lazy/Lazy.fs b/semester4/Lazy/Lazy/Lazy.fs new file mode 100644 index 0000000..529469e --- /dev/null +++ b/semester4/Lazy/Lazy/Lazy.fs @@ -0,0 +1,17 @@ +module SingleThreadLazy + +open ILazy + +/// Simple lazy class realization for single thread work +type SingleThreadLazy<'a>(supplier : unit -> 'a) = + let mutable result = None + interface ILazy<'a> with + + /// If Get() called first time, calculates func and returns result, + /// otherwise, returns already computed function + member this.Get() = + match result with + | Some x -> x + | None -> + result <- Some(supplier()) + result.Value \ No newline at end of file diff --git a/semester4/Lazy/Lazy/Lazy.fsproj b/semester4/Lazy/Lazy/Lazy.fsproj new file mode 100644 index 0000000..01caaa0 --- /dev/null +++ b/semester4/Lazy/Lazy/Lazy.fsproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/semester4/Lazy/Lazy/LazyFactory.fs b/semester4/Lazy/Lazy/LazyFactory.fs new file mode 100644 index 0000000..a7b8ced --- /dev/null +++ b/semester4/Lazy/Lazy/LazyFactory.fs @@ -0,0 +1,11 @@ +module LazyFactory + +open ILazy +open SingleThreadLazy +open LockMultiThreadLazy +open LockFreeLazy + +type LazyFactory<'a> = + static member CreateSingleThreadLazy supplier = SingleThreadLazy<'a>(supplier) :> ILazy<'a> + static member LockFreeLazy supplier = LockFreeLazy<'a>(supplier) :> ILazy<'a> + static member LockMultiThreadLazy supplier = LockMultiThreadLazy<'a>(supplier) :> ILazy<'a> \ No newline at end of file diff --git a/semester4/Lazy/Lazy/LockFreeLazy.fs b/semester4/Lazy/Lazy/LockFreeLazy.fs new file mode 100644 index 0000000..71cb777 --- /dev/null +++ b/semester4/Lazy/Lazy/LockFreeLazy.fs @@ -0,0 +1,21 @@ +module LockFreeLazy + +open ILazy +open System.Threading + +/// Simple lazy class realization for multi thread work +/// without locks +type LockFreeLazy<'a>(supplier : unit -> 'a) = + let mutable desiredResult = None + let mutable startResult = None + let mutable computed = false + interface ILazy<'a> with + + /// If Get() called first time, calculates func and returns result, + /// otherwise, returns already computed function + member this.Get() = + if not computed then + desiredResult <- Some(supplier()) + Interlocked.CompareExchange(ref startResult, desiredResult, None) |> ignore + computed <- true + desiredResult.Value \ No newline at end of file diff --git a/semester4/Lazy/Lazy/LockMultithreadLazy.fs b/semester4/Lazy/Lazy/LockMultithreadLazy.fs new file mode 100644 index 0000000..305a0fb --- /dev/null +++ b/semester4/Lazy/Lazy/LockMultithreadLazy.fs @@ -0,0 +1,23 @@ +module LockMultiThreadLazy + +open ILazy + +/// Simple lazy class realization for multi thread work +/// based on lock +type LockMultiThreadLazy<'a>(supplier : unit->'a) = + let mutable result = None + let locker = obj() + interface ILazy<'a> with + /// If Get() called first time, calculates func and returns result, + /// otherwise, returns already computed function + + member this.Get() = + match result with + | Some x -> x + | None -> + lock locker <| fun () -> + match result with + | Some x -> x + | None -> + result <- Some(supplier()) + result.Value \ No newline at end of file From 2d3ee99469ae8c48508783f73891fe5b83c79f2f Mon Sep 17 00:00:00 2001 From: dmitry Date: Sat, 29 Aug 2020 17:05:01 +0300 Subject: [PATCH 2/4] Updated appveyor.yml --- appveyor.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 855f34f..53ce8d1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,17 @@ -image: Visual Studio 2017 +image: Visual Studio 2019 + +init: + - git config --global core.autocrlf true + +environment: + matrix: + - solution: Semester4/Lazy/Lazy.sln before_build: - - nuget restore semester2/6.1/HW6T2.sln + - nuget restore %solution% build: - project: semester2/2.3/2.3.sln + project: $(solution) + +test_script: + - dotnet test %solution% From ab9de4eeaaf63a648008cd95921c06cd4fa9ec3a Mon Sep 17 00:00:00 2001 From: dmitry Date: Fri, 11 Sep 2020 22:32:36 +0300 Subject: [PATCH 3/4] Added more tests --- semester4/Lazy/Lazy.Tests/LazyTests.fs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/semester4/Lazy/Lazy.Tests/LazyTests.fs b/semester4/Lazy/Lazy.Tests/LazyTests.fs index 7e7a0df..fae4601 100644 --- a/semester4/Lazy/Lazy.Tests/LazyTests.fs +++ b/semester4/Lazy/Lazy.Tests/LazyTests.fs @@ -4,6 +4,7 @@ open System.Threading open NUnit.Framework open FsUnit open LazyFactory +open ILazy [] let ``simple int single thread lazy test`` () = @@ -72,4 +73,24 @@ let ``simple work lock free test`` () = [] let ``simple work multi thread lock test`` () = let holder = LazyFactory.LockMultiThreadLazy(fun () -> 1) - holder.Get() |> should equal 1 \ No newline at end of file + holder.Get() |> should equal 1 + +let LazyTestHelper (object : ILazy<'a>, expected) = + let threadWork () = + object.Get() |> should equal expected + + let threads = Array.init 10 (fun _ -> Thread(threadWork)) + + for thread in threads do + thread.Start() + + for thread in threads do + thread.Join() + +[] +let ``multithread lock free lazy`` () = + LazyTestHelper(LazyFactory.LockFreeLazy(fun _ -> "hello" + " " + "world"), "hello world") + +[] +let ``multithread lock lazy`` () = + LazyTestHelper(LazyFactory.LockMultiThreadLazy(fun _ -> "hello" + " " + "world"), "hello world") \ No newline at end of file From efbc2006d3b94440a55602cdcf98cb2fc6fd3cae Mon Sep 17 00:00:00 2001 From: dmitry Date: Fri, 11 Sep 2020 22:54:23 +0300 Subject: [PATCH 4/4] Added comments --- semester4/Lazy/Lazy/LazyFactory.fs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/semester4/Lazy/Lazy/LazyFactory.fs b/semester4/Lazy/Lazy/LazyFactory.fs index a7b8ced..f1e0f45 100644 --- a/semester4/Lazy/Lazy/LazyFactory.fs +++ b/semester4/Lazy/Lazy/LazyFactory.fs @@ -5,7 +5,13 @@ open SingleThreadLazy open LockMultiThreadLazy open LockFreeLazy +/// Creates lazy classes of various types type LazyFactory<'a> = + /// Creates single thread lazy object static member CreateSingleThreadLazy supplier = SingleThreadLazy<'a>(supplier) :> ILazy<'a> + + /// Creates multithread lazy object that works without locks static member LockFreeLazy supplier = LockFreeLazy<'a>(supplier) :> ILazy<'a> + + /// Creates simple multithread lazy based on locks static member LockMultiThreadLazy supplier = LockMultiThreadLazy<'a>(supplier) :> ILazy<'a> \ No newline at end of file