Library of Assembled Shared Sources
thread.h
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2024 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43/**
44* @defgroup Threading Threading
45* Threading functions. The Mutex and Thread classes for WIN32 are almost literal copy and pastes
46* of the functions of wxWindows2.4.2. The implementation has changed a little bit to
47* use more of the LASS utility functions. The most noticible difference is in the thread
48* class where the Delete is not implemented, due to too large dependencies. The Critical
49* Section on WIN32 is an own implementation, just like all the UNIX Posix threading stuff.
50*
51* A little word on synchronization: in windows there are quite a few synchronisation
52* primitives and synchronized objects. Only the most common are implemented and wrapped
53* in the LASS environment:
54* Mutex : system-wide synchronisation object between processes/threads. Mutexes set in one process can be
55* unlocked in another one. That is the theory. In practice with the LASS
56* wrapping, there is no possibility to reference these mutexes in other
57* processes, named mutexes are not supported. Mutex are rather heavyweight
58* structures. Cannot be used for intrathread synchronisation AFAIK [TDM]
59* CriticalSection : process-wide synchronisation object between threads. Lightweight
60* construction. Cannot be used for intra-thread synchronisation.
61* Condition: this corresponds with MS Windows Events, can be used for interprocess/
62* interthread and intrathread synchronisation. Not implemented in UNIX build.
63*/
64
65#ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_THREAD_H
66#define LASS_GUARDIAN_OF_INCLUSION_UTIL_THREAD_H
67
68#include "util_common.h"
69#include "non_copyable.h"
70#include "atomic.h"
71
72namespace lass
73{
74namespace util
75{
76namespace impl
77{
78 class MutexInternal;
79 class ConditionInternal;
80 class ThreadInternal;
81 class ThreadLocalStorageInternal;
82}
83
84/** Return code for lock functions
85 * @ingroup Threading
86 * @see Mutex,CriticalSection
87 */
89{
90 lockSuccess, /**< Mutex/CriticalSection is succesfully locked by this thread */
91 lockBusy /**< Mutex/CriticalSection is locked by another thread */
92};
93
94/** Return code for wait functions
95 * @ingroup Threading
96 * @see Condition
97 */
99{
100 waitSuccess, /**< Wait is successfully terminated */
101 waitTimeout /**< Wait failed because of a timeout */
102};
103
104/** ThreadKind.
105* @ingroup Threading
106* @see Thread
107*/
109{
110 threadDetached, /**< detached thread */
111 threadJoinable /**< joinable thread, can be waited for */
112};
113
114typedef std::vector<bool> TCpuSet;
115
116LASS_DLL const TCpuSet availableProcessors();
117
118/** Return highest id of processor + 1, in this machine
119 * @ingroup Threading
120 */
122
123/** Return total number of processors in machine that are online
124 * @ingroup Threading
125 */
127
128/** Check whether a processor is avaialable.
129 * @ingroup Threading
130 */
131LASS_DLL bool isAvailableProcessor(size_t processor);
132
133/** Mutex.
134* @ingroup Threading
135* @see MutexLocker
136* A mutex is a systemwide synchronization object. It is heavier in use than a critical
137* section. For exception-safe basic use, the MutexLocker can be used.
138*/
139class LASS_DLL Mutex : NonCopyable
140{
141public:
142 Mutex();
143 ~Mutex();
144 void lock();
145 LockResult tryLock();
146 void unlock();
147 bool isLocked() const;
148
149private:
150 impl::MutexInternal* pimpl_;
151};
152
153/** CriticalSection.
154* @ingroup Threading
155* @see CriticalSectionLocker
156* @author Tom De Muer [TDM]
157* @date 2004
158* Process wide synchronization object. The recommended object for interthread synchronisation
159* within a process in Windows.
160*/
161class LASS_DLL CriticalSection : NonCopyable
162{
163public:
164 CriticalSection();
165 ~CriticalSection();
166
167 void lock();
168 LockResult tryLock();
169 void unlock();
170
171 bool isLocked() const;
172
173private:
174 void* pimpl_;
175};
176
177
178
179/** Condition.
180* @ingroup Threading
181* A condition can be used to synchronize using messages were a condition waits for
182* a signal or broadcast. A signal will only release one waiter, a broadcast will release
183* all current waiting wait-conditions.
184*/
185class LASS_DLL Condition: NonCopyable
186{
187public:
188 Condition();
189 ~Condition();
190 void wait();
191 WaitResult wait(unsigned long iMilliSeconds);
192 void signal();
193 void broadcast();
194private:
195 impl::ConditionInternal* pimpl_;
196};
197
198/** A base class for threads.
199* @ingroup Threading
200* The virtual function doRun() needs to be overriden and after creation of the
201* thread on the heap or stack, the thread is in a state ready to be run.
202* The run() function actually starts the thread.
203*
204* JOINABLE threads can be waited for, DETACHED threads can not be waited for.
205*/
206class LASS_DLL Thread: NonCopyable
207{
208public:
209
210 constexpr static size_t anyProcessor = size_t(-1); /**< argument for Thread::bind to unbind the thread so it runs on any processor */
211
212 virtual ~Thread();
213
214 void run();
215 void join();
216 void bind(size_t processor);
217 const TCpuSet affinity() const;
218
219
220 static void sleep(unsigned long milliseconds);
221 static void yield();
222 static void bindCurrent(size_t processor);
223
224protected:
225
226 Thread(ThreadKind kind = threadDetached, const char* name = 0);
227
228private:
229
230 friend class impl::ThreadInternal;
231
232 virtual void doRun() = 0;
233
234 impl::ThreadInternal* pimpl_;
235};
236
237
238
239/** A primitive to provide Thread Local Storage functionality
240 * @ingroup Threading
241 */
242class LASS_DLL ThreadLocalStorage: NonCopyable
243{
244public:
245 ThreadLocalStorage(void (*destructor)(void*) = 0);
246 ~ThreadLocalStorage();
247 void* get() const;
248 void set(void* value);
249private:
250 impl::ThreadLocalStorageInternal* pimpl_;
251};
252
253
254
255
256/** A primitive to provide Thread Local Storage functionality for a first-citizen class.
257 * @ingroup Threading
258 *
259 * @arg requirements: T must be copyconstructible.
260 *
261 * @warning
262 * On Win32 platform, you should _access_ a ThreadLocalVariable inside code running
263 * from a util::Thread (or util::threadFun for that matter). This is because the destructor
264 * of the variable will _only_ be called on exit of a util::Thread. If you access it from
265 * any other thread (even the main thread), the variable will correctly constructed for that
266 * thread, but it will _never_ be destructed!
267 * On a POSIX platform, this is not a problem though! The destructor will always be called.
268 */
269template <typename T>
270class ThreadLocalVariable: NonCopyable
271{
272public:
273 typedef typename CallTraits<T>::TValue TValue;
274 typedef typename CallTraits<T>::TParam TParam;
275 typedef typename CallTraits<T>::TConstReference TConstReference;
276 typedef typename CallTraits<T>::TReference TReference;
277
278 explicit ThreadLocalVariable(TParam prototype = T()):
279 prototype_(prototype),
280 storage_(&ThreadLocalVariable<T>::destructor)
281 {
282 }
283
284 TValue* get()
285 {
286 TValue* ptr = static_cast<TValue*>(storage_.get());
287 if (!ptr)
288 {
289 TUniquePtr newCopy(new TValue(prototype_));
290 storage_.set(newCopy.get());
291 ptr = newCopy.release();
292 }
293 return ptr;
294 }
295
296 const TValue* get() const
297 {
298 const TValue* ptr = static_cast<const TValue*>(storage_.get());
299 if (!ptr)
300 {
301 TUniquePtr newCopy(new TValue(prototype_));
302 storage_.set(newCopy.get());
303 ptr = newCopy.release();
304 }
305 return ptr;
306 }
307
308 TValue* operator->()
309 {
310 return get();
311 }
312
313 const TValue* operator->() const
314 {
315 return get();
316 }
317
318 TReference operator*()
319 {
320 return *get();
321 }
322
323 TConstReference operator*() const
324 {
325 return *get();
326 }
327
328private:
329
330 typedef std::unique_ptr<T> TUniquePtr;
331
332 static void destructor(void* p)
333 {
334 delete static_cast<T*>(p);
335 }
336
337 TValue prototype_;
338 mutable ThreadLocalStorage storage_;
339};
340
341
342
343template <typename T>
344class ThreadLocalPtr: NonCopyable
345{
346public:
347 typedef T TPointee;
348 typedef T* TPointer;
349 typedef T& TReference;
350
351 explicit ThreadLocalPtr():
352 storage_(&ThreadLocalPtr<T>::destructor)
353 {
354 }
355
356 void reset(TPointer p)
357 {
358 TPointer old = get();
359 storage_.set(p);
360 delete old;
361 }
362
363 template <typename U> void reset(std::unique_ptr<U>&& p)
364 {
365 TPointer old = get();
366 storage_.set(p.get());
367 p.release();
368 delete old;
369 }
370
371 TPointer get() const
372 {
373 return static_cast<TPointer>(storage_.get());
374 }
375
376 TPointer operator->() const
377 {
378 LASS_ASSERT(get());
379 return get();
380 }
381
382 TReference operator*() const
383 {
384 LASS_ASSERT(get());
385 return *get();
386 }
387
388 bool operator!() const
389 {
390 return !get();
391 }
392
393private:
394
395 static void destructor(void* p)
396 {
397 delete static_cast<T*>(p);
398 }
399
400 mutable ThreadLocalStorage storage_;
401};
402
403
404
405/** Lean and mean synchronisation object, without OS support.
406* @ingroup Threading
407* @see SemaphoreLocker
408* @author Bram de Greve
409* @date 2004
410*
411* This semaphore is built upon atomic operations that are programmed in assembly.
412*/
413class Semaphore: NonCopyable
414{
415public:
416 Semaphore(int numberOfSlots = 1): freeSlots_(numberOfSlots) {}
417 void lock() { atomicLock(freeSlots_); }
418 LockResult tryLock() { return atomicTryLock(freeSlots_) ? lockSuccess : lockBusy; }
419 void unlock() { atomicUnlock(freeSlots_); }
420 bool isLocked() const { return freeSlots_ == 0; }
421
422private:
423 std::atomic<int> freeSlots_;
424};
425
426
427
428
429/** Common base class for lockers
430 * @ingroup Threading
431 */
432class LockerBase
433{
434public:
435 bool hasLock() const { return hasLock_; }
436 operator bool() const { return hasLock_; }
437protected:
438 LockerBase(bool hasLock = true): hasLock_(hasLock) {}
439private:
440 bool hasLock_;
441};
442
443
444
445template <typename LockType>
446struct DefaultLockTraits
447{
448 static bool lock(LockType& x) { x.lock(); return true; }
449 static void unlock(LockType& x) { x.unlock(); }
450};
451
452template <typename LockType>
453struct TryLockTraits
454{
455 static bool lock(LockType& x) { return x.tryLock() == lockSuccess; }
456 static void unlock(LockType& x) { x.unlock(); }
457};
458
459template <typename LockType>
460struct IntegralLockTraits
461{
462 static bool lock(LockType& x) { atomicLock(x); return true; }
463 static void unlock(LockType& x) { atomicUnlock(x); }
464};
465
466
467/** @ingroup Threading
468 */
469template
470<
471 typename LockType,
472 typename LockTraits = DefaultLockTraits<LockType>
473>
474class Locker: public LockerBase
475{
476public:
477 typedef LockType TLock;
478 typedef LockTraits TLockTraits;
479 Locker(TLock& iLock):
480 LockerBase(TLockTraits::lock(iLock)),
481 lock_(&iLock)
482 {
483 }
484 ~Locker()
485 {
486 if (!hasLock())
487 {
488 return;
489 }
490 LASS_ASSERT(lock_);
491 try
492 {
493 TLockTraits::unlock(*lock_);
494 }
495 catch (const std::exception& error)
496 {
497 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: "
498 << "exception thrown in ~Locker(): " << error.what() << std::endl;
499 }
500 catch (...)
501 {
502 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: "
503 << "unknown exception thrown in ~Locker()" << std::endl;
504 }
505 }
506 const TLock& mutex() const
507 {
508 LASS_ASSERT(lock_);
509 return *lock_;
510 }
511 void swap(Locker& iOther)
512 {
513 std::swap(lock_, iOther.lock_);
514 }
515private:
516 TLock* lock_;
517};
518
519
520
521/** @ingroup Threading
522 * @relates Locker
523 */
524template <typename T>
525inline Locker<T, DefaultLockTraits<T> > makeLocker(T& iLock)
526{
527 return Locker<T, DefaultLockTraits<T> >(iLock);
528}
529
530
531
532/** @ingroup Threading
533 * @relates Locker
534 */
535template <typename T>
536inline Locker<T, TryLockTraits<T> > makeTryLocker(T& iLock)
537{
538 return Locker<T, TryLockTraits<T> >(iLock);
539}
540
541
542
543/** @ingroup Threading
544 * @relates Locker
545 */
546template <typename T>
547inline Locker<T, IntegralLockTraits<T> > makeIntegralLocker(T& islots)
548{
549 return Locker<T, IntegralLockTraits<T> >(islots);
550}
551
552
553
554/** typedef of Locker for Mutex
555 * @ingroup Threading
556 * @sa Mutex
557 * @sa Locker
558*/
559typedef Locker<Mutex> MutexLocker;
560
561/** typedef of Locker for CriticalSection
562 * @ingroup Threading
563 * @sa CriticalSection
564 * @sa Locker
565*/
566typedef Locker<CriticalSection> CriticalSectionLocker;
567
568/** typedef of Locker for Semaphore
569 * @ingroup Threading
570 * @sa Semaphore
571 * @sa Locker
572*/
573typedef Locker<Semaphore> SemaphoreLocker;
574
575}
576
577}
578
579/** @brief Locks a @a iLock and starts a scope block in which it remains locked.
580 * @author [Bramz]
581 * @ingroup Threading
582 *
583 * This macro starts a scope block through a @c if/else construct where the lock will be locked
584 * on entering the scope, and will be unlocked on leaving the scope. You can use this scope in two
585 * ways: by using the braces:
586 *
587 * @code
588 * LASS_LOCK(lock)
589 * {
590 * // locked
591 * doSomethingWhileLocked();
592 * }
593 * // unlocked
594 * @endcode
595 *
596 * Because the macro uses an @c if/else construct, you can ommit the braces if you only need one
597 * statement in the locked scope:
598 *
599 * @code
600 * LASS_LOCK(lock) doSomethingWhileLocked();
601 * @endcode
602 *
603 * @par requirements for @a iLock:
604 * @a iLock can be of any type that implements two methods @c lock() and @c unlock(), that have
605 * to do the obvious thing. Two excellent types to be used are lass::util::Mutex and
606 * lass::util::CriticalSection.
607 *
608 * @par references:
609 * <i>Concurrent Acces Control & C++</i>, C/C++ Users Journal, January 2004
610 *
611 * @note Guess what, for some reason bill gates might know,
612 * @c LASS_UNIQUENAME(lassUtilImplGenericLocker) causes troubles in the @c win32_vc7_d build.
613 * huray! Does the concatenation fails? It seems not, since in the preprocessor files
614 * everything seems fine. And yet, it breaks. Hence, we had to give up on it, there's only
615 * @c lassUtilImplGenericLocker now. I don't think that will cause any problems, since it's
616 * inside an @c if/else scope block. But if you get name conflicts on LASS_LOCK, this might
617 * be the reason ...
618 */
619#define LASS_LOCK(iLock)\
620 if (const ::lass::util::LockerBase& LASS_UNUSED(lassUtilImplLocker) = ::lass::util::makeLocker(iLock))
621
622#define LASS_TRY_LOCK(iLock)\
623 if (const ::lass::util::LockerBase& LASS_UNUSED(lassUtilImplLocker) = ::lass::util::makeTryLocker(iLock))
624
625#define LASS_LOCK_INTEGRAL(iSlots)\
626 if (const ::lass::util::LockerBase& LASS_UNUSED(lassUtilImplLocker) = ::lass::util::makeIntegralLocker(iSlots))
627
628#endif
Common base class for lockers.
Definition thread.h:433
use as base class if derived should not be copyable
A primitive to provide Thread Local Storage functionality.
Definition thread.h:243
static void bindCurrent(size_t processor)
bind current thread to a processor (current as in callee's context)
Definition thread.cpp:287
static constexpr size_t anyProcessor
argument for Thread::bind to unbind the thread so it runs on any processor
Definition thread.h:210
void bind(size_t processor)
bind this thread to a processor (this as in this-pointer)
Definition thread.cpp:263
Locker< CriticalSection > CriticalSectionLocker
typedef of Locker for CriticalSection
Definition thread.h:566
ThreadKind
ThreadKind.
Definition thread.h:109
bool isAvailableProcessor(size_t processor)
Check whether a processor is avaialable.
Definition thread.cpp:85
size_t numberOfAvailableProcessors()
Return total number of processors in machine that are online.
Definition thread.cpp:79
LockResult
Return code for lock functions.
Definition thread.h:89
size_t numberOfProcessors()
Return highest id of processor + 1, in this machine.
Definition thread.cpp:74
Locker< Semaphore > SemaphoreLocker
typedef of Locker for Semaphore
Definition thread.h:573
WaitResult
Return code for wait functions.
Definition thread.h:99
Locker< Mutex > MutexLocker
typedef of Locker for Mutex
Definition thread.h:559
@ threadJoinable
joinable thread, can be waited for
Definition thread.h:111
@ threadDetached
detached thread
Definition thread.h:110
@ lockSuccess
Mutex/CriticalSection is succesfully locked by this thread.
Definition thread.h:90
@ lockBusy
Mutex/CriticalSection is locked by another thread.
Definition thread.h:91
@ waitSuccess
Wait is successfully terminated.
Definition thread.h:100
@ waitTimeout
Wait failed because of a timeout.
Definition thread.h:101
void atomicLock(volatile T &semaphore)
Definition atomic.h:168
bool atomicTryLock(volatile T &semaphore)
Definition atomic.h:186
void atomicUnlock(volatile T &semaphore)
Definition atomic.h:209
#define LASS_DLL
DLL interface: import or export symbols?
general utility, debug facilities, ...
Library for Assembled Shared Sources.
Definition config.h:53