summaryrefslogtreecommitdiffstats
path: root/src/kvilib/system/kvi_thread.h
blob: 75bc07715a7922071f279c0557af413bfa9ed2a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#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;
	TQ_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_