summaryrefslogtreecommitdiffstats
path: root/src/kvilib/system/kvi_thread.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/kvilib/system/kvi_thread.h')
-rw-r--r--src/kvilib/system/kvi_thread.h378
1 files changed, 378 insertions, 0 deletions
diff --git a/src/kvilib/system/kvi_thread.h b/src/kvilib/system/kvi_thread.h
new file mode 100644
index 00000000..bd6dab3b
--- /dev/null
+++ b/src/kvilib/system/kvi_thread.h
@@ -0,0 +1,378 @@
+#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 <qnamespace.h>
+#include <qobject.h>
+#include <qsocketnotifier.h>
+#include "kvi_pointerlist.h"
+#include <qevent.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<QEvent> * 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(QObject *o,QEvent *e);
+private:
+ void setRunning(bool bRunning);
+ void setStartingUp(bool bStartingUp);
+};
+
+// QEvent::Type for Thread events
+#define KVI_THREAD_EVENT (((int)QEvent::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 QEvent, public KviHeapObject
+{
+protected:
+ int m_eventId;
+ KviThread * m_pSender;
+public:
+ KviThreadEvent(int evId,KviThread * sender = 0)
+ : QEvent((QEvent::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
+{
+ QObject *o;
+ QEvent *e;
+} KviThreadPendingEvent;
+
+class KVILIB_API KviThreadManager : public QObject
+{
+ 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(QObject * receiver);
+private:
+#ifndef COMPILE_ON_WINDOWS
+ QSocketNotifier * 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(QObject *o,QEvent *e);
+ void killPendingEventsByReceiver(QObject * receiver);
+ // Public to KviApp only
+ static void globalInit();
+ static void globalDestroy();
+private slots:
+ void eventsPending(int fd);
+};
+
+
+#endif //!_KVI_THREAD_H_