Modified Chapter20 tests to run check for no-atomic operations using parellelization.
MODIFY
src/Chapter20.Tests/Listing20.01.Tests.cs
src/Chapter20.Tests/Listing20.01.Tests.cs
@@ -9,10 +9,26 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_01.Tests
[TestClass]
public class ProgramTests
{
- public static bool IsIncrementDecrementSynchronized(Action<string[]> action)
+ public static bool IsIncrementDecrementLikelySynchronized(
+ Func<string[], int> func, long iterations)
{
- string expected = $"Increment and decrementing \\d* times...{Environment.NewLine}Count = (?<Count>\\d*)";
- bool synchronized = true;
+ string[] args = { iterations.ToString() };
+ return func(args) == 0;
+ }
+
+
+ /// <summary>
+ /// Returns true if the increment/decrement operations are not atomic.
+ ///
+ /// IMPORTANT: This method does not prove something is atomic. It returns
+ /// true if they are not atomic. A return of false indicates atomicity is unknown.
+ /// </summary>
+ /// <param name="func"></param>
+ /// <returns></returns>
+ public static bool IsIncrementDecrementNotAtomic(
+ Func<string[], int> func)
+ {
+ bool unsyncrhonized = false;
CancellationTokenSource cancellationtokenSource
= new CancellationTokenSource();
@@ -24,50 +40,62 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_01.Tests
try
{
- Parallel.For(5, 9, options, i =>
+ Parallel.For(1, 3, options, i =>
{
- string output = "";
- string[] count = { (2 * Math.Pow(10, i)).ToString() };
+ string[] count = { int.MaxValue.ToString() };
+ // Alternatively, iterate from 5-9 (10 exclusive) and
+ // set count using { (2 * Math.Pow(10, i)).ToString() };
+ // Thereby, incrementing count logarithmically until int.MaxValue.
- output = IntelliTect.TestTools.Console.ConsoleAssert.Execute("",
- () =>
- {
- action(count);
- });
- Console.WriteLine(output);
- MatchCollection matches = Regex.Matches(output, expected, System.Text.RegularExpressions.RegexOptions.Multiline);
- if(matches.Count > 0 && matches[0].Success)
+ if (func(count) != 0)
{
- string result = matches[0].Groups["Count"].Value;
- if (result != "0")
- {
- synchronized = false; // No synchronization required for bool
+ unsyncrhonized = true;
cancellationtokenSource.Cancel();
}
- }
+
});
}
catch (OperationCanceledException) { }
- catch(AggregateException exception)
- {
- foreach (Exception innerException in exception.Flatten().InnerExceptions)
+
+ return unsyncrhonized;
+ }
+
+ static public void VerifyOutputIncrementAndDecrement(Func<string[], int> main)
{
- if (!(innerException is OperationCanceledException)){}
- else
+ int? result = null;
+ string expected = $"Increment and decrementing \\d* times...{Environment.NewLine}Count = (?<Count>\\d*)";
+ string output = IntelliTect.TestTools.Console.ConsoleAssert.Execute("",
+ () =>
{
- System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(
- exception.InnerException).Throw();
- }
- }
+ result = main(new string[] { "1" });
+ });
+ MatchCollection matches = Regex.Matches(output, expected, System.Text.RegularExpressions.RegexOptions.Multiline);
+ Assert.IsTrue(matches.Count > 0);
+ Assert.IsTrue(matches[0].Success);
+ Assert.AreEqual(result.ToString(), matches[0].Groups["Count"].Value);
}
- return synchronized;
+
+
+ [TestMethod]
+ public void MainVerifyOutputIncrementAndDecrement()
+ {
+ VerifyOutputIncrementAndDecrement(Program.Main);
}
+ /// <summary>
+ /// Indicates that a the increment/decrement operations are not atomic
+ /// IMPORTANT: Returns Inconclusive as it may just happen to increment/decrement
+ /// consistently but that doesn't prove that it is synchronized (which is not reasonably
+ /// possible to prove with code execution).
+ /// </summary>
[TestMethod]
public void UnsynchronizedIncrementAndDecrement()
{
- Assert.IsFalse(
- IsIncrementDecrementSynchronized(Program.Main));
+ bool isUnsynchronized = IsIncrementDecrementNotAtomic(Program.Main);
+ if (!isUnsynchronized)
+ {
+ Assert.Inconclusive("Unexpectedly, the number of increments and decrements was the same even though there was no lock mechanism.");
+ }
}
}
}

MODIFY
src/Chapter20.Tests/Listing20.02.Tests.cs
src/Chapter20.Tests/Listing20.02.Tests.cs
@@ -8,11 +8,26 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_02.Tests
public class ProgramTests
{
+ [TestMethod]
+ public void MainVerifyOutputIncrementAndDecrement()
+ {
+ VerifyOutputIncrementAndDecrement(Program.Main);
+ }
+
+ /// <summary>
+ /// Indicates that a the increment/decrement operations are not atomic
+ /// IMPORTANT: Returns Inconclusive as it may just happen to increment/decrement
+ /// consistently but that doesn't prove that it is synchronized (which is not reasonably
+ /// possible to prove with code execution).
+ /// </summary>
[TestMethod]
public void UnsynchronizedIncrementAndDecrement()
{
- Assert.IsFalse(
- IsIncrementDecrementSynchronized(Program.Main));
+ bool isUnsynchronized = IsIncrementDecrementNotAtomic(Program.Main);
+ if (!isUnsynchronized)
+ {
+ Assert.Inconclusive("Unexpectedly, the number of increments and decrements was the same even though there was no lock mechanism.");
+ }
}
}
}

MODIFY
src/Chapter20.Tests/Listing20.03.Tests.cs
src/Chapter20.Tests/Listing20.03.Tests.cs
@@ -7,12 +7,18 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_03.Tests
[TestClass]
public class ProgramTests
{
+ [TestMethod]
+ public void MainVerifyOutputIncrementAndDecrement()
+ {
+ VerifyOutputIncrementAndDecrement(Program.Main);
+ }
[TestMethod]
public void SynchronizedIncrementAndDecrement()
{
- Assert.IsFalse(
- IsIncrementDecrementSynchronized(Program.Main));
+ Assert.IsTrue(
+ IsIncrementDecrementLikelySynchronized(Program.Main, short.MaxValue));
}
}
}
+

MODIFY
src/Chapter20.Tests/Listing20.04.Tests.cs
src/Chapter20.Tests/Listing20.04.Tests.cs
@@ -12,8 +12,8 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_04.Tests
[TestMethod]
public void SynchronizedIncrementAndDecrement()
{
- Assert.IsFalse(
- IsIncrementDecrementSynchronized(Program.Main));
+ Assert.IsTrue(
+ IsIncrementDecrementLikelySynchronized(Program.Main, short.MaxValue));
}
}

MODIFY
src/Chapter20.Tests/Listing20.05.Tests.cs
src/Chapter20.Tests/Listing20.05.Tests.cs
using System;
+using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using static AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_01.Tests.ProgramTests;
@@ -12,12 +13,8 @@ namespace AddisonWesley.Michaelis.EssentialCSharp.Chapter20.Listing20_05.Tests
[TestMethod]
public void SynchronizedIncrementAndDecrement()
{
- Assert.IsTrue(
- IsIncrementDecrementSynchronized(
- (args) =>
- {
- Program.Main(args).Wait();
- }));
+ Assert.IsTrue(IsIncrementDecrementLikelySynchronized(
+ (args)=>Program.Main(args).Result, short.MaxValue));
}
}

MODIFY
src/Chapter20/Listing20.01.UnsychronizedState.cs
src/Chapter20/Listing20.01.UnsychronizedState.cs
public class Program
{
- static public CancellationToken CancellationToken;
+ static public CancellationToken? CancellationToken;
static int _Total = int.MaxValue;
static int _Count = 0;
- public static void Main(string[] args)
+ public static int Main(string[] args)
{
if (args?.Length > 0) { int.TryParse(args[0], out int _Total); }
Task task = Task.Run(() => Decrement());
// Increment
- for(int i = 0; i < _Total && !CancellationToken.IsCancellationRequested; i++)
+ for(int i = 0; (i < _Total) &&
+ (CancellationToken != null) &&!CancellationToken.Value.IsCancellationRequested;
+ i++)
{
_Count++;
}
task.Wait();
Console.WriteLine($"Count = {_Count}");
+
+ return _Count;
}
static void Decrement()
{
// Decrement
- for(int i = 0; i < _Total && !CancellationToken.IsCancellationRequested; i++)
+ for(int i = 0; i < _Total &&
+ (CancellationToken != null) && !CancellationToken.Value.IsCancellationRequested;
+ i++)
{
_Count--;
}

MODIFY
src/Chapter20/Listing20.02.UnsychronizedLocalVariables.cs
src/Chapter20/Listing20.02.UnsychronizedLocalVariables.cs
{
static public CancellationToken CancellationToken;
- public static void Main(string[] args)
+ public static int Main(string[] args)
{
int total = int.MaxValue;
int x = 0;
if (args?.Length > 0) { int.TryParse(args[0], out total); }
+ Console.WriteLine($"Increment and decrementing {total} times...");
Parallel.For(0, total,
new ParallelOptions() { CancellationToken = CancellationToken}
x--;
});
Console.WriteLine("Count = {0}", x);
+ return x;
}
}
}

MODIFY
src/Chapter20/Listing20.03.SynchronizingWithMonitor.cs
src/Chapter20/Listing20.03.SynchronizingWithMonitor.cs
{
readonly static object _Sync = new object();
static int _Total = int.MaxValue;
- static long _Count = 0;
+ static int _Count = 0;
- public static void Main(string[] args)
+ public static int Main(string[] args)
{
if (args?.Length > 0) { int.TryParse(args[0], out _Total); }
Console.WriteLine($"Increment and decrementing {_Total} times...");
task.Wait();
Console.WriteLine($"Count = {_Count}");
+ return _Count;
}
static void Decrement()

MODIFY
src/Chapter20/Listing20.04.SynchronizingWithLockKeyword.cs
src/Chapter20/Listing20.04.SynchronizingWithLockKeyword.cs
{
readonly static object _Sync = new object();
static int _Total = int.MaxValue;
- static long _Count = 0;
+ static int _Count = 0;
- public static void Main(string[] args)
+ public static int Main(string[] args)
{
if (args?.Length > 0) { int.TryParse(args[0], out _Total); }
Console.WriteLine($"Increment and decrementing {_Total} times...");
task.Wait();
Console.WriteLine($"Count = {_Count}");
+ return _Count;
}
static void Decrement()

MODIFY
src/Chapter20/Listing20.05.AsyncMain.cs
src/Chapter20/Listing20.05.AsyncMain.cs
{
readonly static object _Sync = new object();
static int _Total = int.MaxValue;
- static long _Count = 0;
+ static int _Count = 0;
- public static async Task Main(string[] args)
+ public static async Task<int> Main(string[] args)
{
if (args?.Length > 0) { int.TryParse(args[0], out _Total); }
Console.WriteLine($"Increment and decrementing {_Total} times...");
await task;
Console.WriteLine($"Count = {_Count}");
+ return _Count;
}
static void Decrement()

Modified Chapter20 tests to run check for no-atomic operations using parellelization.