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