// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- // 
// C++ Source Code File Name: testprog.cpp
// Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: DataReel Software Development Team
// File Creation Date: 03/25/2000
// Date Last Modified: 06/17/2016
// Copyright (c) 2001-2024 DataReel Software Development
// ----------------------------------------------------------- // 
// ------------- Program Description and Details ------------- // 
// ----------------------------------------------------------- // 
/*
This library is free software; you can redistribute it and/or 
modify it under the terms of the GNU Lesser General Public 
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version. 
  
This library is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
USA

Test program used to the basic functionality of the Thread Pool
class.
*/
// ----------------------------------------------------------- //   
#include "gxdlcode.h"

#if defined (__USE_ANSI_CPP__) // Use the ANSI Standard C++ library
#include <iostream>
using namespace std; // Use unqualified names for Standard C++ library
#else // Use the old iostream library by default
#include <iostream.h>
#endif // __USE_ANSI_CPP__

#include "thrpool.h"
#include "gthreadt.h"

#ifdef __MSVC_DEBUG__
#include "leaktest.h"
#endif

void PausePrg()
// Pause the program and wait for the user to press enter.
{
  // Pause the program until enter is pressed
  cout << "\n" << flush;
  cout << "Press Enter to continue..." << "\n" << flush;
  cin.get();
}

void ScrollForward(thrPool &pool)
{
  cout << "Walking through the pool..." << "\n" << flush;
  thrPoolNode *ptr = pool.GetHead();
  while(ptr) {
    cout << (char)ptr->GetThreadPtr()->GetObjectID() << ' ';
    ptr = ptr->GetNext();
  } 
  cout << "\n" << flush;
}

void RewindPool(thrPool &pool)
{
  cout << "Walking through the pool in a backward direction..." 
       << "\n" << flush;
  thrPoolNode *ptr = pool.GetTail();

  // Walk through the pool in a backward direction
  while(ptr) {
    cout << (char)ptr->GetThreadPtr()->GetObjectID() << ' ';
    ptr = ptr->GetPrev();
  } 

  cout << "\n" << flush;
}

int main()
{
#ifdef __MSVC_DEBUG__
  InitLeakTest();
#endif

  thrPool pool;
  thrPoolNode *ptr;

  cout << "Loading data elements into the pool..." << "\n" << flush;
  thrPoolNode *na = new thrPoolNode;
  thrPoolNode *nb = new thrPoolNode;
  thrPoolNode *nc = new thrPoolNode;
  thrPoolNode *nd = new thrPoolNode;
  thrPoolNode *n1 = new thrPoolNode;
  thrPoolNode *n2 = new thrPoolNode;
  thrPoolNode *n3 = new thrPoolNode;
  thrPoolNode *n4 = new thrPoolNode;

  // Allocating some memory buffers
  const unsigned num_blocks = 9;
  gxThread_t *buf[num_blocks];
  unsigned i;
  for(i = 0; i < num_blocks; i++) {
    buf[i] = new gxThread_t;
    // Set an arbitary object ID stating starting with ASCII character 'A'
    buf[i]->SetObjectID(65+i);
  }
  
  na->SetThreadPtr(buf[0]);
  nb->SetThreadPtr(buf[1]);
  nc->SetThreadPtr(buf[2]);
  nd->SetThreadPtr(buf[3]);

  // Load some pool elements
  pool.InsertAfter(pool.GetHead(), na);
  pool.InsertAfter(na, nb);
  pool.InsertAfter(nb, nc);
  pool.InsertAfter(nc, nd);
  
  n1->SetThreadPtr(buf[4]);
  n2->SetThreadPtr(buf[5]);
  n3->SetThreadPtr(buf[6]);
  n4->SetThreadPtr(buf[7]);
  
  // Testing insert functions
  pool.InsertBefore(na, n1);
  pool.InsertBefore(nc, n2);
  pool.InsertAfter(nb, n3);
  pool.InsertAfter(nd, n4);

  // Reordering the pool
  pool.MoveToBack(na);
  pool.MoveAfter(na, nc);
  pool.MoveBefore(nc, nb);
  pool.MoveAfter(nc, nd);
  pool.MoveAfter(nd, n2);
  pool.MoveBefore(n2, n1);
  pool.MoveAfter(n2, n3);
  pool.MoveAfter(n3, n4);
  
  ScrollForward(pool);
  RewindPool(pool);

  PausePrg();

  cout << "Adding another item to the pool..." << "\n" << flush;
  ptr = pool.AddThread(buf[8]);
  ScrollForward(pool);

  PausePrg();

  cout << "Moving pool item to the front of the pool" << "\n" << flush;
  pool.MoveToFront(ptr);
  ScrollForward(pool);

  cout << "Moving item to the back of the pool..." << "\n" << flush;
  pool.MoveToBack(ptr);
  ScrollForward(pool);
  
  PausePrg();
  
  cout << "Removing the head of the pool..." << "\n" << flush;
  ptr = pool.GetHead();
  gxThread_t *tptr = pool.RemoveNode(ptr);
  if(tptr) delete tptr; // Prevent memory leaks
  ScrollForward(pool);

  cout << "Removing the tail of the pool..." << "\n" << flush;
  ptr = pool.GetTail();
  tptr = pool.RemoveNode(ptr);
  if(tptr) delete tptr; // Prevent memory leaks
  ScrollForward(pool);

  PausePrg();
  cout << "Testing for memory leaks..." << "\n" << flush;
  unsigned len = sizeof(gxThread_t);
  const unsigned num_to_allocate = 1000; // 1000 * 1000;
  const unsigned mem_bufs = (sizeof(thrPoolNode) + len) * num_to_allocate;
  cout << "Allocating " << mem_bufs << " bytes..." << "\n" << flush;

  // Clear the thread pool without checking the status of each thread
  pool.ClearPool();

  unsigned count1 = 0;
  unsigned count2 = 0;
  for(i = 0; i < num_to_allocate; ++i) {
    gxThread_t *sbuf = new gxThread_t;
    count1 += sizeof(sbuf);
    if(!sbuf) {
      cout << "Memory allocation error at " << (count1+count2) << " bytes"
	   << "\n" << flush;
      break;
    }
    
    count2 += sizeof(thrPoolNode);
    if(!pool.AddThread(sbuf))  {
      cout << "Memory allocation error at " << (count1+count2) << " bytes"
	   << "\n" << flush;
      break;
    }
  }
  cout << "Done" << "\n" << flush;
  PausePrg();

  cout << "Releasing memory allocated back to the heap..." << "\n" << flush;

  while(!pool.IsEmpty()) {
    // Clear the pool, giving the application a chance to check the
    // status of each thread.
    gxThread_t *t = pool.RemoveTail();
    if(t) {
      // Process the thread and release any memory the thread is holding
      // ...
      delete t;
    }
  }
  pool.MakeEmpty(); // The thread pool is no empty
  
  PausePrg();
  
  cout << "Exiting..." << "\n" << flush;
  cout << "\n" << flush;
  return 0;
}
// ----------------------------------------------------------- // 
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //