[M3devel] sample program for testing threads / MUTEX
Randy Coleburn
rcoleburn at scires.com
Fri Nov 6 06:16:44 CET 2009
Some time ago we had a discussion about needing some tests for threading and MUTEX.
Jay Krell later referenced an online book titled "The Little Book of Semaphores".
I've attached a program to this email that is a variation of the problem presented in Section 8.1 of this book.
You might try running it on various platforms to see if a problem is detected.
The program takes 3 inputs:
1 = The number of Threads that should be created.
2 = The maximum count, i.e., the number of shared counter variables to be created.
3 = Whether to use MUTEX protection for concurrency control.
For example, on Windows Vista with the following inputs, I get the results shown below:
(1001, 1000000, 0) = FAIL (as expected), histogram = (0:32), (1:999943), (2:25)
(1001. 1000000, 1) = PASS (as expected), histogram = (1:1000000)
Using these inputs, the program took nearly 3 minutes to run on a recent vintage Dell laptop computer.
Just because a particular test run succeeds doesn't mean all is well. Obviously, for small # of threads and/or small maxCount, even the non-MUTEX version will succeed sometimes. But, trying larger numbers will give a more robust test.
If desired, I can try to implement some of the other test programs in the book. Let me know.
Regards,
Randy Coleburn
MODULE ThreadTest1 EXPORTS Main;
(* This program designed to test if MUTEX working properly using multiple threads.
Author: Randy Coleburn
Inspiration: "The Little Book of Semaphores", by Allen Downey, Section 8.1: Mutex checker problem.
*)
IMPORT Fmt, IO, Thread, Time;
<*FATAL IO.Error*>
CONST
ModuleName = "ThreadTest1";
Delay = 0.11d0;
TYPE
ChildClosure = Thread.Closure OBJECT
id: CARDINAL;
METHODS
OVERRIDES
apply := ChildApply;
END; (* ChildClosure *)
CounterArray = REF ARRAY OF CARDINAL;
VAR
maxCount: CARDINAL := 0;
mutex: MUTEX := NEW(MUTEX);
numThreads: CARDINAL := 10;
sharedArray: CounterArray;
sharedCounter: CARDINAL := 0;
startTime: LONGREAL;
threadSafeMode: BOOLEAN := FALSE;
PROCEDURE Print (msg: TEXT;
)
RAISES {} =
(* *)
BEGIN (* Print *)
IO.Put(msg & "\n");
END Print;
PROCEDURE ChildApply (self: ChildClosure;
): REFANY
RAISES {} =
(* *)
CONST Proc = ModuleName & ".ChildApply";
VAR
myID: TEXT := Proc & "( " & Fmt.Int(self.id) & " )";
numLoops: CARDINAL := 0;
BEGIN (* ChildApply *)
Print("Begin " & myID);
IF threadSafeMode
THEN
WHILE sharedCounter < maxCount
DO
LOCK mutex DO
INC(sharedArray[sharedCounter]);
INC(sharedCounter);
END; (* lock *)
INC(numLoops);
Thread.Pause(Delay);
END; (* while *)
ELSE
WHILE sharedCounter < maxCount
DO
INC(sharedArray[sharedCounter]);
INC(sharedCounter);
INC(numLoops);
Thread.Pause(Delay);
END; (* while *)
END; (* if *)
Print("End " & myID & " ran " & Fmt.Int(numLoops) & " times.");
RETURN NIL;
END ChildApply;
PROCEDURE PrintHistogram ()
RAISES {} =
(* *)
CONST Proc = ModuleName & ".PrintHistogram";
VAR
count: CounterArray;
error: BOOLEAN := FALSE;
BEGIN (* PrintHistogram *)
Print("Begin" & Proc);
Print("(this make take a few moments when max count is large)\n");
count := NEW(CounterArray, maxCount+1);
FOR i := 0 TO maxCount
DO
count[i] := 0;
END; (* for *)
FOR i := 0 TO (maxCount - 1)
DO
WITH c = sharedArray[i]
DO
IF c > maxCount
THEN
error := TRUE;
Print("!!! Something really broken in CM3 because sharedArray[" & Fmt.Int(i) & "] = " & Fmt.Int(c) & " which is greater than maxCount !!!");
ELSE
INC(count[c]);
END; (* if *)
END; (* with *)
END; (* for *)
FOR n := 0 TO maxCount
DO
WITH total = count[n]
DO
IF total > 0
THEN
Print("(" & Fmt.Int(n) & ": " & Fmt.Int(total) & ")");
END; (* if *)
IF (n # 1) AND (total # 0)
THEN
error := TRUE;
ELSIF (n = 1) AND (total # maxCount)
THEN
error := TRUE;
END; (* if *)
END; (* with *)
END; (* for *)
IF error
THEN
Print("\n! ERROR DETECTED !");
ELSE
Print("\n! TEST PASSED !");
END; (* if *)
IF error AND threadSafeMode
THEN
Print("\n!!! Something is broken in the CM3 system and needs to be fixed !!!");
ELSIF (NOT threadSafeMode)
THEN
IF error
THEN
Print("\nNote that errors are expected when not using concurrency control.");
ELSE
Print("\nYou got lucky because the test should fail when not using concurrency control.\nTry again with more threads and/or a greater max count.");
END; (* if *)
END; (* if *)
Print("\nEnd" & Proc);
END PrintHistogram;
VAR
answer: INTEGER;
BEGIN (* ThreadTest1 *)
Print("-------------------------------------------------------------------------------");
Print("Module " & ModuleName);
Print("-------------------------------------------------------------------------------");
Print("This program designed to test if MUTEX working properly using multiple threads.");
Print("Author: Randy Coleburn");
Print("Inspiration: \"The Little Book of Semaphores\", by Allen Downey");
Print(" Section 8.1: Mutex checker problem.");
Print(" http://www.greenteapress.com/semaphores/");
Print("-------------------------------------------------------------------------------\n");
REPEAT
IO.Put("Enter # of threads [0.." & Fmt.Int(LAST(CARDINAL)) & "]: ");
answer := IO.GetInt();
UNTIL answer >= 0;
numThreads := answer;
REPEAT
IO.Put("Enter max count [10.." & Fmt.Int(LAST(CARDINAL)-1) & "]: ");
answer := IO.GetInt();
UNTIL (answer >= 10) AND (answer < LAST(CARDINAL));
maxCount := answer;
REPEAT
IO.Put("Run in thread-safe mode [0=false, 1=true]: ");
answer := IO.GetInt();
UNTIL (answer = 0) OR (answer = 1);
threadSafeMode := (answer = 1);
sharedArray := NEW(CounterArray, maxCount);
FOR i := 0 TO (maxCount - 1)
DO
sharedArray[i] := 0;
END; (* for *)
Print("\n-------------------------------------------------------------------------------");
Print("Ready to start " & Fmt.Int(numThreads) & " threads incrementing " & Fmt.Int(maxCount) & " shared counters");
IF threadSafeMode
THEN
Print(" using mutual exclusion semaphore for concurrency control.");
Print(" Expected Result = Test Passed with no errors.");
ELSE
Print(" without using any concurrency controls.");
Print(" Expected Result = Test Fails with errors.");
END; (* if *)
Print("Expected runtime = " & Fmt.Int(ROUND(FLOAT(maxCount, LONGREAL) * Delay / FLOAT(numThreads, LONGREAL) / 60.0d0) + 1) & " minutes.");
Print("-------------------------------------------------------------------------------");
Print("---Press ENTER to begin---");
EVAL IO.GetLine();
EVAL IO.GetLine();
startTime := Time.Now();
VAR child := NEW(REF ARRAY OF Thread.T, numThreads);
BEGIN (* block *)
FOR i := 1 TO numThreads
DO
child[i-1] := Thread.Fork(NEW(ChildClosure, id := i));
END; (* for *)
FOR i := 1 TO numThreads
DO
EVAL Thread.Join(child[i-1]);
END; (* for *)
END; (* block *)
Print("-------------------------------------------------------------------------------");
Print("All threads finished. Run time = " & Fmt.LongReal((Time.Now()-startTime)/60.0d0) & " minutes.");
Print("Result should be a total of " & Fmt.Int(maxCount) & " ones.\nSee histogram below.\n");
PrintHistogram();
END ThreadTest1.
CONFIDENTIALITY NOTICE: This email and any attachments are intended solely for the use of the named recipient(s). This e-mail may contain confidential and/or proprietary information of Scientific Research Corporation. If you are not a named recipient, you are prohibited from making any use of the information in the email and attachments. If you believe you have received this email in error, please notify the sender immediately and permanently delete the email, any attachments, and all copies thereof from any drives or storage media and destroy any printouts of the email or attachments.
EXPORT COMPLIANCE NOTICE: This email and any attachments may contain technical data subject to U.S export restrictions under the International Traffic in Arms Regulations (ITAR) or the Export Administration Regulations (EAR). Export or transfer of this technical data and/or related information to any foreign person(s) or entity(ies), either within the U.S. or outside of the U.S., may require export authorization by the appropriate U.S. Government agency prior to export or transfer. In addition, technical data may not be exported or transferred to certain countries or specified designated nationals identified by U.S. embargo controls without prior export authorization. By accepting this email and any attachments, all recipients confirm that they understand and will comply with all applicable ITAR, EAR and embargo compliance requirements.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20091106/ebc4f221/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: m3makefile
Type: application/octet-stream
Size: 111 bytes
Desc: not available
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20091106/ebc4f221/attachment-0002.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ThreadTest1.m3
Type: application/octet-stream
Size: 8513 bytes
Desc: not available
URL: <http://m3lists.elegosoft.com/pipermail/m3devel/attachments/20091106/ebc4f221/attachment-0003.obj>
More information about the M3devel
mailing list