SOURCE FILE: sequence.cpp

To make a copy of sequence.cpp, just select the text in-between the spacer lines below and copy it to your 'clipboard'. Then make a new file and paste the text into it. Name the file "sequence.cpp" - exactly that name.


/*
---------------0---------------
-------3--------------5--------
-------------------------------
-------------------------------
-----------1-------2-----------
-------------------------------
-------------------------------
---------------4---------------

Constraints: 
* Threads 0, 1, and 2 should proceed to complete their work
  as soon as possible after they are created. 
* Thread 3 must wait for threads 0 and 1 to finish before
  thread 3 begins its work.  However, thread 3 should begin
  and complete its work as soon as possible after threads 0 
  and 1 are finished. 
* The constraints for threads 4 and 5 are like those for thread
  3, except that thread 4 waits for threads 1 and 2 to finish
  before starting, and thread 5 waits for threads 0 and 2.

  Also, each child thread increments the counter called 
  thrds_finished just before exiting.  (Note that access to
  this counter is a critical section problem.) The mother thread 
  reports the value of thrds_finished just before the program stops.
*/

/*  Thread Sequencing Program   */

#include <iostream>
#include <sched.h>
#include <time.h>
#include <pthread.h>
#include "sem.h"

#define THREAD_COUNT 6

using namespace std;

extern sim_semaphore create_sim_sem(int) ;
extern void wait_sem (sim_semaphore) ;
extern void signal_sem (sim_semaphore) ;

      /* For technical reasons, we use the pthread mutex below,
         instead of a semaphore, to lock the screen for output.  Don't
         change this.  */

pthread_mutex_t stdoutLock ;

   /* How many child threads have finished their assignments */
int thrds_finished ;

     /* Here declare whatever shared variables you need for
        synchronization. Variables declared here will be visible
        to (shared by) all the threads in the task. */
   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */ 


   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */ 

     /* These are global variable to represent threads created
        dynamically. */
pthread_t thr[THREAD_COUNT] ; 

    /* This is included to facilitate adding random delays in the code
       -- as a debugging aid. */
extern long random(void);  

   /* This can be changed to 1, but the resulting diagnostic output
      will probably seem excessive. */
int checking = 0 ;

     /* A data type - a struct (class) with an int field to represent
        a thread ID. */
struct threadIdType
{
  int id ;
};

/* ################################################## */
/*                         init                       */
/* ################################################## */
void init() 
{ 
      /* This code initializes special mutex lock for screen output.
         Just leave this alone. */
  if ( 0!=pthread_mutex_init(&stdoutLock, NULL) )
  {  cout << "MUTEX INITIALIZATION FAILURE!" << endl ;
     exit(-1) ;}

    thrds_finished = 0 ;

    /* Here insert the code that you want to initialize
       the shared variables that you are using
       for synchronization. */
   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */






   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */

       /* This initializes a random number generator */ 
  srandom(time((time_t *) 0));
}

/* ################################################## */
/*                     rand_delay                     */
/* ################################################## */
void rand_delay(int max) 
{
  int m_delay, j ;
  m_delay = (int) random()%max ;
  for (j=0; j<m_delay; j++) sched_yield();  
}

/* ################################################## */
/*                childMessage                        */
/* ################################################## */
void childMessage (int ID)
{
   pthread_mutex_lock(&stdoutLock) ;
   cout << "Child # " << ID 
        << " has completed its assignment.\n" ;
   pthread_mutex_unlock(&stdoutLock) ;
}

/* ################################################## */
/*                        child                       */
/* ################################################## */

