Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyName>TP.ConcurrentProgramming.$(MSBuildProjectName)</AssemblyName>
Expand Down
67 changes: 67 additions & 0 deletions ConcurrentProgramming/CommonDataConsistency/CriticalSectionLock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//____________________________________________________________________________________________________________________________________
//
// Copyright (C) 2024, Mariusz Postol LODZ POLAND.
//
// To be in touch join the community by pressing the `Watch` button and get started commenting using the discussion panel at
//
// https://github.com/mpostol/TP/discussions/182
//
//_____________________________________________________________________________________________________________________________________

namespace TP.ConcurrentProgramming.CommonDataConsistency
{
/// <summary>
/// Code sample to examine data consistence in th concurrent programming environment
/// </summary>
public class CriticalSectionLock
{
#region Monitor methods

private readonly Lock _lockObj = new();

public void NoProtectedMethod(object? state)
{
CommonDataProcessingSimulator();
}

public void ProtectedMethod(object? state)
{
using (_lockObj.EnterScope())
{
CommonDataProcessingSimulator();
}
}

#endregion Monitor methods

#region private

private int m_IntegerA = 0;
private int m_IntegerB = 0;
private Random m_Random = new Random();

private void CommonDataProcessingSimulator()
{
for (int i = 0; i < 1000000; i++)
{
int _value = m_Random.Next(0, 10000);
m_IntegerA = _value;
m_IntegerB = -_value;
IsConsistent &= m_IntegerA + m_IntegerB == 0;
}
}

#endregion private

#region UT Instrumentation

/// <summary>
/// Gets a value indicating whether this instance is consistent.
/// </summary>
/// <remarks>Always must be true.</remarks>
/// <value><c>true</c> if this instance is consistent; otherwise, <c>false</c>.</value>
public bool IsConsistent = true;

#endregion UT Instrumentation
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//____________________________________________________________________________________________________________________________________
//
// Copyright (C) 2024, Mariusz Postol LODZ POLAND.
//
// To be in touch join the community by pressing the `Watch` button and get started commenting using the discussion panel at
//
// https://github.com/mpostol/TP/discussions/182
//
//_____________________________________________________________________________________________________________________________________

namespace TP.ConcurrentProgramming.CommonDataConsistency.UnitTest
{
[TestClass]
public class CriticalSectionLockUnitTest
{
/// <summary>
/// Test lack of data consistency using manually created <seealso cref="Thread"/>.
/// </summary>
[TestMethod]
public void LackOfDataConsistencyUsingManuallyCreatedThreads()
{
CriticalSectionLock m_ThreadsExample = new CriticalSectionLock();
RunConcurrentlyManualyCreatedThreads(m_ThreadsExample.NoProtectedMethod);
Assert.IsFalse(m_ThreadsExample.IsConsistent);
}

/// <summary>
/// Test how to protect data consistency using critical section implemented using Monitor and manually created <seealso cref="Thread"/>.
/// </summary>
[TestMethod]
public void CriticalSectionImplementedUsingMonitorManuallyCreatedThreads()
{
CriticalSectionLock m_ThreadsExample = new CriticalSectionLock();
RunConcurrentlyManualyCreatedThreads(m_ThreadsExample.ProtectedMethod);
Assert.IsTrue(m_ThreadsExample.IsConsistent);
}

/// <summary>
/// Test lack of data consistency using <seealso cref="ThreadPool"/>.
/// </summary>
[TestMethod]
public void LackOfDataConsistencyUsingThreadPool()
{
CriticalSectionLock m_ThreadsExample = new CriticalSectionLock();
RunThreadsUsingThreadPool(m_ThreadsExample.NoProtectedMethod);
Assert.IsFalse(m_ThreadsExample.IsConsistent);
}

/// <summary>
/// Test how to protect data consistency using critical section implemented using Monitor and <seealso cref="ThreadPool"/>.
/// </summary>
[TestMethod]
public void CriticalSectionImplementedUsingMonitorThreadPool()
{
CriticalSectionLock m_ThreadsExample = new CriticalSectionLock();
RunThreadsUsingThreadPool(m_ThreadsExample.ProtectedMethod);
Assert.IsTrue(m_ThreadsExample.IsConsistent);
}

/// <summary>
/// Test lack of data consistency using manually created <seealso cref="Task"/>
/// </summary>
[TestMethod]
public void CheckConsitencyUsingTaskNoMonitor()
{
CriticalSectionLock m_ThreadsExample = new CriticalSectionLock();
RunThreadsUsingTask(m_ThreadsExample.NoProtectedMethod);
Assert.IsFalse(m_ThreadsExample.IsConsistent);
}

/// <summary>
/// Test how to protect data consistency using critical section implemented using Monitor and <seealso cref="Task"/>.
/// </summary>
[TestMethod]
public void CheckWhetherThreadsUsingThreadPoolAreSynchronized()
{
CriticalSectionLock m_ThreadsExample = new CriticalSectionLock();
RunThreadsUsingTask(m_ThreadsExample.ProtectedMethod);
Assert.IsTrue(m_ThreadsExample.IsConsistent);
}

#region Test Instrumentation

public void RunConcurrentlyManualyCreatedThreads(ParameterizedThreadStart start)
{
if (start == null)
throw new ArgumentNullException(nameof(start));
Thread[] threadsArray = new Thread[2];
for (int i = 0; i < threadsArray.Length; i++)
threadsArray[i] = new Thread(start);
foreach (Thread _thread in threadsArray)
_thread.Start();
foreach (Thread _thread in threadsArray)
_thread.Join();
}

public void RunThreadsUsingThreadPool(WaitCallback callBack)
{
for (int i = 0; i < 2; i++)
ThreadPool.QueueUserWorkItem(callBack);
//wait for threads
//TODO must be improved it could cause race condition
Thread.Sleep(1000);
}

public void RunThreadsUsingTask(WaitCallback callBack)
{
if (callBack == null)
throw new ArgumentNullException(nameof(callBack));
List<Task> _tasksInProgress = new List<Task>();
for (int i = 0; i < 2; i++)
_tasksInProgress.Add(Task.Run(() => callBack(null)));
//wait for threads
Task.WaitAll(_tasksInProgress.ToArray());
}

#endregion Test Instrumentation
}
}
14 changes: 7 additions & 7 deletions ConcurrentProgramming/ConcurrentProgramming.sln
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".Project", ".Project", "{16
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonDataConsistencyUnitTest", "CommonDataConsistencyUnitTest\CommonDataConsistencyUnitTest.csproj", "{76CD1D47-4B5A-48B8-B8C0-A8BF2041B42B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonDataConsistencyUnitTest", "CommonDataConsistencyUnitTest\CommonDataConsistencyUnitTest.csproj", "{76CD1D47-4B5A-48B8-B8C0-A8BF2041B42B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresentationViewModelTest", "ReactiveInteractiveUserInterface\PresentationViewModelTest\PresentationViewModelTest.csproj", "{8F68B3AE-490D-4B8D-A2C8-421E1D0DD6B2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationViewModelTest", "ReactiveInteractiveUserInterface\PresentationViewModelTest\PresentationViewModelTest.csproj", "{8F68B3AE-490D-4B8D-A2C8-421E1D0DD6B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data", "ReactiveInteractiveUserInterface\Data\Data.csproj", "{3AA27DB8-F9C6-4587-8290-0FC0FEC69AC2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Data", "ReactiveInteractiveUserInterface\Data\Data.csproj", "{3AA27DB8-F9C6-4587-8290-0FC0FEC69AC2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresentationModel", "ReactiveInteractiveUserInterface\PresentationModel\PresentationModel.csproj", "{9B39E905-6C7C-4F2B-BD7A-77B7E3893F59}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PresentationModel", "ReactiveInteractiveUserInterface\PresentationModel\PresentationModel.csproj", "{9B39E905-6C7C-4F2B-BD7A-77B7E3893F59}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogic", "ReactiveInteractiveUserInterface\BusinessLogic\BusinessLogic.csproj", "{BE3CC0CD-F262-4122-B7C7-F37EEB8F3C48}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BusinessLogic", "ReactiveInteractiveUserInterface\BusinessLogic\BusinessLogic.csproj", "{BE3CC0CD-F262-4122-B7C7-F37EEB8F3C48}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLogicTest", "ReactiveInteractiveUserInterface\BusinessLogicTest\BusinessLogicTest.csproj", "{8760446B-8BD6-4254-9977-475EAEE45B0B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BusinessLogicTest", "ReactiveInteractiveUserInterface\BusinessLogicTest\BusinessLogicTest.csproj", "{8760446B-8BD6-4254-9977-475EAEE45B0B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataTest", "ReactiveInteractiveUserInterface\DataTest\DataTest.csproj", "{FD3FA340-EB7B-4037-B3AB-710E6817AC9D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DataTest", "ReactiveInteractiveUserInterface\DataTest\DataTest.csproj", "{FD3FA340-EB7B-4037-B3AB-710E6817AC9D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
86 changes: 86 additions & 0 deletions ConcurrentProgramming/Fundamentals/HoareMonitor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//____________________________________________________________________________________________________________________________________
//
// Copyright (C) 2024, Mariusz Postol LODZ POLAND.
//
// To be in touch join the community by pressing the `Watch` button and get started commenting using the discussion panel at
//
// https://github.com/mpostol/TP/discussions/182
//
//_____________________________________________________________________________________________________________________________________

namespace TP.ConcurrentProgramming.Fundamentals
{
public abstract class HoareMonitor
{
protected interface ISignal
{
/// <summary>
/// A thread that changes the state of the monitor in a way that might allow a waiting thread to proceed will signal the <see cref="ISignal"/> variable and,
/// as a result, awake up one of the waiting threads.
/// </summary>
/// <remarks>
/// The send signal operation needs some scheduling decisions. After sending a signal the thread should immediately wake up the waiting one, if any,
/// and give up the monitor to it. It means that the monitor is transferred from a thread issuing a signal to the signaled one.
/// The suspended process will afterward regain the processor. This kind of scheduling treats the waiting process as a continuation of the thread that has established
/// the awaited condition. The main advantage of the above solution could be revealed when the program validity is proved because the monitor is not released at all,
/// and thereby there is no possibility to change the data enclosed by the monitor, and the established condition as well, by another, third process.
/// </remarks>
void Send();

/// <summary>
/// A thread that cannot proceed because an event is not met will wait on a signal variable.
/// </summary>
/// <remarks>
/// After invoking the wait operation the current process releases all the monitors that it possesses, thus it must leave all relevant data in a consistent state.
/// There must be initiated a scheduling mechanism to choose another process to run because the processor is released as well.
/// </remarks>
void Wait();

/// <summary>
/// Check if any thread is waiting for the specified signal
/// </summary>
bool Await();
}

protected interface ICondition
{
/// <summary>
/// A thread that changes the state of the monitor in a way that might allow a waiting thread to proceed will signal the condition variable and,
/// as a result, awake up one of the waiting threads.
/// </summary>
/// <remarks>
/// This operation is based upon the principle that the signaling thread keeps control of the monitor, and the signaled one changes only its state and becomes ready to run.
/// Of course, it cannot be assumed that the announced condition is still fulfilled when the signaled process is resumed, because other processes,
/// taking precedence, may have changed it in the meantime. Therefore, the signaled process, just after taking the control, should check again the condition,
/// except that it cannot be changed, and, if necessary, wait once more for its occurrence.
/// </remarks>
void Send();

/// <summary>
/// A thread that cannot proceed because an event is not met will wait on a condition variable.
/// </summary>
/// <remarks>
/// After invoking the wait operation the current process releases all the monitors that it possesses, thus it must leave all relevant data in a consistent state.
/// There must be initiated a scheduling mechanism to choose another process to run because the processor is released as well.
/// </remarks>
void Wait();

/// <summary>
/// Check if any thread is waiting for the specified signal
/// </summary>
bool Await();
}

/// <summary>
/// Creates <see cref="ISignal"/> to be instantiated and used inside the monitor. If <see cref="ISignal"/> is not used in the context of the monitor it was created an exception is thrown.
/// </summary>
/// <returns>a new instance of <see cref="ISignal"/> attached to this monitor.</returns>
protected abstract ISignal CreateSignal();

/// <summary>
/// Creates <see cref="ICondition"/> to be instantiated and used inside the monitor. If <see cref="ISignal"/> is not used in the context of the monitor it was created an exception is thrown.
/// </summary>
/// <returns></returns>
protected abstract ICondition GetCondition();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public MainWindow()
Random random = new Random();
InitializeComponent();
MainWindowViewModel viewModel = (MainWindowViewModel)DataContext;
double screenWidth = SystemParameters.PrimaryScreenWidth;
double screenHeight = SystemParameters.PrimaryScreenHeight;
viewModel.Start(random.Next(5, 10));
}

Expand Down