home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.pdx.edu / 2014.02.ftp.ee.pdx.edu.tar / ftp.ee.pdx.edu / pub / users / Harry / Blitz / version-1-0 / OSProject / p2 / Main.c < prev    next >
Text File  |  2006-04-17  |  19KB  |  560 lines

  1. code Main
  2.  
  3.   -- OS Class: Project 2
  4.   --
  5.   -- <PUT YOUR NAME HERE>
  6.   --
  7.   -- This package contains the following:
  8.   --     SimpleThreadExample
  9.   --     MoreThreadExamples
  10.   --     ProducerConsumer
  11.   --     TestMutex
  12.   --     Dining Philospohers
  13.  
  14.  
  15.  
  16. -----------------------------  SynchTest  ---------------------------------
  17.  
  18.   function main ()
  19.       print ("Example Thread-based Programs...\n")
  20.  
  21.       InitializeScheduler ()
  22.  
  23.       -----  Uncomment any one of the following to perform the desired test  -----
  24.  
  25.       SimpleThreadExample ()
  26.       -- MoreThreadExamples ()
  27.       -- TestMutex ()
  28.       -- ProducerConsumer ()
  29.       -- DiningPhilosophers ()
  30.  
  31.       ThreadFinish ()
  32.  
  33.     endFunction
  34.  
  35.  
  36.  
  37. -----------------------------  SimpleThreadExample  ---------------------------------
  38.  
  39.   var aThread: Thread     -- Don't put Thread objects on the stack, since the
  40.                           -- routine that contains them may return!
  41.  
  42.   function SimpleThreadExample ()
  43.     -- This code illustrates the basics of thread usage.
  44.     --
  45.     -- This code uses 2 threads.  Each thread loops a few times.
  46.     -- Each loop iteration prints a message and executes a "Yield".
  47.     -- This code illustrates the following operations:
  48.     --     Thread creation
  49.     --     Fork
  50.     --     Yield
  51.     --     Thread termination
  52.     -- This code creates only one new thread; the currrent ("main") thread, which
  53.     -- already exists, is the other thread.  Both the main thread and the newly
  54.     -- created thread will call function "SimpleThreadFunction" to perform the looping.
  55.     --
  56.     -- Notice that timer interrupts will also cause "Yields" to be inserted at
  57.     -- unpredictable places.  Thus, the threads will not simply alternate.
  58.     --
  59.     -- Things to experiment with:
  60.     --    In TimerInterruptHandler (in Thread.c), uncomment "print ('_')".
  61.     --    In TimerInterruptHandler (in Thread.c), comment out the call to
  62.     --            Yield, which will suspend timeslicing.
  63.     --    Edit .blitzrc (see "sim" command) and change TIME_SLICE value.
  64.     --    In this function, comment out the call to "Yield".
  65.     --    
  66.  
  67.       print ("Simple Thread Example...\n")
  68.       aThread = new Thread
  69.       aThread.Init ("Second-Thread")   -- Initialize, giving thread a name
  70.       aThread.Fork (SimpleThreadFunction, 4)  -- Pass "4" as argument to the thread
  71.       SimpleThreadFunction (7)                -- The main thread will loop 7 times
  72.     endFunction
  73.  
  74.   function SimpleThreadFunction (cnt: int)
  75.     -- This function will loop "cnt" times.  Each iteration will print a
  76.     -- message and execute a "Yield", which will give the other thread a
  77.     -- chance to run.
  78.       var i: int
  79.       for i = 1 to cnt
  80.         print (currentThread.name)
  81.         nl ()
  82.         currentThread.Yield ()
  83.       endFor
  84.       ThreadFinish ()
  85.     endFunction
  86.  
  87.  
  88.  
  89. -----------------------------  MoreThreadExamples  ---------------------------------
  90.  
  91.   var th1, th2, th3, th4, th5, th6: Thread
  92.  
  93.   function MoreThreadExamples ()
  94.       var j: int
  95.           oldStatus: int
  96.  
  97.       print ("Thread Example...\n")
  98.  
  99.       -- Create some thread objects (not on the heap).
  100.  
  101.       th1 = new Thread
  102.       th2 = new Thread
  103.       th3 = new Thread
  104.       th4 = new Thread
  105.       th5 = new Thread
  106.       th6 = new Thread
  107.  
  108.       -- Initialize them.
  109.       th1.Init ("thread-a")
  110.       th2.Init ("thread-b")
  111.       th3.Init ("thread-c")
  112.       th4.Init ("thread-d")
  113.       th5.Init ("thread-e")
  114.       th6.Init ("thread-f")
  115.  
  116.       -- Start all threads running.  Each thread will execute the "foo"
  117.       -- function, but each will be passed a different argument. 
  118.       th1.Fork (foo, 1)
  119.       th2.Fork (foo, 2)
  120.       th3.Fork (foo, 3)
  121.       th4.Fork (foo, 4)
  122.       th5.Fork (foo, 5)
  123.       th6.Fork (foo, 6)
  124.  
  125.       -- Print this thread's name.  Note that we temporarily disable
  126.       -- interrupts so that all printing will happen together.  Without
  127.       -- this, the other threads might print in the middle, causing a mess.
  128.       oldStatus = SetInterruptsTo (DISABLED)
  129.         print ("\nThe currently running thread is ")
  130.         print (currentThread.name)
  131.         print ("\n")
  132.         PrintReadyList ()
  133.       oldStatus = SetInterruptsTo (oldStatus)
  134.  
  135.       for j = 1 to 10
  136.         currentThread.Yield ()
  137.         print ("\n..Main..\n")
  138.       endFor
  139.  
  140.       -- Print the readyList at this point...
  141.       PrintReadyList ()
  142.       currentThread.Print()
  143.  
  144. /*
  145.       -- Put this thread to sleep...
  146.       oldStatus = SetInterruptsTo (DISABLED)
  147.       print ("About to Sleep main thread...\n")
  148.       currentThread.Sleep ()
  149.       FatalError ("BACK FROM SLEEP !?!?!")
  150.       -- Execution will never reach this point, since the current thread
  151.       -- was not placed on any list of waiting threads.  Nothing in this
  152.       -- code could ever move this thread back to the ready list.
  153. */
  154.  
  155.       ThreadFinish ()
  156.  
  157.     endFunction
  158.  
  159.  
  160.   function foo (i: int)
  161.       var j: int
  162.  
  163.       for j = 1 to 30
  164.         printInt (i)
  165.  
  166.         if j == 20
  167.           -- Next is an example of aborting all threads and shutting down...
  168.           --   FatalError ("Whoops...(SAMPLE ERROR MESSAGE)")
  169.  
  170.           -- Next is an example of just quietly shutting down...
  171.           --   RuntimeExit ()
  172.  
  173.           -- Next is an example of what happens if execution errors occur...
  174.           --   i = j / 0         -- Generate an error
  175.         endIf
  176.  
  177.         -- Call Yield so other threads can run.  This is not necessary,
  178.         -- but it will cause more interleaving of the various threads,
  179.         -- making this program's output more interesting.
  180.         currentThread.Yield ()
  181.       endFor
  182.     endFunction
  183.  
  184.  
  185.  
  186. -----------------------------  Test Mutex  ---------------------------------
  187.  
  188.   -- This code illustrates the ideas behind "critical sections" and "mutual
  189.   -- exclusion".  This code creates several threads.  Each thread accesses
  190.   -- some shared data (an integer) in a critical section.  A single lock
  191.   -- is used to control access to the shared variable.  Each thread locks
  192.   -- the mutex, computes a while, increments the integer, prints the new value,
  193.   -- updates the shared copy, and unlocks the mutex.  Then it does some
  194.   -- non-critical computation and repeats.
  195.  
  196.   var
  197.     myLock: Mutex = new Mutex      -- Could also use "Mutex2" instead
  198.     sharedInt: int = 0
  199.     thArr: array [7] of Thread = new array of Thread {7 of new Thread }
  200.  
  201.   function TestMutex ()
  202.       myLock.Init ()
  203.  
  204.       print ("\n-- You should see 70 lines, each consecutively numbered. --\n\n")
  205.  
  206.       thArr[0].Init ("LockTester-A")
  207.       thArr[0].Fork (LockTester, 100)
  208.  
  209.       thArr[1].Init ("LockTester-B")
  210.       thArr[1].Fork (LockTester, 200)
  211.  
  212.       thArr[2].Init ("LockTester-C")
  213.       thArr[2].Fork (LockTester, 1)
  214.  
  215.       thArr[3].Init ("LockTester-D")
  216.       thArr[3].Fork (LockTester, 50)
  217.  
  218.       thArr[4].Init ("LockTester-E")
  219.       thArr[4].Fork (LockTester, 300)
  220.  
  221.       thArr[5].Init ("LockTester-F")
  222.       thArr[5].Fork (LockTester, 1)
  223.  
  224.       thArr[6].Init ("LockTester-G")
  225.       thArr[6].Fork (LockTester, 1)
  226.  
  227.       ThreadFinish ()
  228.     endFunction
  229.  
  230.   function LockTester (waitTime: int)
  231.     -- This function will do the following actions, several times in a loop:
  232.     --     Lock the mutex
  233.     --     Get the current value of the "sharedInt" variable
  234.     --     Compute a new value by adding 1
  235.     --     Wait a while (determined by parameter "waitTime") to simulate
  236.     --        actions done within the critical section
  237.     --     Print the thread's name and the new value
  238.     --     Update the "sharedInt" variable
  239.     --     Unlock the mutex
  240.     --     Wait a while (determined by parameter "waitTime") to simulate
  241.     --        actions done outside of the critical section
  242.       var
  243.         i, j, k: int
  244.       for i = 1 to 10
  245.  
  246.         -- Enter
  247.         myLock.Lock()
  248.  
  249.         -- Critical Section
  250.         j = sharedInt + 1                    -- read shared data
  251.         for k = 1 to waitTime                -- do some computation
  252.         endFor                               --
  253.         printIntVar (currentThread.name, j)  -- print new data value
  254.         sharedInt = j                        -- update shared data
  255.  
  256.         -- Leave
  257.         myLock.Unlock()
  258.  
  259.         -- Perform non-critical work
  260.         for k = 1 to waitTime
  261.         endFor
  262.  
  263.       endFor
  264.     endFunction
  265.  
  266.  
  267.  
  268. -----------------------------  ProducerConsumer  ---------------------------------
  269.  
  270.   -- This code implements the consumer-producer task.  There are several
  271.   -- "producers", several "consumers", and a single shared buffer.
  272.   --
  273.   -- The producers are named "A", "B", "C", etc.  Each producer is a thread which
  274.   -- will loop 5 times.  For each iteration, the producer thread will add its
  275.   -- character to a shared buffer.  For example, "Producer-B" will add 5 "B"s to
  276.   -- the shared buffer.  Since the 5 producer threads will run concurrently, the
  277.   -- characters will be added in an unpredictable order.  Regardless of the order,
  278.   -- however, there will be five "A"s, five "B"s, five "C"s, etc.
  279.   --
  280.   -- There are several consumers.  Each consumer is a thread which executes an
  281.   -- inifinite loop.  During each iteration of its loop, a consumer will remove
  282.   -- whatever character is next in the buffer and will print it.
  283.   --
  284.   -- The shared buffer is a FIFO queue of characters.  The producers put characters
  285.   -- in one end and the consumers take characters out the other end.  Think of a
  286.   -- section of steel pipe.  The capacity of the buffer is limited to BUFFER_SIZE
  287.   -- characters.
  288.   --
  289.   -- This code illustrates the mechanisms required to synchronize the producers,
  290.   -- consumers, and the shared buffer.  Consumers must wait if the buffer is empty.
  291.   -- Producers must wait if the buffer is full.  Furthermore, the buffer is a shared
  292.   -- data structure.  (The buffer is implemented as an array with pointers to the
  293.   -- next position to add or remove characters.)  No two threads are allowed to
  294.   -- access these pointers simultaneously, or else errors may result.
  295.   --
  296.   -- To document what is happening, each producer will print a line when it adds
  297.   -- a character to the buffer.  The line printed will include the buffer contents
  298.   -- along with the name of the poducer.  Also, each time a consumer removes a
  299.   -- character from the buffer, it will print a line, showing the buffer contents
  300.   -- after the removal, along with the name of the consumer thread.  Each line of
  301.   -- output is formated so that you can see the buffer growing and shrinking.  By
  302.   -- reading the output vertically, you can also see what each thread does.
  303.   --
  304.   const
  305.     BUFFER_SIZE = 5
  306.  
  307.   var
  308.     buffer: array [BUFFER_SIZE] of char = new array of char {BUFFER_SIZE of '?'}
  309.     bufferSize: int = 0
  310.     bufferNextIn: int = 0
  311.     bufferNextOut: int = 0
  312.     thArray: array [8] of Thread = new array of Thread { 8 of new Thread }
  313.  
  314.   function ProducerConsumer ()
  315.  
  316.       print ("     ")
  317.  
  318.       thArray[0].Init ("Consumer-1                               |      ")
  319.       thArray[0].Fork (Consumer, 1)
  320.  
  321.       thArray[1].Init ("Consumer-2                               |          ")
  322.       thArray[1].Fork (Consumer, 2)
  323.  
  324.       thArray[2].Init ("Consumer-3                               |              ")
  325.       thArray[2].Fork (Consumer, 3)
  326.  
  327.       thArray[3].Init ("Producer-A         ")
  328.       thArray[3].Fork (Producer, 1)
  329.  
  330.       thArray[4].Init ("Producer-B             ")
  331.       thArray[4].Fork (Producer, 2)
  332.  
  333.       thArray[5].Init ("Producer-C                 ")
  334.       thArray[5].Fork (Producer, 3)
  335.  
  336.       thArray[6].Init ("Producer-D                     ")
  337.       thArray[6].Fork (Producer, 4)
  338.  
  339.       thArray[7].Init ("Producer-E                         ")
  340.       thArray[7].Fork (Producer, 5)
  341.  
  342.       ThreadFinish ()
  343.     endFunction
  344.  
  345.   function Producer (myId: int)
  346.       var
  347.         i: int
  348.         c: char = intToChar ('A' + myId - 1)
  349.       for i = 1 to 5
  350.         -- Perform synchroniztion...
  351.  
  352.         -- Add c to the buffer
  353.         buffer [bufferNextIn] = c
  354.         bufferNextIn = (bufferNextIn + 1) % BUFFER_SIZE
  355.         bufferSize = bufferSize + 1
  356.  
  357.         -- Print a line showing the state
  358.         PrintBuffer (c)
  359.  
  360.         -- Perform synchronization...
  361.  
  362.       endFor
  363.     endFunction
  364.  
  365.   function Consumer (myId: int)
  366.       var
  367.         c: char
  368.       while true
  369.         -- Perform synchroniztion...
  370.  
  371.         -- Remove next character from the buffer
  372.         c = buffer [bufferNextOut]
  373.         bufferNextOut = (bufferNextOut + 1) % BUFFER_SIZE
  374.         bufferSize = bufferSize - 1
  375.  
  376.         -- Print a line showing the state
  377.         PrintBuffer (c)
  378.  
  379.         -- Perform synchronization...
  380.  
  381.       endWhile
  382.     endFunction
  383.  
  384.   function PrintBuffer (c: char)
  385.     --
  386.     -- This method prints the buffer and what we are doing to it.  Each
  387.     -- line should have
  388.     --        <buffer>  <threadname> <character involved>
  389.     -- We want to print the buffer as it was *before* the operation;
  390.     -- however, this method is called *after* the buffer has been modified.
  391.     -- To achieve the right order, we print the operation first, skip to
  392.     -- the next line, and then print the buffer.  Assuming we start by
  393.     -- printing an empty buffer first, and we are willing to end the output
  394.     -- in the middle of a line, this prints things in the desired order.
  395.     --
  396.       var
  397.         i, j: int
  398.       -- Print the thread name, which tells what we are doing.
  399.       print ("   ")
  400.       print (currentThread.name)  -- Will include right number of spaces after name
  401.       printChar (c)
  402.       nl ()
  403.       -- Print the contents of the buffer.
  404.       j = bufferNextOut
  405.       for i = 1 to bufferSize
  406.         printChar (buffer[j])
  407.         j = (j + 1) % BUFFER_SIZE
  408.       endFor
  409.       -- Pad out with blanks to make things line up.
  410.       for i = 1 to BUFFER_SIZE-bufferSize
  411.         printChar (' ')
  412.       endFor
  413.     endFunction
  414.  
  415.  
  416.  
  417. -----------------------------  Dining Philosophers  ---------------------------------
  418.  
  419.   -- This code is an implementation of the Dining Philosophers problem.  Each
  420.   -- philosopher is simulated with a thread.  Each philosopher thinks for a while
  421.   -- and then wants to eat.  Before eating, he must pick up both his forks.
  422.   -- After eating, he puts down his forks.  Each fork is shared between
  423.   -- two philosophers and there are 5 philosophers and 5 forks arranged in a
  424.   -- circle.
  425.   --
  426.   -- Since the forks are shared, access to them is controlled by a monitor
  427.   -- called "ForkMonitor".  The monitor is an object with two "entry" methods:
  428.   --     PickupForks (phil)
  429.   --     PutDownForks (phil)
  430.   -- The philsophers are numbered 0 to 4 and each of these methods is passed an integer
  431.   -- indicating which philospher wants to pickup (or put down) the forks.
  432.   -- The call to "PickUpForks" will wait until both of his forks are
  433.   -- available.  The call to "PutDownForks" will never wait and may also
  434.   -- wake up threads (i.e., philosophers) who are waiting.
  435.   --
  436.   -- Each philospher is in exactly one state: HUNGRY, EATING, or THINKING.  Each time
  437.   -- a philosopher's state changes, a line of output is printed.  The output is organized
  438.   -- so that each philosopher has column of output with the following code letters:
  439.   --           E    --  eating
  440.   --           .    --  thinking
  441.   --         blank  --  hungry (i.e., waiting for forks)
  442.   -- By reading down a column, you can see the history of a philosopher.
  443.   --
  444.   -- The forks are not modeled explicitly.  A fork is only picked up
  445.   -- by a philospher if he can pick up both forks at the same time and begin
  446.   -- eating.  To know whether a fork is available, it is sufficient to simply
  447.   -- look at the status's of the two adjacent philosophers.  (Another way to state
  448.   -- the problem is to forget about the forks altogether and stipulate that a
  449.   -- philosopher may only eat when his two neighbors are not eating.)
  450.  
  451.   enum HUNGRY, EATING, THINKING
  452.   var
  453.     mon: ForkMonitor
  454.     philospher: array [5] of Thread = new array of Thread {5 of new Thread }
  455.  
  456.   function DiningPhilosophers ()
  457.  
  458.       print ("Plato\n")
  459.       print ("    Sartre\n")
  460.       print ("        Kant\n")
  461.       print ("            Nietzsche\n")
  462.       print ("                Aristotle\n")
  463.  
  464.       mon = new ForkMonitor
  465.       mon.Init ()
  466.       mon.PrintAllStatus ()
  467.  
  468.       philospher[0].Init ("Plato")
  469.       philospher[0].Fork (PhilosphizeAndEat, 0)
  470.  
  471.       philospher[1].Init ("Sartre")
  472.       philospher[1].Fork (PhilosphizeAndEat, 1)
  473.  
  474.       philospher[2].Init ("Kant")
  475.       philospher[2].Fork (PhilosphizeAndEat, 2)
  476.  
  477.       philospher[3].Init ("Nietzsche")
  478.       philospher[3].Fork (PhilosphizeAndEat, 3)
  479.  
  480.       philospher[4].Init ("Aristotle")
  481.       philospher[4].Fork (PhilosphizeAndEat, 4)
  482.  
  483.      endFunction
  484.  
  485.   function PhilosphizeAndEat (p: int)
  486.     -- The parameter "p" identifies which philosopher this is.
  487.     -- In a loop, he will think, acquire his forks, eat, and
  488.     -- put down his forks.
  489.       var
  490.         i: int
  491.       for i = 1 to 7
  492.         -- Now he is thinking
  493.         mon. PickupForks (p)
  494.         -- Now he is eating
  495.         mon. PutDownForks (p)
  496.       endFor
  497.     endFunction
  498.  
  499.   class ForkMonitor
  500.     superclass Object
  501.     fields
  502.       status: array [5] of int             -- For each philosopher: HUNGRY, EATING, or THINKING
  503.     methods
  504.       Init ()
  505.       PickupForks (p: int)
  506.       PutDownForks (p: int)
  507.       PrintAllStatus ()
  508.   endClass
  509.  
  510.   behavior ForkMonitor
  511.  
  512.     method Init ()
  513.       -- Initialize so that all philosophers are THINKING.
  514.       -- ...unimplemented...
  515.       endMethod
  516.  
  517.     method PickupForks (p: int)
  518.       -- This method is called when philosopher 'p' is wants to eat.
  519.       -- ...unimplemented...
  520.       endMethod
  521.  
  522.     method PutDownForks (p: int)
  523.       -- This method is called when the philosopher 'p' is done eating.
  524.       -- ...unimplemented...
  525.       endMethod
  526.  
  527.     method PrintAllStatus ()
  528.       -- Print a single line showing the status of all philosophers.
  529.       --      '.' means thinking
  530.       --      ' ' means hungry
  531.       --      'E' means eating
  532.       -- Note that this method is internal to the monitor.  Thus, when
  533.       -- it is called, the monitor lock will already have been acquired
  534.       -- by the thread.  Therefore, this method can never be re-entered,
  535.       -- since only one thread at a time may execute within the monitor.
  536.       -- Consequently, printing is safe.  This method calls the "print"
  537.       -- routine several times to print a single line, but these will all
  538.       -- happen without interuption.
  539.         var
  540.           p: int
  541.         for p = 0 to 4
  542.           switch status [p]
  543.             case HUNGRY:
  544.               print ("    ")
  545.               break
  546.             case EATING:
  547.               print ("E   ")
  548.               break
  549.             case THINKING:
  550.               print (".   ")
  551.               break
  552.           endSwitch
  553.         endFor
  554.         nl ()
  555.       endMethod
  556.  
  557.   endBehavior
  558.  
  559. endCode
  560.