// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: testprog.cpp
// Compiler Used: MSVC40, egcs-2.90.29, HP CPP 10.24
// Produced By: Doug Gaer   
// File Creation Date: 03/25/2000
// Date Last Modified: 05/26/2000
// Copyright (c) 2000 Douglas M. Gaer
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
The VBD and VBS C++ classes are copyright (c) 1997 and 2000 by 
Douglas M. Gaer. All those who put this code or its derivatives 
in a commercial product MUST mention this copyright in their
documentation for users of the products in which this code or its 
derivative classes are used. Otherwise, you have the freedom to 
redistribute verbatim copies of this source code, adapt it to your 
specific needs, or improve the code and release your improvements 
to the public provided that the modified files carry prominent 
notices stating that you changed the files and the date of any
change.

THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
CORRECTION.

Test program demonstrating the basic operation of the vbThread
class using mutex and condition variable synchronization 
primitives.
*/
// ----------------------------------------------------------- //   
#include <iostream.h>
#include "vbthread.h"
#include "vbmutex.h"
#include "vbcond.h"

// Constants
const int NUMTHREADS = 2;

// Global synchronization objects
vbMutex dataMutex;
vbCondition dataPresentCondition;

// Global variables
int dataPresent = 0;
int sharedData = 0;
 
class ConsumerThread : public vbThread
{
private: // Base class interface
  void *ThreadEntryRoutine(vbThread_t *thread);
};

void *ConsumerThread::ThreadEntryRoutine(vbThread_t *thread)
// Thread's entry function
{
  int retries=2;
 
  cout << "Entering consumer thread" << endl;
  if(dataMutex.MutexLock() != 0) {
    cout << "Consumer thread mutex lock failed!" << endl;
    cout << dataMutex.MutexExceptionMessage() << endl;
    return ExitThread(thread, 1);
  }
  
  while(retries--) {
    // The boolean dataPresent value is required for safe use of
    // condition variables. If no data is present we wait, other 
    // wise we process immediately.
    while(!dataPresent) {
      cout << "Consumer thread waiting for data to be produced" << endl;
      if(dataPresentCondition.ConditionWait(&dataMutex) != 0) {
	cout << "Consumer thread condition wait failed!" << endl;
	cout << dataPresentCondition.ConditionExceptionMessage() << endl;
	dataMutex.MutexUnlock();
	return ExitThread(thread, 1);
      }
    }

    cout << "Consumer thread found data or notified" << endl;
    cout << "CONSUME DATA while holding lock" << endl;

    // Typically an application should remove the data from being 
    // in the shared structure or Queue, then unlock. Processing 
    // of the data does not necessarily require that the lock is held
    // Access to shared data goes here
    --sharedData;
      
    // We consumed the last of the data 
    if(sharedData == 0) dataPresent=0;

    // Repeat holding the lock.
    // The vbCondition::ConditionWait() function releases it atomically 
  }

  cout << "Consumer thread done" << endl;

  if(dataMutex.MutexUnlock() != 0) {
    cout << "Consumer thread mutex unlock failed!" << endl;
    cout << dataMutex.MutexExceptionMessage() << endl;
    return ExitThread(thread, 1);
  }

  return 0;
}
  
// Program's main thread of execution
int main()
{
  ConsumerThread t;
  vbThread_t *thread[NUMTHREADS];

  int amountOfData = 4;
  int i;
 
  cout << "Create/start threads" << endl;
  for(i = 0; i < NUMTHREADS; ++i) {
    thread[i] = t.CreateThread();
    if(thread[i]->GetThreadError() != vbTHREAD_NO_ERROR)
      cout << thread[i]->ThreadExceptionMessage() << endl;
   }
 
  // The producer loop 
  while(amountOfData--) {
    cout << "Producer finding data..." << endl;
    t.sSleep(1);
    
    if(dataMutex.MutexLock() != 0) { // Protect shared data and flag
      cout << "Producer thread mutex lock failed!" << endl;
      cout << dataMutex.MutexExceptionMessage() << endl;
    }
    
    cout << "Producer thread make data shared and notify consumer" << endl;
    ++sharedData;    // Add data
    dataPresent = 1; // Set boolean predicate
    
    // Wake up a consumer
    if(dataPresentCondition.ConditionSignal() != 0) {
      cout << "Producer thread condition signal failed!" << endl;
      cout << dataPresentCondition.ConditionExceptionMessage() << endl;
      dataMutex.MutexUnlock();
      return 1;
    }
 
    cout << "Producer thread unlock shared data and flag" << endl;
    if(dataMutex.MutexUnlock() != 0) {
      cout << "Producer thread mutex unlock failed!" << endl;
      cout << dataMutex.MutexExceptionMessage() << endl;
    }
  }
 
  cout << "Wait for the threads to complete, and release their resources"
       << endl;

  for(i = 0; i < NUMTHREADS; i++) {
    if(t.JoinThread(thread[i]) != 0)
      cout << "Could not join the thread" << endl;
    if(thread[i]->GetThreadExitCode() != 0)
      cout << "This thread exited with an error" << endl;
  }
 
  return 0;
}
// ----------------------------------------------------------- // 
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //