1 //------------------------------------------------------------------------------
2 // emThread.h
3 //
4 // Copyright (C) 2009-2010,2016-2017 Oliver Hamann.
5 //
6 // Homepage: http://eaglemode.sourceforge.net/
7 //
8 // This program is free software: you can redistribute it and/or modify it under
9 // the terms of the GNU General Public License version 3 as published by the
10 // Free Software Foundation.
11 //
12 // This program is distributed in the hope that it will be useful, but WITHOUT
13 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 // FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
15 // more details.
16 //
17 // You should have received a copy of the GNU General Public License version 3
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 //------------------------------------------------------------------------------
20 
21 #ifndef emThread_h
22 #define emThread_h
23 
24 #ifndef emStd2_h
25 #include <emCore/emStd2.h>
26 #endif
27 
28 class emThreadPrivate;
29 struct emThreadEventReceiver;
30 
31 
32 //==============================================================================
33 //================================= emThreadId =================================
34 //==============================================================================
35 
36 typedef emUInt64 emThreadId;
37 
38 
39 //==============================================================================
40 //================================== emThread ==================================
41 //==============================================================================
42 
43 class emThread : public emUncopyable {
44 
45 public:
46 
47 	// Class for a thread of program execution.
48 	//
49 	// Some hints on multi-threading:
50 	// - Where not stated otherwise, all classes and functions of emCore are
51 	//   thread-reentrant but not thread-safe. This means that the classes
52 	//   and functions can be used by multiple threads simultaneously only
53 	//   if no shared data is accessed. Yes, even emContext is not
54 	//   thread-safe.
55 	// - The objects of container classes like emString, emArray, emList and
56 	//   emImage are sharing data implicitly. You cannot protect that data
57 	//   with a mutex. Instead, these classes have a method named
58 	//   MakeNonShared which must always be called before giving the object
59 	//   to another thread.
60 	// - Multi-threading provides many pitfalls, and the correctness of
61 	//   multi-threaded programs cannot be proved by testing very well.
62 	// - Alternative concepts are: emEngine, emProcess.
63 
64 	emThread();
65 		// Construct a thread object where the thread is not yet
66 		// running.
67 
68 	virtual ~emThread();
69 		// If the thread is still running, WaitForTermination() is
70 		// called.
71 
72 	void Start(void * arg);
73 		// This is a short cut for Start(NULL,arg). Should only be used
74 		// if Run has been overloaded.
75 
76 	void Start(int (* func)(void *), void * arg);
77 		// Start this thread. The thread calls the method Run, and the
78 		// default Implementation of that method calls the function
79 		// func. arg is a custom pointer forwarded to Run and func.
80 
81 	static void StartUnmanaged(int (* func)(void *), void * arg);
82 		// This function is like the method Start, but the new thread is
83 		// not managed by an emThread object and the function func is
84 		// called directly instead of Run. Yes, the return value of func
85 		// has no meaning and exists only for compatibility with managed
86 		// threads.
87 
88 	bool WaitForTermination(unsigned timeoutMS=UINT_MAX);
89 		// Wait for the thread to terminate.
90 		// Arguments:
91 		//   timeoutMS - Time-out in milliseconds. UINT_MAX means
92 		//               infinite.
93 		// Returns:
94 		//   true if the thread has terminated (or never started), or
95 		//   false on time-out.
96 
97 	bool IsRunning();
98 		// Whether the thread has been started and not yet exited.
99 
100 	int GetExitStatus() const;
101 		// Get the return value of a terminated thread.
102 
103 	emThreadId GetThreadId() const;
104 		// Get the identity number of this thread, if running.
105 
106 	static emThreadId GetCurrentThreadId();
107 		// Get the identity number of the calling thread.
108 
109 	static void ExitCurrentThread(int exitStatus);
110 		// Exit the calling thread.
111 
112 	static int GetHardwareThreadCount();
113 		// Maximum number of threads the hardware can run concurrently.
114 
115 protected:
116 
117 	virtual int Run(void * arg);
118 		// The default implementation calls the function given with the
119 		// Start method.
120 
121 private:
122 
123 	friend class emThreadPrivate;
124 	emThreadPrivate * P;
125 };
126 
Start(void * arg)127 inline void emThread::Start(void * arg)
128 {
129 	Start(NULL,arg);
130 }
131 
IsRunning()132 inline bool emThread::IsRunning()
133 {
134 	return !WaitForTermination(0);
135 }
136 
137 
138 //==============================================================================
139 //============================ emThreadMutexLocker =============================
140 //==============================================================================
141 
142 template <class OBJ> class emThreadMutexLocker : public emUncopyable {
143 
144 public:
145 
146 	// This is a template class for a locker which simply locks a mutex for
147 	// the time the locker exists. The template parameter OBJ describes the
148 	// class of the mutex.
149 
150 	emThreadMutexLocker(OBJ & mutex);
151 		// Calls Lock() on the given mutex.
152 		// Arguments:
153 		//   mutex - The mutex to be locked by this locker.
154 
155 	~emThreadMutexLocker();
156 		// Calls Unlock() on the mutex.
157 
158 	OBJ & GetMutex() const;
159 		// Get the mutex which is locked by this locker.
160 
161 private:
162 
163 	OBJ & Mutex;
164 };
165 
emThreadMutexLocker(OBJ & mutex)166 template <class OBJ> inline emThreadMutexLocker<OBJ>::emThreadMutexLocker(
167 	OBJ & mutex
168 )
169 	: Mutex(mutex)
170 {
171 	Mutex.Lock();
172 }
173 
~emThreadMutexLocker()174 template <class OBJ> inline emThreadMutexLocker<OBJ>::~emThreadMutexLocker()
175 {
176 	Mutex.Unlock();
177 }
178 
GetMutex()179 template <class OBJ> inline OBJ & emThreadMutexLocker<OBJ>::GetMutex() const
180 {
181 	return Mutex;
182 }
183 
184 
185 //==============================================================================
186 //============================= emThreadMiniMutex ==============================
187 //==============================================================================
188 
189 class emThreadMiniMutex : public emUncopyable {
190 
191 public:
192 
193 	// Class for a mutual exclusion variable with minimum costs. It could
194 	// also be called a "spin lock". This type of mutex may perform some
195 	// kind of busy waiting, and it may be unfair. Therefore it should be
196 	// used for very short critical sections only, so that it is very
197 	// unlikely that a thread really has to wait. This mutex also does not
198 	// support recursive locks.
199 
200 	emThreadMiniMutex();
201 	~emThreadMiniMutex();
202 
203 	void Lock();
204 		// Lock this mutex. Only one thread can have the mutex locked at
205 		// a time. Therefore this method may block in order to wait for
206 		// unlocked state. It may be a kind of busy waiting (yielding to
207 		// another thread again and again).
208 
209 	void Unlock();
210 		// Unlock this mutex.
211 
212 	typedef emThreadMutexLocker<emThreadMiniMutex> Locker;
213 		// A locker class for this mutex class.
214 
215 private:
216 
217 	union {
218 		emInt64 Val;
219 		void * Ptr;
220 	};
221 };
222 
223 
224 //==============================================================================
225 //=============================== emThreadEvent ================================
226 //==============================================================================
227 
228 class emThreadEvent : public emUncopyable {
229 
230 public:
231 
232 	// Class for an unary event that can be used for thread communication.
233 	// It is much like a classic semaphore, but the methods are named "Send"
234 	// and "Receive" instead of "Increment" and "Decrement".
235 
236 	emThreadEvent();
237 		// Construct with no pending events.
238 
239 	emThreadEvent(int count);
240 		// Construct with an initial number of pending event, or, if
241 		// count is negative, with an initial phantom receiver. See
242 		// SetCount.
243 
244 	~emThreadEvent();
245 		// Destructor.
246 
247 	emInt64 Send(emInt64 n=1);
248 		// Send this event n times. If one or more threads are blocked
249 		// in Receive, they are served accordingly. A negative n means
250 		// to decrease the internal counter of pending events (which can
251 		// be negative) - it acts like a phantom receiver which has
252 		// precedence over all the other receivers.
253 		// Arguments:
254 		//   n         - How many events to send. A negative n acts like
255 		//               a phantom receiver.
256 		// Returns:
257 		//   Same as GetCount.
258 
259 	bool Receive(emInt64 n=1, unsigned timeoutMS=UINT_MAX);
260 		// Receive this event n times. If there are at least n pending
261 		// events, this method returns immediately. Otherwise this
262 		// thread is added to an internal queue and it sleeps until it
263 		// is removed from the queue by Send, or until the time-out
264 		// elapses.
265 		// Arguments:
266 		//   n         - How many events to receive. A negative n acts
267 		//               exactly like Send(-n).
268 		//   timeoutMS - Time-out in milliseconds. UINT_MAX means
269 		//               infinite.
270 		// Returns:
271 		//   true if the events have been received, or false on
272 		//   time-out. On time-out, absolute no event is removed from
273 		//   the internal counter.
274 
275 	emInt64 GetCount() const;
276 		// Get the number of pending sendings of this event. A negative
277 		// value indicates waiting receivers.
278 
279 	void SetCount(emInt64 count);
280 		// Same as Send(count-GetCount()), but atomically.
281 
282 	void Clear();
283 		// Same as SetCount(0).
284 
285 private:
286 
287 	void UpdateReceivers();
288 
289 	emThreadMiniMutex Mutex;
290 	emInt64 Count;
291 	emThreadEventReceiver * Ring;
292 };
293 
Clear()294 inline void emThreadEvent::Clear()
295 {
296 	SetCount(0);
297 }
298 
299 
300 //==============================================================================
301 //=============================== emThreadMutex ================================
302 //==============================================================================
303 
304 class emThreadMutex : private emUncopyable {
305 
306 public:
307 
308 	// Class for a normal mutual exclusion variable. Its properties are:
309 	// - The lock methods support a time-out.
310 	// - Waiting is non-busy.
311 	// - Waiting is fair (first come, first serve).
312 	// - There are methods for solving the readers/writers problem. This
313 	//   means, the mutex can be locked by either a single thread for
314 	//   read/write access, or by one or more threads for read-only
315 	//   access. If you don't need that, simply don't use the *ReadOnly
316 	//   methods.
317 	// - This mutex does not support recursive locks. A thread would
318 	//   dead-lock when trying to lock the mutex more than once (except when
319 	//   locking for read-only access).
320 
321 	emThreadMutex();
322 	~emThreadMutex();
323 
324 	bool Lock(unsigned timeoutMS=UINT_MAX);
325 		// Lock this mutex (for read/write access). Only one thread can
326 		// have the mutex locked this way at a time, without any
327 		// read-access locks in parallel. Therefore this method may
328 		// block in order to wait for unlocked state.
329 		// Arguments:
330 		//   timeoutMS - Time-out in milliseconds. UINT_MAX means
331 		//               infinite.
332 		// Returns:
333 		//   true if the mutex has been locked, false on time-out.
334 
335 	void Unlock();
336 		// Unlock this mutex (from read/write access).
337 
338 	bool IsLocked() const;
339 		// Whether this mutex is currently locked (for read/write
340 		// access).
341 
342 	bool LockReadOnly(unsigned timeoutMS=UINT_MAX);
343 		// Lock this mutex for read-only access. Multiple threads
344 		// can have locked the mutex this way simultaneously. If a
345 		// thread has locked the mutex for read/write access (with
346 		// Lock), this method may block in order to wait for unlocked
347 		// state.
348 		// Arguments:
349 		//   timeoutMS - Time-out in milliseconds. UINT_MAX means
350 		//               infinite.
351 		// Returns:
352 		//   true if the mutex has been locked, false on time-out.
353 
354 	void UnlockReadOnly();
355 		// Unlock this mutex from read/write access.
356 
357 	bool IsLockedAnyhow() const;
358 		// Whether this mutex is currently locked for any access.
359 
360 	typedef emThreadMutexLocker<emThreadMutex> Locker;
361 		// A locker class for this mutex class.
362 
363 	class ReadOnlyLocker : public emUncopyable {
364 	public:
365 		// A read-only locker class for this mutex class.
366 		ReadOnlyLocker(emThreadMutex & mutex);
367 		~ReadOnlyLocker();
368 		emThreadMutex & GetMutex() const;
369 	private:
370 		emThreadMutex & Mutex;
371 	};
372 
373 private:
374 
375 	enum { MAX_COUNT = 2147483647 };
376 	emThreadEvent Event;
377 };
378 
Lock(unsigned timeoutMS)379 inline bool emThreadMutex::Lock(unsigned timeoutMS)
380 {
381 	return Event.Receive(MAX_COUNT,timeoutMS);
382 }
383 
IsLocked()384 inline bool emThreadMutex::IsLocked() const
385 {
386 	return Event.GetCount()<=0;
387 }
388 
LockReadOnly(unsigned timeoutMS)389 inline bool emThreadMutex::LockReadOnly(unsigned timeoutMS)
390 {
391 	return Event.Receive(1,timeoutMS);
392 }
393 
IsLockedAnyhow()394 inline bool emThreadMutex::IsLockedAnyhow() const
395 {
396 	return Event.GetCount()<MAX_COUNT;
397 }
398 
ReadOnlyLocker(emThreadMutex & mutex)399 inline emThreadMutex::ReadOnlyLocker::ReadOnlyLocker(emThreadMutex & mutex)
400 	: Mutex(mutex)
401 {
402 	Mutex.LockReadOnly();
403 }
404 
~ReadOnlyLocker()405 inline emThreadMutex::ReadOnlyLocker::~ReadOnlyLocker()
406 {
407 	Mutex.UnlockReadOnly();
408 }
409 
GetMutex()410 inline emThreadMutex & emThreadMutex::ReadOnlyLocker::GetMutex() const
411 {
412 	return Mutex;
413 }
414 
415 
416 //==============================================================================
417 //=========================== emThreadRecursiveMutex ===========================
418 //==============================================================================
419 
420 class emThreadRecursiveMutex : public emUncopyable {
421 
422 public:
423 
424 	// This is just like emThreadMutex, but it supports recursive locks, and
425 	// therefore it lacks the readers/writers solution (having the whole
426 	// luxury would be very costly).
427 
428 	emThreadRecursiveMutex();
429 	~emThreadRecursiveMutex();
430 
431 	bool Lock(unsigned timeoutMS=UINT_MAX);
432 	void Unlock();
433 	bool IsLocked() const;
434 
435 	typedef emThreadMutexLocker<emThreadRecursiveMutex> Locker;
436 
437 private:
438 
439 	emThreadEvent Event;
440 	emThreadMiniMutex Mutex;
441 	emThreadId ThreadId;
442 	int LockCount;
443 };
444 
IsLocked()445 inline bool emThreadRecursiveMutex::IsLocked() const
446 {
447 	return Event.GetCount()<=0;
448 }
449 
450 
451 #endif
452