// Create a thread directory class that is instantiated by the main // thread. Create threads through this class. Each thread is assigned // a name as it is created. At the same time, each thread is given the // address of the directory object. When a thread wishes to post an // event to another thread, it checks its member variable that contains // the address of the other thread. If that is zero, then the client // thread asks the directory object for the address of the server // thread by using the name previously assigned to the server thread. // //********************************************************************* // thread.cpp //********************************************************************* #include <windows.h> #define __MT__ #include <process.h> #include "prefixb.h" #include "atom.h" #include "syslog.h" #include "thread.h" ushort CThread::threads = 0; //===================================================================== // TSema4 class implementation //--------------------------------------------------------------------- // CSemaphore::CSemaphore(short _start, short _size): CAtom (), m_hSema4(0) { cls = iSemaphore; m_hSema4 = ::CreateSemaphore(0, _start, _size, 0); Assert(m_hSema4); } // CSemaphore //--------------------------------------------------------------------- // CSemaphore::~CSemaphore() { Assert(m_hSema4); ::CloseHandle(m_hSema4); } // ~CSemaphore //--------------------------------------------------------------------- // RTTI_CPP(Semaphore, Atom) //--------------------------------------------------------------------- // ulong CSemaphore::Wait(void) { Assert(m_hSema4); return ::WaitForSingleObject(m_hSema4, INFINITE); } // Wait //--------------------------------------------------------------------- // ulong CSemaphore::Release(void) { Assert(m_hSema4); return ::ReleaseSemaphore(m_hSema4, 1, 0); } // Release //===================================================================== // CPort class implementation // //--------------------------------------------------------------------- // CPort::CPort(short _size): CAtom(), s1 (0), s2 (0), s3 (0), list (0) { cls = iPort; s1 = new CSemaphore(_size, _size); s2 = new CSemaphore(0, _size); s3 = new CSemaphore(1, 1); list = new CList(); } // CPort //--------------------------------------------------------------------- // CPort::~CPort() { //?delete s1; //?delete s2; //?delete s3; //?delete list; } // ~CPort //--------------------------------------------------------------------- // RTTI_CPP(Port, Atom) //--------------------------------------------------------------------- // bool CPort::Empty(void) { return list->Empty(); } // Empty //--------------------------------------------------------------------- // void CPort::Post(RPCItem _item) { s1->Wait(); s3->Wait(); list->Insert(_item, atEnd); s3->Release(); s2->Release(); } // Post //--------------------------------------------------------------------- // PCItem CPort::Receive(void) { s2->Wait(); s3->Wait(); Assert(list->GotoHead()); PCItem item = list->Remove(after, false); s3->Release(); s1->Release(); return item; } // Receive //===================================================================== // CCriticalSection class implementation // CCriticalSection::CCriticalSection(void): CAtom() { cls = iCriticalSection; ::InitializeCriticalSection(&m_cs); } // CCriticalSection //--------------------------------------------------------------------- // CCriticalSection::~CCriticalSection() { ::DeleteCriticalSection(&m_cs); } // ~CCriticalSection //--------------------------------------------------------------------- // RTTI_CPP(CriticalSection, Atom) //--------------------------------------------------------------------- // void CCriticalSection::Enter(void) { ::EnterCriticalSection(&m_cs); } // Enter //--------------------------------------------------------------------- // void CCriticalSection::Leave(void) { ::LeaveCriticalSection(&m_cs); } // Leave //===================================================================== // CLock class implementation // //--------------------------------------------------------------------- // RTTI_CPP(Lock, Atom) //===================================================================== // CThread class implementation // // After constructing a CThread object, call the Start() method to // begin the thread. You must override the Run() method to implement // the thread. The thread must Stop() itself, noone else! Another // thread can terminate the thread by calling its Join() method. // // Also, to operate in the farBook environment, your thread class name // must begin with a capital-C, as in CMyThread, and you must define a // ulong constant whose name must begin with a lower-case-i, as in // iMyThread. This strangeness is because of the _FBDEF and RTTI_CPP // macros, which implement farVIEW's run-time type checking. Actually, // these rules are true for any class you derive within the farBook // environment. // // Example Code: // // Derive a class from CThread: // // const ulong iMyThread = 1000; // // _FBDEF(CMyThread) // class FBC_USE CMyThread: public CThread { // public: // FB_USE CMyThread(void); // virtual FB_USE ~CMyThread(); // // virtual FB_USE_(short) Ima(void); // virtual FB_USE_(Pchar) IAm(void); // virtual FB_USE_(bool) Isa(const short _type); // // FB_USE_(void) Run(void); // // }; // CMyThread // //--------------------------------------------------------------------- // // CMyThread::CMyThread(void): // CThread() { // } // CMyThread // //--------------------------------------------------------------------- // // CMyThread::~CMyThread() { // } // ~CMyThread // //--------------------------------------------------------------------- // Macro to generate code for Ima(), IAm(), and Isa() // // RTTI_CPP(MyThread, Thread) // //--------------------------------------------------------------------- // // void CMyThread::Run(void) { // while (state == THREAD_RUNNING) { // // Insert your control logic for the thread here // Pause(wait); // } // Stop(); // } // Run // //--------------------------------------------------------------------- // The parent instantiates the thread using the Start() method as in // the following two lines of code: // // PCMyThread myThread = new CMyThread(); // myThread->Start(32000); // 32000 specifies the stack size // // The P-prefix on the class name (as in PCMyThread) is defined by the // _FBDEF macro and declares a smart pointer, which will take care of // deleting and destructing the object. Do not delete the object // yourself. When it goes out of scope, it will decrement its use-count, // and when that reaches zero, the object will self-destruct. As a // general rule, you should always use smart pointers when using the // classes provided in the farBook DLL. // // In the CMyThread example, when the parent wants the thread to quit, // it simply calls the thread's Join() method: // // myThread->Join(); // // Note that the thread inherits an input port that other threads can // use to post messages (derived from CItem) to the thread. The client // posts a message with the following line: // // myThread->GetPort()->Post(myMessage); // // The thread receives the message with the following line: // // PCMessage message = GetPort()->Receive(); // // It is up to the application to define the message object. // // By including a status flag member in the message that the server // thread sets according to the state of the request, the client thread // can keep track of progress. Just be sure that only the server thread // sets the value of the flag member, and the client thread only // examines the value. 'Two' many cooks will spoil the broth. // // Note: farSlang modules must never instantiate a farSlang thread // object using the CThread class. A different class (C???) is // available for the purpose. // //--------------------------------------------------------------------- // CThread::CThread(short _size): CItem (), id (++threads), port (0), state (THREAD_READY) { cls = iThread; port = new CPort(_size); //?then = GetTicks(); } // CThread //--------------------------------------------------------------------- // CThread::~CThread() { Log('D', "Thread %d deleted (%d)", id, NrRefs()); } // ~CThread //--------------------------------------------------------------------- // RTTI_CPP(Thread, Item) //--------------------------------------------------------------------- // Private static method to act as thread root for _beginthread // void CThread::run(void *_arg) { pCThread thread = (CThread*)_arg; thread->Run(); thread->Stop(); } // run //--------------------------------------------------------------------- // ulong CThread::Start(ulong _sizeStack) { Assert(state == THREAD_READY || state == THREAD_STOPPED); state = THREAD_RUNNING; Log('D', "Thread %d starting", id); return ::_beginthread(run, _sizeStack, (void*)this); } // Start //--------------------------------------------------------------------- // A thread class MUST override this function // void CThread::Run(void) { Assert(false); } // Run //--------------------------------------------------------------------- // MUST be called from within the thread. Otherwise a surprise awaits! // void CThread::Stop(void) { //?Assert(port->Empty()); Assert(state == THREAD_RUNNING || state == THREAD_ENDING); Log('D', "Thread %d stopping (%d)", id, NrRefs()); state = THREAD_STOPPED; ::_endthread(); } // Stop //--------------------------------------------------------------------- // void CThread::Join(void) { state = THREAD_ENDING; while (state != THREAD_STOPPED) Pause(10); } // Join //--------------------------------------------------------------------- // void CThread::Log(char _flag, Pchar _format, ...) { if (!StrEmpty(_format)) { // Print formatted message va_list args; va_start(args, _format); char line[sizeString]; vsprintf(line, _format, args); va_end(args); Assert(strlen(line) < sizeString); char date[sizeLine]; GetGMT(date, sizeLine); ulong now = GetTicks(); SysLog::Log(_flag, "(%d: %ld) %s", id, now, line); //? SysLog::Log(_flag, "(%d: %ld) %s", id, now - then, line); //? then = now; } } // Log