// ------------------------------- //
// -------- Start of File -------- //
// ------------------------------- //
// ----------------------------------------------------------- //
// C++ Source Code File Name: testprog.cpp
// Compiler Used: MSVC, BCC32, GCC, HPUX aCC, SOLARIS CC
// Produced By: glNET Software
// File Creation Date: 05/17/2000
// Date Last Modified: 06/12/2001
// Copyright (c) 2001 glNET Software
// ----------------------------------------------------------- //
// ------------- 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
This is a test program used demonstrate the use of the database
engine in a multi-threaded application.
*/
// ----------------------------------------------------------- //
#include <iostream.h>
#include <string.h>
#include "gxthread.h"
#include "gxmutex.h"
#include "gxcond.h"
#include "gxdbase.h"
#include "gxdstats.h"
// Constants
const int NUM_THREADS = 26;
const int MAX_NUM_TRY = 3;
const int name_length = 16;
const char *name_string = "File Object ";
struct DatabaseObject {
char name[name_length];
int id;
};
// Class used to perform multi-threaded reads
class gxdReadThread : public gxThread
{
public:
gxdReadThread(gxDatabase *gxdfile) { f = gxdfile; curr_offset = (FAU)0; }
~gxdReadThread() { }
private: // Base class interface
void *ThreadEntryRoutine(gxThread_t *thread);
private:
gxDatabase *f; // Pointer to the open database file
gxMutex 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 gxdWriteThread : public gxThread
{
public:
gxdWriteThread(gxDatabase *gxdfile) { f = gxdfile; }
~gxdWriteThread() { }
private: // Base class interface
void *ThreadEntryRoutine(gxThread_t *thread);
private:
gxMutex write_lock; // Mutex object used to lock the file
gxCondition write_cond; // Condition variable used to block other threads
gxDatabase *f; // Pointer to the open database file
};
void *gxdReadThread::ThreadEntryRoutine(gxThread_t *thread)
{
offset_lock.MutexLock(); // Serialize access to curr_offset
curr_offset = f->FindFirstObject(curr_offset);
FAU block_address = curr_offset - f->BlockHeaderSize();
if(curr_offset) {
DatabaseObject ob;
f->LockRecord(gxDBASE_READLOCK, block_address);
f->Read(&ob, sizeof(DatabaseObject), curr_offset);
cout << "Reading: \"" << ob.name << "\" at address: " << (long)curr_offset
<< "\n" << flush;
f->UnlockRecord(gxDBASE_READLOCK, block_address);
}
offset_lock.MutexUnlock();
return 0;
}
void *gxdWriteThread::ThreadEntryRoutine(gxThread_t *thread)
// Thread safe write function that will not allow access to
// the critical section until the write operation is complete.
{
DatabaseObject *ob = (DatabaseObject *)thread->GetThreadParm();
write_lock.MutexLock();
// Tell other threads to wait until this write is complete
int num_try = 0;
while(f->LockFile() != 0) {
// Block this thread from its own execution if a another thread
// is writing to the file
if(++num_try < MAX_NUM_TRY) {
write_cond.ConditionWait(&write_lock);
}
else {
cout << "Could not write object to the file.\n" << flush;
return 0;
}
}
// ********** Enter Critical Section ******************* //
f->Write(ob, sizeof(DatabaseObject), f->Alloc(sizeof(DatabaseObject)));
// ********** Leave Critical Section ******************* //
f->UnlockFile(); // 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()
{
gxDatabase *f = new gxDatabase;
const char *fname = "testfile.gxd";
f->Create(fname, FAU(0), 'C'); // Persistent lock revision
if(CheckError(f) != 0) return 1;
// Initialize the multi-threaded database objects
gxdReadThread *read_thread = new gxdReadThread(f);
gxdWriteThread *write_thread = new gxdWriteThread(f);
// Arrays used to hold the read and write threads
gxThread_t *wthreads[NUM_THREADS];
gxThread_t *rthreads[NUM_THREADS];
int i, j;
cout << "Writing " << NUM_THREADS << " objects to the " << fname << " file"
<< endl;
DatabaseObject *ob_ptr[NUM_THREADS];
for(i = 0; i < NUM_THREADS; i++) {
DatabaseObject *ob = new DatabaseObject; // 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);
ob_ptr[i] = 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
if(CheckError(f) != 0) return 1;
delete f;
// Free the memory allocated for each database object pointer
for(i = 0; i < NUM_THREADS; i++) delete ob_ptr[i];
return 0;
}
// ----------------------------------------------------------- //
// ------------------------------- //
// --------- End of File --------- //
// ------------------------------- //