[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