|
|
|
#ifndef _KVI_THREAD_H_
|
|
|
|
#define _KVI_THREAD_H_
|
|
|
|
//
|
|
|
|
// File : kvi_thread.h
|
|
|
|
// Creation date : Mon May 17 1999 04:26:41 CEST by Szymon Stefanek
|
|
|
|
//
|
|
|
|
// This file is part of the KVirc irc client distribution
|
|
|
|
// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net)
|
|
|
|
//
|
|
|
|
// This program is FREE software. You can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License
|
|
|
|
// as published by the Free Software Foundation; either version 2
|
|
|
|
// of the License, or (at your opinion) any later version.
|
|
|
|
//
|
|
|
|
// This program 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 General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, write to the Free Software Foundation,
|
|
|
|
// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
//
|
|
|
|
|
|
|
|
#include "kvi_settings.h"
|
|
|
|
#include "kvi_heapobject.h"
|
|
|
|
#include "kvi_string.h"
|
|
|
|
|
|
|
|
#include <tqnamespace.h>
|
|
|
|
#include <tqobject.h>
|
|
|
|
#include <tqsocketnotifier.h>
|
|
|
|
#include "kvi_pointerlist.h"
|
|
|
|
#include <tqevent.h>
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Simple thread implementation
|
|
|
|
// This is enough for KVIrc needs
|
|
|
|
// HANDLE WITH CARE
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
// Portability stuff
|
|
|
|
|
|
|
|
#ifdef COMPILE_ON_WINDOWS
|
|
|
|
|
|
|
|
#include <winsock2.h> // this will pull in windows.h and will avoid windock.h inclusion
|
|
|
|
//#include <windows.h>
|
|
|
|
// Windoze thread abstraction layer
|
|
|
|
#define kvi_mutex_t HANDLE
|
|
|
|
inline void kvi_threadMutexInit(kvi_mutex_t * _pMutex_t)
|
|
|
|
{
|
|
|
|
*_pMutex_t = CreateMutex(0,0,NULL);
|
|
|
|
}
|
|
|
|
#define kvi_threadMutexLock(_pMutex_t) WaitForSingleObject(*_pMutex_t,INFINITE)
|
|
|
|
#define kvi_threadMutexUnlock(_pMutex_t) ReleaseMutex(*_pMutex_t)
|
|
|
|
#define kvi_threadMutexDestroy(_pMutex_t) CloseHandle(*_pMutex_t)
|
|
|
|
inline bool kvi_threadMutexTryLock(kvi_mutex_t *_pMutex_t)
|
|
|
|
{
|
|
|
|
return (WaitForSingleObject(*_pMutex_t,0) == WAIT_OBJECT_0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define kvi_thread_t HANDLE
|
|
|
|
|
|
|
|
inline bool kvi_threadCreate(kvi_thread_t *t,LPTHREAD_START_ROUTINE start_routine,void * arg)
|
|
|
|
{
|
|
|
|
DWORD dwThreadId;
|
|
|
|
*t = CreateThread(NULL,0,start_routine,arg,0,&dwThreadId);
|
|
|
|
return (*t != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define kvi_threadExit() ExitThread(0)
|
|
|
|
|
|
|
|
#else
|
|
|
|
#ifdef COMPILE_THREADS_USE_POSIX
|
|
|
|
// Glibc pthread implementation
|
|
|
|
|
|
|
|
#include <pthread.h>
|
|
|
|
#include <errno.h> // for EBUSY
|
|
|
|
|
|
|
|
// Mutex stuff
|
|
|
|
#define kvi_mutex_t pthread_mutex_t
|
|
|
|
#define kvi_threadMutexInit(_pMutex_t) pthread_mutex_init(_pMutex_t,0)
|
|
|
|
#define kvi_threadMutexLock(_pMutex_t) pthread_mutex_lock(_pMutex_t)
|
|
|
|
#define kvi_threadMutexUnlock(_pMutex_t) pthread_mutex_unlock(_pMutex_t)
|
|
|
|
#define kvi_threadMutexDestroy(_pMutex_t) pthread_mutex_destroy(_pMutex_t)
|
|
|
|
inline bool kvi_threadMutexTryLock(kvi_mutex_t *_pMutex_t)
|
|
|
|
{
|
|
|
|
return (pthread_mutex_trylock(_pMutex_t) != EBUSY);
|
|
|
|
}
|
|
|
|
// Actually unused
|
|
|
|
// #define kvi_threadMutexTryLock(_pMutex_t) pthread_mutex_trylock(_pMutex_t)
|
|
|
|
|
|
|
|
// Thread stuff
|
|
|
|
#define kvi_thread_t pthread_t
|
|
|
|
|
|
|
|
inline bool kvi_threadCreate(kvi_thread_t *t,void * (*start_routine)(void *),void * arg)
|
|
|
|
{
|
|
|
|
pthread_attr_t a;
|
|
|
|
pthread_attr_init(&a);
|
|
|
|
pthread_attr_setinheritsched(&a,PTHREAD_INHERIT_SCHED);
|
|
|
|
pthread_attr_setdetachstate(&a,PTHREAD_CREATE_DETACHED);
|
|
|
|
|
|
|
|
int ret = pthread_create(t,&a,start_routine,arg);
|
|
|
|
|
|
|
|
pthread_attr_destroy(&a);
|
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't care about exit codes at all
|
|
|
|
#define kvi_threadExit() pthread_exit(0)
|
|
|
|
#else
|
|
|
|
#ifdef COMPILE_THREADS_USE_SOLARIS_LIBTHREAD
|
|
|
|
// Native solaris implementation
|
|
|
|
#include <thread.h>
|
|
|
|
#include <synch.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
// Mutex stuff
|
|
|
|
#define kvi_mutex_t mutex_t
|
|
|
|
#define kvi_threadMutexInit(_pMutex_t) mutex_init(_pMutex_t,0,0)
|
|
|
|
#define kvi_threadMutexLock(_pMutex_t) mutex_lock(_pMutex_t)
|
|
|
|
#define kvi_threadMutexUnlock(_pMutex_t) mutex_unlock(_pMutex_t)
|
|
|
|
#define kvi_threadMutexDestroy(_pMutex_t) mutex_destroy(_pMutex_t)
|
|
|
|
inline bool kvi_threadMutexTryLock(kvi_mutex_t *_pMutex_t)
|
|
|
|
{
|
|
|
|
return (mutex_trylock(_pMutex_t) != EBUSY);
|
|
|
|
};
|
|
|
|
// Actually unused
|
|
|
|
// #define kvi_threadMutexTryLock(_pMutex_t) mutex_trylock(_pMutex_t)
|
|
|
|
|
|
|
|
// Thread stuff
|
|
|
|
#define kvi_thread_t thread_t
|
|
|
|
|
|
|
|
inline bool kvi_threadCreate(kvi_thread_t *t,void * (*start_routine)(void *),void *arg)
|
|
|
|
{
|
|
|
|
return (thr_create(0,0,start_routine,arg,THR_DETACHED,t) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't care about exit codes at all
|
|
|
|
#define kvi_threadExit() thr_exit(0)
|
|
|
|
#else
|
|
|
|
// FIXME: #warning "Missing a decent thread implementation: we're going to fail , sorry!"
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
class KVILIB_API KviMutex : public KviHeapObject
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
kvi_mutex_t m_mutex;
|
|
|
|
#ifdef COMPILE_ON_WINDOWS
|
|
|
|
bool m_bLocked;
|
|
|
|
#endif
|
|
|
|
public:
|
|
|
|
KviMutex(){ kvi_threadMutexInit(&m_mutex); };
|
|
|
|
virtual ~KviMutex(){ kvi_threadMutexDestroy(&m_mutex); };
|
|
|
|
public:
|
|
|
|
#ifdef COMPILE_ON_WINDOWS
|
|
|
|
void lock(){ kvi_threadMutexLock(&m_mutex); m_bLocked = true; };
|
|
|
|
void unlock(){ m_bLocked = false; kvi_threadMutexUnlock(&m_mutex); };
|
|
|
|
bool locked(){ return m_bLocked; };
|
|
|
|
#else
|
|
|
|
void lock(){ kvi_threadMutexLock(&m_mutex); };
|
|
|
|
void unlock(){ kvi_threadMutexUnlock(&m_mutex); };
|
|
|
|
bool locked();
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// simple thread class implementation
|
|
|
|
// this is also called "Blind" thread class
|
|
|
|
|
|
|
|
class KVILIB_API KviThread : public KviHeapObject
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KviThread();
|
|
|
|
virtual ~KviThread();
|
|
|
|
private:
|
|
|
|
kvi_thread_t m_thread;
|
|
|
|
bool m_bRunning;
|
|
|
|
bool m_bStartingUp;
|
|
|
|
KviMutex * m_pRunningMutex;
|
|
|
|
KviPointerList<TQEvent> * m_pLocalEventQueue;
|
|
|
|
public:
|
|
|
|
// public KviThread interface
|
|
|
|
// HANDLE WITH CARE
|
|
|
|
|
|
|
|
// Runs the thread...call only from external threads!!! :)
|
|
|
|
// This function returns true if the child thread has been succesfully created
|
|
|
|
// this des not mean that run() is being already executed...
|
|
|
|
// isStartingUp() will return true from this moment until
|
|
|
|
// the child thread jumps into run() where it will be set to running state (isRunning() == true)
|
|
|
|
// and removed from startingUp state.
|
|
|
|
bool start();
|
|
|
|
// Returns the state of the thread...safe to call from anywhere
|
|
|
|
bool isRunning();
|
|
|
|
// Returns the state of the thread...safe to call from anywhere
|
|
|
|
bool isStartingUp(); // start() called , but not in run() yet...
|
|
|
|
// Waits for the termination of this thread: call only from external threads!!! :)
|
|
|
|
void wait();
|
|
|
|
// DO NOT TOUCH THIS ONE!
|
|
|
|
void internalThreadRun_doNotTouchThis();
|
|
|
|
|
|
|
|
static void sleep(unsigned long sec);
|
|
|
|
static void msleep(unsigned long msec);
|
|
|
|
static void usleep(unsigned long usec);
|
|
|
|
protected:
|
|
|
|
// protected KviThread interface
|
|
|
|
// HANDLE WITH CARE TOO!
|
|
|
|
|
|
|
|
// Reimplement this with your job
|
|
|
|
virtual void run(){};
|
|
|
|
// Terminates the execution of the calling thread
|
|
|
|
void exit();
|
|
|
|
// The tricky part: threadsafe event dispatching
|
|
|
|
// Slave thread -> main thread objects
|
|
|
|
void postEvent(TQObject *o,TQEvent *e);
|
|
|
|
private:
|
|
|
|
void setRunning(bool bRunning);
|
|
|
|
void setStartingUp(bool bStartingUp);
|
|
|
|
};
|
|
|
|
|
|
|
|
// TQEvent::Type for Thread events
|
|
|
|
#define KVI_THREAD_EVENT (((int)TQEvent::User) + 2000)
|
|
|
|
|
|
|
|
// CONSTANTS FOR KviThreadEvent::eventId();
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// extern -> slave thread
|
|
|
|
|
|
|
|
// Your reimplementation of KviSensitiveThread MUST handle this
|
|
|
|
// and exit when this event is received
|
|
|
|
|
|
|
|
// Terminate is a plain KviThreadEvent
|
|
|
|
#define KVI_THREAD_EVENT_TERMINATE 0
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// slave thread -> master object
|
|
|
|
|
|
|
|
// The following standard events are sent from the thread to the master object
|
|
|
|
|
|
|
|
// The following are plain KviThreadEvent objects
|
|
|
|
#define KVI_THREAD_EVENT_SUCCESS 100
|
|
|
|
|
|
|
|
// The following are KviThreadDataEvent<int>
|
|
|
|
#define KVI_THREAD_EVENT_STATECHANGE 150
|
|
|
|
|
|
|
|
// The following are KviThreadDataEvent<KviStr>
|
|
|
|
#define KVI_THREAD_EVENT_MESSAGE 200
|
|
|
|
#define KVI_THREAD_EVENT_WARNING 201
|
|
|
|
#define KVI_THREAD_EVENT_ERROR 202
|
|
|
|
#define KVI_THREAD_EVENT_DATA 203
|
|
|
|
|
|
|
|
// The following is KviThreadDataEvent<KviDataBuffer>
|
|
|
|
#define KVI_THREAD_EVENT_BINARYDATA 300
|
|
|
|
|
|
|
|
// The user events
|
|
|
|
#define KVI_THREAD_USER_EVENT_BASE 1000
|
|
|
|
|
|
|
|
// #warning "Get rid of the m_szMessage member of KviThreadEvent : eventual data should be passed with a KviThreadDataEvent"
|
|
|
|
|
|
|
|
// Base class for all thread events
|
|
|
|
class KVILIB_API KviThreadEvent : public TQEvent, public KviHeapObject
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
int m_eventId;
|
|
|
|
KviThread * m_pSender;
|
|
|
|
public:
|
|
|
|
KviThreadEvent(int evId,KviThread * sender = 0)
|
|
|
|
: TQEvent((TQEvent::Type)KVI_THREAD_EVENT) , m_eventId(evId) , m_pSender(sender) {};
|
|
|
|
virtual ~KviThreadEvent(){};
|
|
|
|
public:
|
|
|
|
// This is the sender of the event
|
|
|
|
// WARNING : this MAY be null , threads CAN send anonymous events
|
|
|
|
KviThread * sender(){ return m_pSender; };
|
|
|
|
int id(){ return m_eventId; };
|
|
|
|
};
|
|
|
|
|
|
|
|
template<class TData> class KviThreadDataEvent : public KviThreadEvent
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
TData * m_pData;
|
|
|
|
public:
|
|
|
|
KviThreadDataEvent(int evId,TData * pData = 0,KviThread * sender = 0)
|
|
|
|
: KviThreadEvent(evId,sender){ m_pData = pData; };
|
|
|
|
virtual ~KviThreadDataEvent(){ if(m_pData)delete m_pData; };
|
|
|
|
public:
|
|
|
|
void setData(TData * d){ if(m_pData)delete m_pData; m_pData = d; };
|
|
|
|
TData * getData(){ TData * aux = m_pData; m_pData = 0; return aux; };
|
|
|
|
TData * data(){ return m_pData; };
|
|
|
|
};
|
|
|
|
|
|
|
|
// A thread that has also an internal event queue
|
|
|
|
// so events can be posted from the master side to the slave one
|
|
|
|
// Reimplementations of this class should periodically check
|
|
|
|
// dequeueEvent() and eventually process the incoming events (and then DELETE it)
|
|
|
|
|
|
|
|
// KVI_THREAD_EVENT_TERMINATE should be always handled by the reimplementation
|
|
|
|
// and it should always exit (cleanly) when this event is received
|
|
|
|
|
|
|
|
|
|
|
|
class KVILIB_API KviSensitiveThread : public KviThread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
KviSensitiveThread();
|
|
|
|
virtual ~KviSensitiveThread();
|
|
|
|
protected:
|
|
|
|
KviMutex * m_pLocalEventQueueMutex;
|
|
|
|
KviPointerList<KviThreadEvent> * m_pLocalEventQueue;
|
|
|
|
public:
|
|
|
|
// enqueues an event directed to THIS thread
|
|
|
|
// the event must be allocated with NEW and
|
|
|
|
// will be destroyed on the slave side
|
|
|
|
void enqueueEvent(KviThreadEvent *e);
|
|
|
|
// enqueues a terminate event and waits() for the slave thread
|
|
|
|
// the slave thread MUST handle KVI_THREAD_EVENT_TERMINATE
|
|
|
|
void terminate();
|
|
|
|
protected:
|
|
|
|
// slave side:
|
|
|
|
// returns the first event in the local queue
|
|
|
|
// the event MUST BE DELETED after processing
|
|
|
|
KviThreadEvent * dequeueEvent();
|
|
|
|
};
|
|
|
|
|
|
|
|
// =============================================================================================//
|
|
|
|
// This is private stuff...only KviThread and KviApp may use it
|
|
|
|
// and may call only specific functions...don't touch.
|
|
|
|
|
|
|
|
typedef struct _KviThreadPendingEvent
|
|
|
|
{
|
|
|
|
TQObject *o;
|
|
|
|
TQEvent *e;
|
|
|
|
} KviThreadPendingEvent;
|
|
|
|
|
|
|
|
class KVILIB_API KviThreadManager : public TQObject
|
|
|
|
{
|
|
|
|
friend class KviApp;
|
|
|
|
friend class KviThread;
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
protected:
|
|
|
|
// These should be private...but we don't want anyone to complain
|
|
|
|
// Treat as private plz.
|
|
|
|
KviThreadManager();
|
|
|
|
~KviThreadManager();
|
|
|
|
public:
|
|
|
|
static void killPendingEvents(TQObject * receiver);
|
|
|
|
private:
|
|
|
|
#ifndef COMPILE_ON_WINDOWS
|
|
|
|
TQSocketNotifier * m_pSn;
|
|
|
|
#endif
|
|
|
|
KviMutex * m_pMutex; // This class performs only atomic operations
|
|
|
|
KviPointerList<KviThread> * m_pThreadList;
|
|
|
|
int m_iWaitingThreads;
|
|
|
|
#ifndef COMPILE_ON_WINDOWS
|
|
|
|
KviPointerList<KviThreadPendingEvent> * m_pEventQueue;
|
|
|
|
int m_fd[2];
|
|
|
|
int m_iTriggerCount;
|
|
|
|
#endif
|
|
|
|
protected:
|
|
|
|
// Public to KviThread only
|
|
|
|
void registerSlaveThread(KviThread *t);
|
|
|
|
void unregisterSlaveThread(KviThread *t);
|
|
|
|
|
|
|
|
void threadEnteredWaitState();
|
|
|
|
void threadLeftWaitState();
|
|
|
|
|
|
|
|
void postSlaveEvent(TQObject *o,TQEvent *e);
|
|
|
|
void killPendingEventsByReceiver(TQObject * receiver);
|
|
|
|
// Public to KviApp only
|
|
|
|
static void globalInit();
|
|
|
|
static void globalDestroy();
|
|
|
|
private slots:
|
|
|
|
void eventsPending(int fd);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#endif //!_KVI_THREAD_H_
|