Skip to content
Open

Lazy #74

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
16 changes: 13 additions & 3 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -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%
26 changes: 26 additions & 0 deletions semester4/Lazy/Lazy.Tests/Lazy.Tests.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<IsPackable>true</IsPackable>
<GenerateProgramFile>true</GenerateProgramFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FsCheck" Version="3.0.0-alpha4" />
<PackageReference Include="FsUnit" Version="4.0.1" />
<PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
</ItemGroup>

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

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

</Project>
96 changes: 96 additions & 0 deletions semester4/Lazy/Lazy.Tests/LazyTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
module Lazy.Tests

open System.Threading
open NUnit.Framework
open FsUnit
open LazyFactory
open ILazy

[<Test>]
let ``simple int single thread lazy test`` () =
let holder = LazyFactory<int>.CreateSingleThreadLazy <| fun () -> 1
holder.Get() |> should equal 1

[<Test>]
let ``simple string single thread lazy test`` () =
let holder = LazyFactory<string>.CreateSingleThreadLazy <| fun () -> "h" + "e" + "l" + "l" + "o"
holder.Get() |> should equal "hello"

[<Test>]
let ``array single thread lazy test`` () =
let holder =
LazyFactory<List<int>>
.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 ]

[<Test>]
let ``called once single thread test`` () =
let mutable counter = 0
let holder = LazyFactory<unit>.CreateSingleThreadLazy(fun () -> counter <- counter + 1)
for i in 1..100 do
holder.Get()
counter |> should equal 1

[<Test>]
let ``called once test multi thread with lock`` () =
let mutable counter = ref 0
let holder = LazyFactory<int>.LockMultiThreadLazy(fun () -> Interlocked.Increment(counter))
for i in 1..100 do
holder.Get() |> ignore
counter.contents |> should equal 1

[<Test>]
let ``called once test lock free with lock`` () =
let mutable counter = ref 0
let holder = LazyFactory<int>.LockFreeLazy(fun () -> Interlocked.Increment(counter))
for i in 1..100 do
holder.Get() |> ignore
counter.contents |> should equal 1

[<Test>]
let ``lot of thread multi thread lock test`` () =
let holder = LazyFactory<obj>.LockMultiThreadLazy(fun () -> obj())
let toCompare = holder.Get()
for i in 1..100500 do
holder.Get() |> should equal toCompare

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так это ж не многопоточно. Тесты пока нигде не проверяют, что если из нескольких потоков подёргать Lazy, всё хорошо будет. Тем более что самое интересное в многопоточном случае --- это гонки при первом Get, а тут он делается даже до цикла, так что если бы цикл был параллельным, это бы тоже было непоказательно


[<Test>]
let ``lot of thread lock free test`` () =
let holder = LazyFactory<obj>.LockFreeLazy(fun () -> obj())
let toCompare = holder.Get()
for i in 1..100500 do
holder.Get() |> should equal toCompare

[<Test>]
let ``simple work lock free test`` () =
let holder = LazyFactory<int>.LockFreeLazy(fun () -> 1)
holder.Get() |> should equal 1

[<Test>]
let ``simple work multi thread lock test`` () =
let holder = LazyFactory<int>.LockMultiThreadLazy(fun () -> 1)
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()

[<Test>]
let ``multithread lock free lazy`` () =
LazyTestHelper(LazyFactory<string>.LockFreeLazy(fun _ -> "hello" + " " + "world"), "hello world")

[<Test>]
let ``multithread lock lazy`` () =
LazyTestHelper(LazyFactory<string>.LockMultiThreadLazy(fun _ -> "hello" + " " + "world"), "hello world")
22 changes: 22 additions & 0 deletions semester4/Lazy/Lazy.sln
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions semester4/Lazy/Lazy/ILazy.fs
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions semester4/Lazy/Lazy/Lazy.fs
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions semester4/Lazy/Lazy/Lazy.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="ILazy.fs" />
<Compile Include="Lazy.fs" />
<Compile Include="LockMultithreadLazy.fs" />
<Compile Include="LockFreeLazy.fs" />
<Compile Include="LazyFactory.fs" />
</ItemGroup>

</Project>
17 changes: 17 additions & 0 deletions semester4/Lazy/Lazy/LazyFactory.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module LazyFactory

open ILazy
open SingleThreadLazy
open LockMultiThreadLazy
open LockFreeLazy

/// Creates lazy classes of various types
type LazyFactory<'a> =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тоже неплохо бы комментарии

/// 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>
21 changes: 21 additions & 0 deletions semester4/Lazy/Lazy/LockFreeLazy.fs
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions semester4/Lazy/Lazy/LockMultithreadLazy.fs
Original file line number Diff line number Diff line change
@@ -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