// ------------------------------- //
// -------- 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: 05/17/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.

This is a test program used demonstrate the use of the VBD file
manager in a multi-threaded application.
*/
// ----------------------------------------------------------- //   
#include <iostream.h>
#include <string.h>
#include "vbthread.h"
#include "vbmutex.h"
#include "vbcond.h"
#include "vbdfile.h"

// Constants
const int NUM_THREADS = 26;
const int name_length = 16;
const char *name_string = "File Object ";

struct FileObject {
  char name[name_length];
  int id;
};

// Class used to perform multi-threaded reads
class vbdReadThread : public vbThread
{
public:
  vbdReadThread(VBDFilePtr vbdfile) { f = vbdfile; curr_offset = (FAU)0; }
  ~vbdReadThread() { }

private: // Base class interface
  void *ThreadEntryRoutine(vbThread_t *thread);

private:
  VBDFilePtr f;         // Pointer to the open database file
  vbMutex offset_lock;  // Mutex used to serialize access to curr_offset
  FAU curr_offset;      // Current file position following a file read
};

// Class used to perform multi-threaded writes
class vbdWriteThread : public vbThread
{
public:
  vbdWriteThread(VBDFilePtr vbdfile) { f = vbdfile; is_locked = 0; }
  ~vbdWriteThread() { }

private: // Base class interface
  void *ThreadEntryRoutine(vbThread_t *thread);

private:
  vbMutex write_lock;     // Mutex object used to lock the file
  vbCondition write_cond; // Condition variable used to block other threads
  int is_locked;          // Variable used to indicate that the file is locked
  VBDFilePtr f;           // Pointer to the open database file
};

void *vbdReadThread::ThreadEntryRoutine(vbThread_t *thread)
{
  offset_lock.MutexLock(); // Serialize access to curr_offset
  curr_offset = f->FindFirstObject(curr_offset);
  if(curr_offset) {
    FileObject ob;
    f->Read(&ob, sizeof(FileObject), curr_offset);
    cout << "Reading: \"" << ob.name << "\" at address: " << (long)curr_offset 
	 << "\n" << flush;
  }
  offset_lock.MutexUnlock();

  return 0;
}

void *vbdWriteThread::ThreadEntryRoutine(vbThread_t *thread)
// Thread safe write function that will not allow access to
// the critical section until the write operation is complete.
{
  FileObject *ob = (FileObject *)thread->GetThreadParm();

  write_lock.MutexLock();
  while(is_locked) {
    // Block this thread from its own execution if a another thread
    // is writing to the file
    write_cond.ConditionWait(&write_lock);
  }

  is_locked = 1; // Tell other threads to wait until this write is complete

  // ********** Enter Critical Section ******************* //
  f->Alloc(sizeof(FileObject));
  f->Write(ob, sizeof(FileObject));
  // ********** Leave Critical Section ******************* //

  is_locked = 0; // Tell other threads that this write is complete
 
  // Wake up the next thread waiting on this condition
  write_cond.ConditionSignal();
  write_lock.MutexUnlock();

  return 0;
}

int main()
{
  VBDFilePtr f(new VBDFile); 
  const char *fname = "testfile.vbd";
  f->Create(fname); // Open the file and truncate it

  // Initialize the multi-threaded VBD objects
  vbdReadThread *read_thread = new vbdReadThread(f);
  vbdWriteThread *write_thread = new vbdWriteThread(f);

  // Arrays used to hold the read and write threads
  vbThread_t *wthreads[NUM_THREADS];
  vbThread_t *rthreads[NUM_THREADS];

  int i, j;
  cout << "Writing " << NUM_THREADS << " objects to the " << fname << " file"
       << endl;
  for(i = 0; i < NUM_THREADS; i++) {
    FileObject *ob = new FileObject; // Persistent object
    ob->id = 65+i;
    for(j = 0; j < name_length; j++) ob->name[j] = 0;
    memmove(ob->name, name_string, strlen(name_string));
    ob->name[strlen(name_string)] = char(ob->id);
    wthreads[i] = write_thread->CreateThread((void *)ob);
  }

  for(i = 0; i < NUM_THREADS; i++) write_thread->JoinThread(wthreads[i]);
  cout << "Write complete" << endl;
  
  cout << "Verifing each object" << endl; 
  cout << "Press Enter to continue..." << endl;
  cin.get();
  for(i = 0; i < NUM_THREADS; i++) {
    rthreads[i] = read_thread->CreateThread();
    read_thread->sSleep(1); // Allow each thread time to print its message
  }

  // Wait for the read threads to complete
  for(i = 0; i < NUM_THREADS; i++) read_thread->JoinThread(rthreads[i]);

  // Cleanup and release all the thread resources
  for(i = 0; i < NUM_THREADS; i++) write_thread->DestroyThread(wthreads[i]);
  for(i = 0; i < NUM_THREADS; i++) read_thread->DestroyThread(rthreads[i]);

  delete write_thread; // Release the write object's file pointer
  delete read_thread;  // Release the read object's file pointer

  f->Close(); // Close the file 
  return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //