diff --git a/Lazy/Lazy.sln b/Lazy/Lazy.sln new file mode 100644 index 0000000..ccbe521 --- /dev/null +++ b/Lazy/Lazy.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33403.182 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lazy", "Lazy\Lazy.csproj", "{F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestsForLazy", "TestsForLazy\TestsForLazy.csproj", "{A08651D9-B95A-4E33-A8B0-D5635E0523C8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8C3CCDA-224A-4684-8E2E-B649BDBB08D3}.Release|Any CPU.Build.0 = Release|Any CPU + {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A08651D9-B95A-4E33-A8B0-D5635E0523C8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8BB48519-EA16-4F07-8949-4E4CC34A90F4} + EndGlobalSection +EndGlobal diff --git a/Lazy/Lazy/ILazy.cs b/Lazy/Lazy/ILazy.cs new file mode 100644 index 0000000..29aaacb --- /dev/null +++ b/Lazy/Lazy/ILazy.cs @@ -0,0 +1,12 @@ +namespace Lazy; + +/// +/// The class implements an object whose function is called only once. +/// +public interface ILazy +{ + /// + /// Gets the created object. + /// + T? Get(); +} \ No newline at end of file diff --git a/Lazy/Lazy/Lazy.csproj b/Lazy/Lazy/Lazy.csproj new file mode 100644 index 0000000..424caff --- /dev/null +++ b/Lazy/Lazy/Lazy.csproj @@ -0,0 +1,11 @@ + + + + Exe + net7.0 + enable + enable + Library + + + diff --git a/Lazy/Lazy/MultiThreadLazy.cs b/Lazy/Lazy/MultiThreadLazy.cs new file mode 100644 index 0000000..9bca5ea --- /dev/null +++ b/Lazy/Lazy/MultiThreadLazy.cs @@ -0,0 +1,57 @@ +namespace Lazy; + +public class MultiThreadLazy : ILazy +{ + private Func? supplier; + private readonly Object objectLock = new (); + private volatile bool isCalculated = false; + private T? resultSuppiler; + private Exception? exceptionSuppiler = default; + + /// + /// Constructor for storing the object creation function + /// + public MultiThreadLazy(Func function) + { + supplier = function; + } + + public T? Get() + { + if (!isCalculated) + { + lock (objectLock) + { + if (!isCalculated) + { + try + { + if (supplier == null) + { + resultSuppiler = default; + } + else + { + resultSuppiler = supplier(); + } + } + catch (Exception ex) + { + exceptionSuppiler = ex; + } + finally + { + supplier = null; + } + isCalculated = true; + } + } + } + + if (exceptionSuppiler != default) + { + throw exceptionSuppiler; + } + return resultSuppiler; + } +} \ No newline at end of file diff --git a/Lazy/Lazy/SingleThreadLazy.cs b/Lazy/Lazy/SingleThreadLazy.cs new file mode 100644 index 0000000..e4f9747 --- /dev/null +++ b/Lazy/Lazy/SingleThreadLazy.cs @@ -0,0 +1,45 @@ +namespace Lazy; + +public class SingleThreadLazy : ILazy +{ + private Func? supplier; + private bool isCalculated = false; + private T? resultSuppiler; + private Exception? exceptionFromSuppiler = default; + + /// + /// Constructor for storing the object creation function + /// + public SingleThreadLazy(Func function) + { + supplier = function; + } + + public T? Get() + { + if (!isCalculated) + { + try + { + if (supplier != null) + { + resultSuppiler = supplier(); + } + } + catch (Exception ex) + { + exceptionFromSuppiler = ex; + } + finally + { + supplier = null; + } + isCalculated = true; + } + if (exceptionFromSuppiler != default) + { + throw exceptionFromSuppiler; + } + return resultSuppiler; + } +} \ No newline at end of file diff --git a/Lazy/TestsForLazy/FunctionsForTests.cs b/Lazy/TestsForLazy/FunctionsForTests.cs new file mode 100644 index 0000000..86afc36 --- /dev/null +++ b/Lazy/TestsForLazy/FunctionsForTests.cs @@ -0,0 +1,28 @@ +namespace Lazy; + +/// +/// Class implements functions for checking Lazy +/// +public class FunctionsForTests +{ + private int howMuchFunctionBeenCalled = 0; + + /// + /// Function that counts how much it is caused by + /// + public int FunctionForLazyWithCounter() + { + Interlocked.Increment(ref howMuchFunctionBeenCalled); + return howMuchFunctionBeenCalled; + } + + /// + /// Function that throws DivideByZeroException + /// + /// + public int DivideByZeroException() + { + var firstNumber = 1; + return firstNumber / 0; + } +} \ No newline at end of file diff --git a/Lazy/TestsForLazy/TestsForLazy.cs b/Lazy/TestsForLazy/TestsForLazy.cs new file mode 100644 index 0000000..6ffc03e --- /dev/null +++ b/Lazy/TestsForLazy/TestsForLazy.cs @@ -0,0 +1,113 @@ +namespace TestsForLazy; +using Lazy; + +public class Tests +{ + private static readonly FunctionsForTests globalFunctionsForTestsForSingleThread = new(); + private static readonly FunctionsForTests globalFunctionsForTestsForMultiThread = new(); + + [Test] + public void SimpleExampleWithMultiThreadLazy() + { + var functionsForTests = new FunctionsForTests(); + var multiThreadLazy = new MultiThreadLazy(() => functionsForTests.FunctionForLazyWithCounter()); + var arrayThreads = new Thread[10]; + + var arrayResult = new int[10]; + + for (int i = 0; i < arrayThreads.Length; i++) + { + var local = i; + arrayThreads[i] = new Thread(() => + { + for (var j = local; j < 10; j++) + { + arrayResult[j] = multiThreadLazy.Get(); + } + }); + } + + foreach (var thread in arrayThreads) + { + thread.Start(); + } + + foreach (var element in arrayThreads) + { + element.Join(); + } + + foreach (var element in arrayResult) + { + Assert.That(element, Is.EqualTo(1)); + } + } + + [TestCaseSource(nameof(LazyForTestWithFunctionWithCounter))] + public void SimpleExampleWithOneThread(ILazy lazy) + { + for (int i = 0; i < 10; ++i) + { + Assert.That(lazy.Get(), Is.EqualTo(1)); + } + } + + [TestCaseSource(nameof(LazyForTestWithFunctionWithException))] + public void ExampleWithExceptionWithOneThread(ILazy lazy) + { + for (int i = 0; i < 10; ++i) + { + Assert.Throws(() => lazy.Get()); + } + } + + [Test] + public void ThreadRaceTest() + { + var functionsForTests = new FunctionsForTests(); + var multiThreadLazy = new MultiThreadLazy(() => functionsForTests.FunctionForLazyWithCounter()); + var arrayThreads = new Thread[10]; + var arrayResult = new int[10]; + + for (int i = 0; i < arrayThreads.Length; i++) + { + var local = i; + arrayThreads[i] = new Thread(() => + { + for (var j = local; j < 10; j++) + { + arrayResult[j] = multiThreadLazy.Get(); + } + }); + } + + foreach (var element in arrayThreads) + { + element.Start(); + } + + foreach (var element in arrayThreads) + { + element.Join(); + } + + foreach (var element in arrayResult) + { + Assert.That(element, Is.EqualTo(1)); + } + } + + private static IEnumerable LazyForTestWithFunctionWithCounter + => new TestCaseData[] + { + new TestCaseData(new MultiThreadLazy(() => globalFunctionsForTestsForSingleThread.FunctionForLazyWithCounter())), + new TestCaseData(new SingleThreadLazy(() => globalFunctionsForTestsForMultiThread.FunctionForLazyWithCounter())), + }; + + private static IEnumerable LazyForTestWithFunctionWithException + => new TestCaseData[] + { + new TestCaseData(new SingleThreadLazy(() => globalFunctionsForTestsForSingleThread.DivideByZeroException())), + new TestCaseData(new MultiThreadLazy(() => globalFunctionsForTestsForMultiThread.DivideByZeroException())), + }; +} \ No newline at end of file diff --git a/Lazy/TestsForLazy/TestsForLazy.csproj b/Lazy/TestsForLazy/TestsForLazy.csproj new file mode 100644 index 0000000..70890c4 --- /dev/null +++ b/Lazy/TestsForLazy/TestsForLazy.csproj @@ -0,0 +1,23 @@ + + + + net7.0 + enable + enable + + false + + + + + + + + + + + + + + + diff --git a/Lazy/TestsForLazy/Usings.cs b/Lazy/TestsForLazy/Usings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/Lazy/TestsForLazy/Usings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file