void * child(void * idPtr) 
{
       /* This is just a change of data type for convenience.  Now
          'me' is the number of the child.  Children have numbers from
          0 to THREAD_COUNT-1. */
   int me = ((threadIdType *) (idPtr))->id, temp ;

       /* Below is delay code inserted here to 'stress test' the program - 
          simulating delay that might be caused by interrupts,
          overloaded ready queues, and so forth.  Random delays
          can affect relative executions rates of the threads.  If the
          synchronization in the program is right, the random delays 
          will not be able to cause the program to exectute improperly.
          The student working on the program should consider inserting 
          other delays to further 'stress test'.  */

   rand_delay(100) ;

       /* Here put whatever synchronization code is required prior
          to the code where the child process declares that it has
          finished its assigned task. */
   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */




   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
          /* Simulate the time it takes to perform the assigned work. */
   rand_delay(1000) ;

          /* Next increment thrds_finished - to keep track
             of how many child processes have finished their
             tasks. */

          /* Here put code you may need for protecting
             thrds_finished. */
        /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */



        /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
             /* This code increments thrds_finished in a way
                that magnifies the problem if the critical section
                problem is not solved correctly.  Of course,
                do not change this part of the code. */
       temp = thrds_finished ;
       rand_delay(1000) ; 
       temp++ ;
       rand_delay(1000) ; 
       thrds_finished = temp ;

          /* Here put additional code you may need for protecting
             thrds_finished. */
        /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */




        /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */
          /* Announce completion of the assignment. */
   childMessage(me) ;  

       /* Here put whatever synchronization code is required AFTER
          the code where the child process declares that it has
          finished its assigned task. */
   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */




   /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */ 

   pthread_exit ((void *)0) ;
}

/* ################################################## */
/*                       mother                       */
/* ################################################## */

/* The mother spawns child threads and then waits for
   them all to finish.  The mother's waiting has to be
   implemented as part of the overall protocol - using
   the sim_semaphore data type. */

void mother() 
{ 
   int i; 

       /* This is a pointer to a struct (class) that contains an int
          field - it is a convenient data type to use as the parameter
          to the child function.  */
   threadIdType * idPtr ; 

        /* Create child threads numbered 1 through 8. */

   pthread_mutex_lock(&stdoutLock) ;
   cout << "Mother Begins Spawning Threads.\n"   << endl ;
   pthread_mutex_unlock(&stdoutLock) ;

   for (i = 0; i < THREAD_COUNT ; i++) 
   {
      idPtr = new threadIdType ; /* allocate memory for struct */
      idPtr->id = i ;  /* records current index as the child's ID */

         /* The call below is what actually creates the child thread
            and passes a pointer to the struct 'idPtr' as the
            parameter to the child function. */

      if ( 0!=pthread_create(&thr[i], NULL, child, (void *) idPtr) )
      {  pthread_mutex_lock(&stdoutLock) ; /* 'error out' code here */
         cout << "THREAD CREATION FAILURE!" << endl ;
         pthread_mutex_unlock(&stdoutLock) ;
         exit(-1) ; }

         /* The call to pthread_detach() below marks a child 
            thread as 'detached' so that the system will not 
            expect the parent to 'join' (wait for) it. */     

      if (0!=pthread_detach(thr[i]))
      {  pthread_mutex_lock(&stdoutLock) ;/* 'error out' code here */
         cout << "THREAD DETACHMENT FAILURE!" << endl ;
         pthread_mutex_unlock(&stdoutLock) ;
         exit(-1) ;}
   }

   rand_delay(3000) ; /* Simulate whatever may delay the mother thread */

   pthread_mutex_lock(&stdoutLock) ;
   cout << "Mother Finishes Spawning Threads.\n"   << endl ;
   pthread_mutex_unlock(&stdoutLock) ;

      /* Some synchronization code is needed here to make the mother
         behave correctly - she is not supposed to exit until after
         all the children have finished their tasks. */

      /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */ 



      /* HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH */ 

   pthread_mutex_lock(&stdoutLock) ;
   cout << "\nAll children have now reported that they finished.\n" ;
   cout << "The value of the thrds_finished counter is: " ;
   cout << thrds_finished << ".\n" ;
   if (thrds_finished != THREAD_COUNT)
   cout << "BAD COUNTER VALUE!! - Critical Section Problem Failure!!\n" ;
   cout << "Mother exiting ... \n\n" ;
   pthread_mutex_unlock(&stdoutLock) ;
   return ;
}

/* ################################################## */
/*                         main                       */
/* ################################################## */

int main() 
{ 

   cout << "\nWelcome to The Child Thread Workplace!\n\n" ;

     /* This calls the function that performs initializations. */
  init(); 

        /* Execute the mother() function */ 
  mother();

  return 0 ;
}