// ------------------------------- // // -------- 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: 05/17/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 This is a test program used demonstrate the use of the 32/64-bit database engine in a multi-threaded application. This example demonstrates file and record locking used with single process threads. File and record locking via persistent locks or lock headers are not required for single process threads but are used here to demonstrate how file and record locking could be used a multi-process/multi-machine environment. Persistent locks provide a platform independent locking mechanism signaling to threads in other process and processes running on remote machines that the file or record is locked. The multi-process thread or remote process is responsible for reading the lock and acting accordingly. Single process threads can omit persistent locks thorough the use of mutex locks and condition variables. */ // ----------------------------------------------------------- // #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 <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_t)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_t 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_t 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(int argv, char **argc) { char rev_letter = 'D'; // Set the revision letter if(argv == 2) { // Set a specified revision letter rev_letter = *argc[1]; if(rev_letter == '0') rev_letter = 0; // Valid persistent record lock rev letters are: // Rev 'C' or 'c' // Rev 'D' or 'd' } gxDatabase *f = new gxDatabase; const char *fname = "testfile.gxd"; f->Create(fname, (FAU_t)0, rev_letter); // Persistent lock revision if(CheckError(f) != 0) { delete f; 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" << "\n"; 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" << "\n"; cout << "Verifing each object" << "\n"; cout << "Press Enter to continue..." << "\n"; 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) { delete f; return 1; } // Free the memory allocated for the file pointer 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 --------- // // ------------------------------- //