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