Skip to content

Commit

Permalink
Merge pull request #432 from mpostol/Monitor
Browse files Browse the repository at this point in the history
Monitor => master - working on concurrent programming.
mpostol authored Dec 10, 2024
2 parents ff9f2e9 + e89e2f4 commit 2afbb81
Showing 8 changed files with 284 additions and 10 deletions.
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>
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>
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
@@ -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
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>
Original file line number Diff line number Diff line change
@@ -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));
}

0 comments on commit 2afbb81

Please sign in to comment.