// $Id: threadtest.cc,v 1.15 2008/04/15 04:17:45 djb4p Exp $ // ====================================================================== // threadtest.cc // Simple test case for the threads assignment. // // Create two threads, and have them context switch // back and forth between themselves by calling Thread::Yield, // to illustratethe inner workings of the thread system. // // Copyright (c) 1992-1993 The Regents of the University of California. // All rights reserved. See copyright.h for copyright notice and limitation // of liability and disclaimer of warranty provisions. #include #include "copyright.h" #include "system.h" #include "synch.h" int testnum; /* Set in main.cc */ //======================= ASSIGNMENT 1 ================================= //---------------------------------------------------------------------- // SimpleThread (testnum = 0) // Loop 5 times, yielding the CPU to another ready thread // each iteration. // // "which" is simply a number identifying the thread, for debugging // purposes. //---------------------------------------------------------------------- void SimpleThread(int which) { int num; for (num = 0; num < 5; num++) { printf("*** thread %d looped %d times\n", which, num); fflush(stdout); currentThread->Yield(); } } //---------------------------------------------------------------------- // ThreadTest0 // Set up a ping-pong between two threads, by forking a thread // to call SimpleThread, and then calling SimpleThread ourselves. //---------------------------------------------------------------------- void ThreadTest0() { DEBUG('t', "Entering ThreadTest0()\n"); Thread *t = new Thread("forked thread"); t->Fork(SimpleThread, 1); SimpleThread(0); } //======================= ASSIGNMENT 2 ================================= //---------------------------------------------------------------------- // Producer/Consumer problem (testnum = 1) //---------------------------------------------------------------------- Condition cOK2Read("OK to Read"); Condition cOK2Write("OK to Write"); Lock lock("the lock"); const int BUF_SIZE = 4; char buf[BUF_SIZE]; int bufIN = 0; // bufIN % BUF_SIZE is index of where to produce int bufOUT = 0; // bufOUT % BUF_SIZE is index of where to consume int count = 0; // # of elements in buffer that haven't been consumed void Producer(int dummy) { int inputIdx = 0; char input[] = "Hello world\n"; int i, j, k; char s[8]; k = sizeof(input); DEBUG('p', "Producer(%d): sizeof(input)=%d\n", dummy, k); // Loop several times through the input string to produce more activity while(inputIdx < (int)(3 *k)) { lock.Acquire(); while(count == BUF_SIZE) { cOK2Write.Wait(&lock); } i = inputIdx % sizeof(input); j = bufIN % BUF_SIZE; buf[j] = input[i]; // Convert newline and NUL characters to printable form for debugging if( buf[j] == '\n') { strcpy(s, "\\n"); } else if (buf[j] == '\0') { strcpy(s, "\\0"); } else { s[0] = buf[j]; s[1] = '\0'; } DEBUG('p', "Producer(%d) put char '%s' @ pos %d\n", dummy, s, j); inputIdx++; bufIN++; count++; cOK2Read.Signal(&lock); lock.Release(); } } void Consumer(int dummy) { int k; char s[8]; while(1) { lock.Acquire(); while(count == 0 ) { cOK2Read.Wait(&lock); } k = bufOUT % BUF_SIZE; // Convert newline and NUL characters to printable form for debugging if (buf[k] == '\n') { strcpy(s, "\\n"); } else if (buf[k] == '\0') { strcpy(s, "\\0"); } else { s[0] = buf[k]; s[1] = '\0'; } DEBUG('c', "Consumer(%d) got char '%s' @ pos %d\n", dummy, s, k); // Output received character: print only non-NUL characters if (buf[k] != '\0') { printf("%c",buf[k]); fflush(stdout); } DEBUG('c', "\n"); bufOUT++; count--; cOK2Write.Signal(&lock); lock.Release(); } } //---------------------------------------------------------------------- // ThreadTest1: Test driver for Producer/Consumer problem //---------------------------------------------------------------------- void ThreadTest1() { DEBUG('t', "Entering ThreadTest1()\n"); int i; const int np = 3; // Number of producers const int nc = 3; // Number of consumers Thread *tp[np]; // Producers thread array Thread *tc[nc]; // Consumers thread array // Start the producers for (i=0; iFork(Producer, i+1); } // Start the consumers for (i=0; iFork(Consumer, i+1); } // Final thread to hold control while other thread run Producer(np+1); } //---------------------------------------------------------------------- // VDOT Bridge problem (ThreadTest2) //---------------------------------------------------------------------- static int cars_onbridge[2] = { 0, 0 }; Lock mutex("the mutex"); Condition cCross("the condition"); const int MAX_CARS = 3; static int curr_direc = 0; void ArriveBridge(int direc) { DEBUG('b', "Entering ArriveBridge(%d)\n", direc); printf("%s arriving @ bridge, direc=%d\n", currentThread->getName(), direc); fflush(stdout); // Loop until car can proceed while(1) { // Lock critical section mutex.Acquire(); DEBUG('b', "ArriveBridge: %s got mutex\n", currentThread->getName()); // Can proceed if this car is moving in current direction and // there are fewer than MAX_CARS cars on the bridge // - OR - // car is moving in opposite of current direction and // there are no cars on the bridge if ((direc == curr_direc && cars_onbridge[direc] < MAX_CARS) || (direc != curr_direc && cars_onbridge[!direc] == 0)) { cars_onbridge[direc]++; curr_direc = direc; // Unlock at end of critical section mutex.Release(); return; } // Otherwise, wait until a car exits the bridge and try again else { DEBUG('b', "ArriveBridge: %s waiting, holding mutex\n", currentThread->getName()); printf("%s waiting for bridge, direc=%d\n", currentThread->getName(), direc); fflush(stdout); // N.B. We've held onto the lock acquired above cCross.Wait(&mutex); DEBUG('b', "ArriveBridge: %s wakeup, releasing mutex\n", currentThread->getName()); // Still have lock (reacquired), release it mutex.Release(); } } } void CrossBridge(int direc) { DEBUG('b', "Entering CrossBridge(%d)\n", direc); printf("%s crossing bridge, direc=%d\t", currentThread->getName(), direc); printf("cars on bridge: direc=0: %d direc=1: %d\n", cars_onbridge[0], cars_onbridge[1]); fflush(stdout); } void ExitBridge(int direc) { DEBUG('b', "Entering Exitbridge(%d)\n", direc); // Lock critical section mutex.Acquire(); // Decrement car count for current direction cars_onbridge[direc]--; printf("%s exiting bridge, direc=%d \n", currentThread->getName(), direc); fflush(stdout); // Notify all waiters that a car has exited bridge // N.B. Broadcast instead of Signal to get as many cars moving as possible cCross.Broadcast(&mutex); // Unlock at end of critical section mutex.Release(); } void OneVehicle(int direc) { DEBUG('b', "Entering OneVehicle(%d) for %s\n", direc, currentThread->getName()); DEBUG('b', "Calling ArriveBridge(%d) for %s\n", direc, currentThread->getName()); ArriveBridge(direc); DEBUG('b', "Calling CrossBrige(%d) for %s\n", direc, currentThread->getName()); CrossBridge(direc); DEBUG('b', "Calling ExitBridge(%d) for %s\n", direc, currentThread->getName()); ExitBridge(direc); } //---------------------------------------------------------------------- // ThreadTest2: Test driver for VDOT Bridge problem //---------------------------------------------------------------------- void ThreadTest2() { DEBUG('t', "Entering Threadtest2\n"); int i; // Loop counter unsigned int dirseed = 0xf8475387; // String of bits to use for car directions // Define a car info structure struct car { Thread *cthread; char cname[20]; int cdir; }; // Generate the cars // // Note: It takes about 20 cars to see the bridge fill up // with this algorithm const int ncars = 20; struct car cars[ncars]; for (i=0; i> i) & 1; cars[i].cdir = dir; // Set car name sprintf(cbuf, "car%d", i+1); strcpy(cars[i].cname, cbuf); DEBUG('b', "Creating %s, dir=%d\n", cars[i].cname, cars[i].cdir); } // Begin forking threads printf("Total number of cars: %d\n", ncars); fflush(stdout); for (i=0; iFork(OneVehicle, cars[i].cdir); } // Wait for threads to complete DEBUG('b', "ThreadTest2(): Turning off interrupts\n"); interrupt->SetLevel(IntOff); DEBUG('b', "ThreadTest2(): Going to sleep\n"); currentThread->Sleep(); } //======================= COMMON TO ALL ASSIGNMENTS ==================== //---------------------------------------------------------------------- // ThreadTest: Common test driver for all Nachos Assignment problems // // Select test by setting 'testnum' on the nachos command-line (-q #) //---------------------------------------------------------------------- void ThreadTest() { printf("Entering ThreadTest(), testnum=%d\n", testnum); fflush(stdout); switch (testnum) { case 0: printf("===> Running ThreadTest%d()\n", testnum); fflush(stdout); ThreadTest0(); break; case 1: printf("===> Running ThreadTest%d()\n", testnum); fflush(stdout); ThreadTest1(); break; case 2: printf("===> Running ThreadTest%d()\n", testnum); fflush(stdout); ThreadTest2(); break; default: printf("****************************************\n"); printf("* ThreadTest: unknown test (testnum=%d)\n", testnum); printf("****************************************\n"); fflush(stdout); break; } }