/* * This Software is the original work of Daniel TUNG * This Software is submitted in partial fulfillment of the * requirements for the degree of BCSE. * Dept. Computer Science, Monash University 2000 * * Copyright (c) 2000 beetung@cs.monash.edu.au * beetung@geocities.com */ #ifndef CCC_PTHREAD_H #define CCC_PTHREAD_H #include #include extern "C" { #include }; #include "timer.h" #include "semaphore.h" /// alias for void* function pointer /// typedef void* (*Vfptr)(void *); #ifdef DEBUG /////////// # define DEBUG_PTHREAD /////////// Modulized debug huh? #endif /////////// //// //// This Thread class is a Wrapper class around a C-Style Pthread. //// Thread::run() is passed onto pthread_create(...), and the templated //// _otype->_ofunc would be invoked inside Thread::run(). This enables //// us to utilize class member and functions on pthread related routines! //// template ////////////////////////////////////////////// class Thread { ////////////////////////////////////////////// typedef void *(ObjType::* ObjFunc)(); // typedef function ptr enum DetachedState { joinable, detached }; private: pthread_t _thread; // C style pthread int _threadID; bool _thread_started; // are we executing this thread already? ObjType *_otype; // "object pointer" as "*_otype::" inside run() ObjFunc _ofunc; // "object function" to execute int creating; // flag used on new() and delete() int dynamic; // string nameID; // Name to id this thread! MySemaphore isRunning; // block on ~dtor() if not running! private: /// /// This is the location where a new thread will begin executing. /// - note, its a static fn() //-------------------- static void* run(void *vPtr) { //-------------------- Thread *pthis = reinterpret_cast< Thread* >(vPtr); pthis->_threadID = pthread_self(); pthis->isRunning.SemPost(); // indicate that we are running now, so // ~dtor() can join() safely pthis->_thread_started =true; cerr << " Thread::run (" << pthis->_threadID << ") ... begins " <nameID << endl; /// :::::::::::::::::::::::::::::::::::::::: /// - immediately call its member function /// :::::::::::::::::::::::::::::::::::::::: /// ( (pthis->_otype) ->* (pthis->_ofunc) )(); cerr << " Thread::run (" << pthis->_threadID << ") ... completed " << pthis->nameID << endl; // pthread_exit(0); return 0; } public: /// /// Thread<> Ctor(); /// /// :::::: How to use :::::::: /// /// main() { /// Win w; /// Thread a(&w , &w.draw); // /// /// Win *wptr = w; /// Thread b(wptr , w->draw); /// } /// //-------------------- Thread( ObjType *otype, ObjFunc ofunc, string n = "un-named" ,void* vArgs = 0, Thread::DetachedState ds =joinable) //-------------------- : _threadID(-1), _thread_started(false),_otype(otype), _ofunc(ofunc),nameID(n) { //-------------------- #ifdef DEBUG_PTHREAD cerr<< " Thread::Thread()... completed" << endl; #endif // allows for ::delete on both NDA object and DA object. // dynamic = isCreating_ExecuteOnceOnlyPerObject() ; creating = 0; // enables delete now } /// /// Thread<> Dtor() /// //-------------------- ~Thread() { //-------------------- #ifdef DEBUG_PTHREAD cout << "Joining inside ~Thread(" ; if (_threadID !=-1) cout << _threadID ; else cout << "no ID yet, still creating"; cout << ") " << nameID <<" " << endl; #endif if ( isRunning.GetValue() ==0 ) { // dont use this function normally!!! /// At this point, we know that *this thread's dtor() is /// called before it has actually started running. /// /// Two distinction situations can both arrive at this point /// /// ::::: Situation 1 :::::::::: /// if we have created several threads, ie: /// /// main() { /// Thread a(...); /// Thread b(...); /// b(); /// a(); /// } /// /// In the above code, we allow both a and b to run /// concurrently. There is a chance that straight after /// a() is made, ___at the same time___, the end of scope /// is reached. Hence, a & b's ~dtor() invoked. A small /// window exists where our thread ~dtor() is called before /// a proper Thread::run(...) is made. /// /// ::::: Situation 2 :::::::::: /// this is more a programming error, where the /// programmer forgets to make a call to a new thread, ie: /// /// main() { /// Thread a(...); /// } /// /// /// ::::: SOLUTION ::::: /// We will igonore case 2 here. We utilize a semaphore /// between Thread::run() and this ~dtor() to synchronize /// the correct thread deletion behaviour. /// /// maybe we can use a timer here to sleep a while? /// Until thread is started? /// /// ... so, dont forget to call the new thread operator()!! /// cerr << "" << "WARNING : ~Thread() called before run() [blocking now] : (" << _threadID << ") " << this->nameID << endl; /* /// cos this is the dtor here, we really need to wait. /// instead of switching or just join... Timer t(0.25); // zZZ... 250mSec do { t.start(); t.block(); t.reset(); } while (! _thread_started); */ } isRunning.SemWait(); join(); // we have to join a dying thread cout << " ~Thread(" << _threadID << ") ...completed " << this->nameID << endl; } //-------------------- inline void join (void) { pthread_join( _thread, NULL); } //-------------------- /// /// overloaded function operator to start executing this thread /// //-------------------- void operator () (void) { //-------------------- // // if thread is already running, just return // if (_thread_started) return; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE); // pthread_attr_setscope(&attr,PTHREAD_SCOPE_SYSTEM); // System wide // pthread_attr_setschedpolicy(&attr,SCHED_RR); // Round Robin // pthread_attr_setschedpolicy(&attr,SCHED_FIFO); // FIFO pthread_attr_setscope(&attr,PTHREAD_SCOPE_PROCESS); // Local to Proc pthread_attr_setschedpolicy(&attr,SCHED_OTHER); // Other // start a new thread by invoking Thread::run(); // and begins executing immediately // pthread_create( &_thread, &attr, (Vfptr)(this->run), (void *)this); // pthread_create( &_thread, NULL, (Vfptr)(this->run), (void *)this); pthread_attr_destroy(&attr); } //------------------------------------ static void* operator new (size_t size ) { //------------------------------------ cout << " Thread::new() -- begins"; void* ptr = ::operator new(size); reinterpret_cast(ptr)->creating =1; setCreating(); // i am the dynamic kind! return ptr; cout << " ... completed "<< endl; } //// //// My overload operator delete //// //------------------------------------ static void operator delete(void *ptr) { //------------------------------------ cout << " Thread::delete() -- "; /// /// performs delete only if dynamic flag is set /// if (!reinterpret_cast(ptr)->creating) ::operator delete(ptr); else cout << "ERROR: attempting to delete while still creating !!" << endl; cout << " Thread::delete() ... completed"<< endl; } /// //------------------------------------ friend ostream& operator<< (ostream& os, const Thread *pthis) { //------------------------------------ os << " ThreadID(" << pthis->_threadID << ") " << endl; } private: //// //// These few functions are for detecting the situation where we have //// new() and delete() being called at the same time! possible if we //// are on a multi-threaded system. but there are simple programming //// tricks to avoid it... //------------------------------------ static int& setCount() { //------------------------------------ static int Count =0; return Count; } //------------------------------------ static void setCreating() { //------------------------------------ setCount() = -1; } // return true if (uniFlag > 0) //------------------------------------ static bool isCreating_ExecuteOnceOnlyPerObject() { //------------------------------------ // mod by 10 and increment by 1 , so wont // be limited by sizeof(int), circular fashion setCount() = (setCount()%10) +1; return !( setCount()> 0); } /* ::::::COMMENTED::::: Dont use this function, still untested!!! ::::::COMMENTED::::: //-------------------- void uDie(void) { //-------------------- if (!_thread_started) return; pthread_cancel(_thread); } */ }; /// if compiled under my /pthread/ directory /// #ifdef CCC_PTHREAD_TEST /// These are for testing our thread class implementation /// and serve as an example for you, to see how we can utilize /// thread.h efficiently and correctly //// //// My Thread<> test class, by inheritance //// //// demonstrate how to use Thread<> via inheritance here. //// I prefer the other method: encapsulation. //// ////////////////////////////////////////////// class WinThread : public Thread{ ////////////////////////////////////////////// private: int i,j; public: /// notice we pass in the function to be /// executed here during Thread<> inits //-------------------- WinThread(): Thread( this, &test ) { //-------------------- i=10; j=4; cerr << "WinThread() " << endl; } //-------------------- ~WinThread() { //-------------------- cerr << "~WinThread() " << endl; } /// The return type __MUST__ be a void* /// //-------------------- void* test() { //-------------------- cout << " ...calling WinThread::test() "<< endl; for (int k=50; k!=0; k--) cout << k << " " ; cout << endl; cout << "i: " << i << " j: " << j < and invoke/execute it later after delete. Yet, // the internal logics on our thread implementation _blocks_ // the destruction until it is executed 1st. DeleteAll da; //// demonstrate how to initialize a new Thread<> on an //// object function here. notice both params are pointers //// 1st param should be an object of a template type //// 2nd param should be a __member function__ of 1st param type //// //// we have created a thread "killer" here, to start executing it //// we call its operator()(); Thread killer(&da,&da.del); killer(); /// Note that every thread must be executed, ie: its operator()() /// must be called, otherwise our program would block forever. /// to-be-implemented: use a smart timer?, // run it now, after the "delete thread" is started! // for (int i=0; i!=max; i++) { (*athread[i])(); } cout << " main() exiting... " << endl; } #endif // for ifdef CCC_PTHREAD_TEST #endif //