Skip to content
Open

Lazy #71

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions F#/Lazy.Test/Lazy.Test.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>

<IsPackable>false</IsPackable>
<GenerateProgramFile>true</GenerateProgramFile>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<Compile Include="LazyTest.fs" />

</ItemGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="FsUnit" Version="6.0.0" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lazy\Lazy.fsproj" />
</ItemGroup>

</Project>
67 changes: 67 additions & 0 deletions F#/Lazy.Test/LazyTest.fs
Original file line number Diff line number Diff line change
@@ -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<int> func :> ILazy.ILazy<int>)
(fun func -> M.Lazy func)
(fun func -> LF.Lazy func)
] |> List.map (fun x -> TestCaseData(x))

[<Test>]
[<TestCaseSource("lazyImplementations")>]
let getTest (genLazy) =
let l : ILazy.ILazy<int> = genLazy (fun () -> 1)
l.Get() |> should equal 1

[<Test>]
let singleThreadCountTest () =
let counter = Counter()
let l : ILazy.ILazy<int> = S.Lazy<int>(counter.Increment)
l.Get() |> ignore
l.Get() |> ignore
l.Get() |> ignore
counter.Counter |> should equal 1

[<Test>]
let simpleMultiThrCountTest () =
let counter = Counter()
let l : ILazy.ILazy<int> = M.Lazy<int>(counter.Increment)
l.Get() |> ignore
l.Get() |> ignore
l.Get() |> ignore
counter.Counter |> should equal 1

[<Test>]
let complexMultiThrCountTest () =
let counter = Counter()
let l : ILazy.ILazy<int> = M.Lazy<int>(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

[<Test>]
let simpleLockFreeTest () =
let counter = Counter()
let l : ILazy.ILazy<int> = LF.Lazy<int>(counter.Increment)
l.Get() |> ignore
l.Get() |> ignore
l.Get() |> ignore
counter.Counter |> should equal 1

[<Test>]
let complexLockFreeTest () =
let counter = Counter()
let l : ILazy.ILazy<int> = LF.Lazy<int>(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
12 changes: 12 additions & 0 deletions F#/Lazy/ILazy.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Lazy.ILazy

/// <summary>
/// Interface for lazy function calculation
/// </summary>
/// <typeparam name="'a">Function return value</typeparam>
type ILazy<'a> =
/// <summary>
/// Lazily calculates given function
/// </summary>
/// <returns>Function result</returns>
abstract member Get: unit -> 'a
15 changes: 15 additions & 0 deletions F#/Lazy/Lazy.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="ILazy.fs" />
<Compile Include="SingleThreadedLazy.fs" />
<Compile Include="MultiThreadedLazy.fs" />
<Compile Include="LockFreeLazy.fs" />
</ItemGroup>

</Project>
14 changes: 14 additions & 0 deletions F#/Lazy/LockFreeLazy.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Lazy.LF

/// <summary>
/// Lock free multi threaded implementation of iLazy interface
/// </summary>
/// <typeparam name="'a">Result type</typeparam>
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
17 changes: 17 additions & 0 deletions F#/Lazy/MultiThreadedLazy.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Lazy.M

/// <summary>
/// Multi threaded implementation of iLazy interface
/// </summary>
/// <typeparam name="'a">Result type</typeparam>
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
13 changes: 13 additions & 0 deletions F#/Lazy/SingleThreadedLazy.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Lazy.S

/// <summary>
/// Single threaded implementation of iLazy interface
/// </summary>
/// <typeparam name="'a">Result type</typeparam>
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
12 changes: 12 additions & 0 deletions F#/forSpbu.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down