// ------------------------------- // // -------- 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 --------- // // ------------------------------- //