ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
thread.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !DOXYGEN
12# include <condition_variable>
13# include <atomic>
14# if ALIB_ENUMS
17# endif
18#endif // !DOXYGEN
19
20
21namespace alib {
22
23//==================================================================================================
24/// This is the reference documentation of sub-namespace \c threads of the \aliblink, which
25/// holds types of library module \alib_threads.
26///
27/// \attention
28/// This module must not be omitted from an \alibdist if the using software does not make
29/// direct use of this module. If omitted, all other \alibmods will <b>silently (!)</b> drop
30/// the protection of their resources against multithreaded access.<br>
31///
32/// Further documentation is provided with
33/// - \ref alib_mod_threads "ALib Module Threads - Programmer's Manual".
34/// - Chapter \ref alib_manual_modules_impact_singlethreaded of the Programmer's Manual of \alib.
35///
36//==================================================================================================
37namespace threads {
38
39// #################################################################################################
40// Anonymous data
41// #################################################################################################
42#if !DOXYGEN
43namespace {
44
45 ThreadID nextSystemThreadId = static_cast<ThreadID>(-1);
46 std::atomic<ThreadID> nextThreadIdx(1);
47 Thread* MAIN_THREAD = nullptr;
48
49} // anonymous namespace
50#endif
51
52
53// #################################################################################################
54// Details
55// #################################################################################################
56/// Details of namespace #alib::threads.
57namespace detail {
58
59#if (ALIB_MONOMEM && ALIB_CONTAINERS) || DOXYGEN
60 #if DOXYGEN
61 /// Directory which assigns system thread IDs to \alib Thread objects.
63 #else
65 #endif
66 #define LOCK_THREADS ALIB_LOCK_RECURSIVE_WITH( monomem::GLOBAL_ALLOCATOR_LOCK )
67#else
68 std::unordered_map<std::thread::id, Thread *> THREAD_MAP;
69 std::mutex MODULE_LOCK;
70 #define LOCK_THREADS std::unique_lock<std::mutex> uniqueLock( MODULE_LOCK );
71#endif
72
73void threadStart( Thread* thread )
74{
76 thread->Run();
77 thread->state = Thread::State::Done;
78}
79
81
82} // namespace alib::threads[::detail];
83
84using namespace detail;
85
86// #################################################################################################
87// Namespace functions
88// #################################################################################################
89#if ALIB_DEBUG && !DOXYGEN
90 namespace{ unsigned int initFlag= 0; }
91#endif // !DOXYGEN
92
94{
95 ALIB_ASSERT_ERROR( initFlag == 0, "THREADS", "This method must not be invoked twice." )
96 ALIB_DBG(initFlag= 0x92A3EF61);
97
98 // already invoked?
99 if( MAIN_THREAD )
100 return;
101
102 Thread* mainThread= new Thread();
103 mainThread->id= static_cast<ThreadID>(-1);
104 mainThread->SetName(A_CHAR("MAIN_THREAD"));
105 mainThread->state = Thread::State::Running;
106
107 #if ALIB_MONOMEM && ALIB_CONTAINERS
108 THREAD_MAP.EmplaceUnique( std::this_thread::get_id(), mainThread );
109 #else
110 THREAD_MAP.insert( std::make_pair( std::this_thread::get_id(), mainThread) );
111 #endif
112
113 // Assign enum records (not resourced, because Threads is not a Camp Module)
114 #if ALIB_ENUMS && ALIB_BOXING && !ALIB_CAMP
116 {
122 } );
123
124 #if ALIB_BOXING
126 #endif
127 #endif
128
129 MAIN_THREAD= mainThread; // to this only here, as this flags the availability
130}
131
132
134{
135 ALIB_ASSERT_ERROR( initFlag == 0x92A3EF61, "THREADS", "Not initialized when calling shutdown." )
136 ALIB_DBG(initFlag= 1);
137
138 // already invoked?
139 if( MAIN_THREAD == nullptr )
140 return;
141
142 {
143 LOCK_THREADS
144
145 // we should have exactly one thread and this is the system thread
146 #if ALIB_MONOMEM && ALIB_CONTAINERS
147 auto qtyThreads= THREAD_MAP.Size();
148 #else
149 auto qtyThreads= THREAD_MAP.size();
150 #endif
151
152 if( qtyThreads != 1 )
153 {
154 #if ALIB_DEBUG
155 String4K dbgThreadList("ALib Termination: More than one thread running: ");
156 dbgThreadList << static_cast<integer>(qtyThreads) << NEW_LINE;
157 int tNr= 0;
158 for( auto it : THREAD_MAP )
159 dbgThreadList << ++tNr << ": " << it.second << ",\tState::" <<
160 #if ALIB_ENUMS && ALIB_BOXING
161 it.second->state
162 #else
163 int(it.second->state )
164 #endif
165 << NEW_LINE;
166 ALIB_STRINGS_TO_NARROW(dbgThreadList, nstring, 4096)
167 ALIB_WARNING( "THREADS", nstring.Terminate() )
168 #endif
169
170 delete MAIN_THREAD;
171 MAIN_THREAD= nullptr;
172 return;
173 }
174
175 Thread* lastThread= THREAD_MAP.begin()->second;
176 ALIB_ASSERT_WARNING( lastThread->id == static_cast<ThreadID>(-1), "THREADS",
177 "threads::Shutdown: last thread is not the main system thread detected "
178 "in threads::Bootstrap" )
179 delete lastThread;
180
181 MAIN_THREAD= nullptr;
182 #if ALIB_MONOMEM && ALIB_CONTAINERS
183 THREAD_MAP.Reset();
184 #endif
185 }
186}
187
189
190// #################################################################################################
191// class Thread
192// #################################################################################################
193Thread::Thread( Runnable* target , const String& pName )
194: runnable(target)
195, name(pName)
196{
197 // get myself an ID
198 id= nextThreadIdx++;
199 if ( name.IsEmpty() )
200 {
201 // use local string to avoid higher capacity allocation.
202 name._( String128()<< '(' << id << ')');
203 }
204}
205
207{
209 "Thread \""<< this <<"\" destructed, while it was never started." )
210
211 if( c11Thread != nullptr )
212 {
213 ALIB_WARNING( "THREADS", NString512() <<
214 "Thread \"" << this << "\" was not terminated before destruction.\n"
215 "Use Thread::Join() to avoid this message. Joining now!..." )
216 Join();
217 }
218}
219
221{
222 if( c11Thread != nullptr )
223 {
224 if( state != State::Done )
225 {
226 NString512 msg;
227 msg << "Terminating thread \"" << this << "\" which is not in state 'Stopped'. State: '"
228 #if ALIB_ENUMS && ALIB_BOXING
229 << state << "'.";
230 #else
231 << int(state) << "'.";
232 #endif
233 ALIB_WARNING( "THREADS", msg.Terminate() )
234 }
235
236 // join
237 if( c11Thread->joinable() )
238 c11Thread->join();
239 else
240 {
241 NString512 msg;
242 msg << "Thread \"" << this << "\" not joinable. State: '"
243 #if ALIB_ENUMS && ALIB_BOXING
244 << state << "'.";
245 #else
246 << int(state) << "'.";
247 #endif
248
249 ALIB_WARNING( "THREADS", msg.Terminate() )
250 }
251
252 // erase from thread map
253 {
254 LOCK_THREADS
255
256 #if ALIB_MONOMEM && ALIB_CONTAINERS
258 #else
260 #endif
261 }
262
263 delete c11Thread;
264 c11Thread= nullptr;
266 }
267
268 // c11Thread == nullptr
269 else
270 {
271 if( state == State::Terminated )
272 ALIB_WARNING( "THREADS",
273 "Double invocation of Thread::Terminate for thread {!Q}",
274 NString256(this) )
275 else
276 ALIB_WARNING( "THREADS",
277 "Terminating thread {!Q} which is not started or not managed by ALIB",
278 NString256(this) )
279 }
280}
281
282
283
285{
286 if ( c11Thread != nullptr )
287 {
288 ALIB_ERROR( "THREADS", NString128("Thread already started. ID: ")
289 << static_cast<integer>(GetID()) )
290 return;
291 }
292
293 if ( id <= 0 )
294 {
295 ALIB_ERROR( "THREADS", NString128("System threads cannot be started. ID: ")
296 << static_cast<integer>(GetID()) )
297 return;
298 }
299
301
302 {
303 LOCK_THREADS // <- locks on monomem::GLOBAL_ALLOCATOR_LOCK or, without monomem, on singleton "MODULE_LOCK"
304 c11Thread= new std::thread( threadStart, this );
305 nativeID= c11Thread->get_id();
306
307 #if ALIB_MONOMEM && ALIB_CONTAINERS
308 THREAD_MAP.EmplaceUnique( nativeID, this );
309 #else
310 THREAD_MAP.insert( std::make_pair( nativeID, this) );
311 #endif
312 }
313}
314
315
316// #################################################################################################
317// static methods
318// #################################################################################################
320Thread* Thread::GetMain() { return MAIN_THREAD; }
321Thread* Thread::Get(std::thread::id nativeID )
322{
323 // check for nulled nativeID or not initialized
324 if( lang::IsNull(nativeID) || !MAIN_THREAD )
325 return nullptr ;
326
327 Thread* result= nullptr;
328
329 // search current in map
330 {
331 LOCK_THREADS
332 #if ALIB_MONOMEM && ALIB_CONTAINERS
333 auto search= THREAD_MAP.Find( nativeID );
334 #else
335 auto search= THREAD_MAP.find( nativeID );
336 #endif
337
338 // found
339 if ( search != THREAD_MAP.end() )
340 result= search->second;
341
342 // not found, this is a system thread!
343 else
344 {
345 result = new Thread( reinterpret_cast<Runnable*>(-1), nullptr );
346 result->id = nextSystemThreadId--;
347 result->state = Thread::State::Running; // we just "guess" here, as documented
348 result->SetName( String64(A_CHAR("SYS_")) << result->id ); // with method Thread::GetState()
349 #if ALIB_MONOMEM && ALIB_CONTAINERS
350 THREAD_MAP.EmplaceUnique( nativeID, result );
351 #else
352 THREAD_MAP.insert( std::make_pair( nativeID, result) );
353 #endif
354 }
355 }
356
357 return result;
358}
360
361
362
363}} // namespace [alib::threads]
364
366{ target << src->GetName() << '(' << src->GetID() << ')';} )
367
369{ target << src.GetName() << '(' << src.GetID() << ')';} )
370
372{ target << src->GetName() << '(' << src->GetID() << ')';} )
373
375{ target << src.GetName() << '(' << src.GetID() << ')';} )
376
constexpr const TChar * Terminate() const
Definition tastring.inl:821
constexpr bool IsEmpty() const
Definition string.hpp:383
virtual ALIB_API void Start()
Definition thread.cpp:284
ThreadID id
The id of the thread.
Definition thread.hpp:138
virtual void SetName(const String &newName)
Definition thread.hpp:233
virtual ALIB_API ~Thread() override
Definition thread.cpp:206
@ Running
The thread's Run method is currently processed.
@ Started
Method Start was invoked but not running, yet.
@ Terminated
The thread is terminated.
ThreadID GetID() const
Definition thread.hpp:207
AString name
The name of the thread.
Definition thread.hpp:141
State state
Internal flag to indicate if the thread is alive.
Definition thread.hpp:144
std::thread * c11Thread
The internal C++ thread object.
Definition thread.hpp:129
static ALIB_API Thread * GetMain()
Definition thread.cpp:320
ALIB_API Thread(const String &pName=EMPTY_STRING)
Definition thread.hpp:160
virtual void Run() override
Definition thread.hpp:195
static ALIB_API Thread * Get(std::thread::id nativeID)
Definition thread.cpp:321
virtual ALIB_API void Join()
Definition thread.cpp:220
std::thread::id nativeID
The internal C++ thread id.
Definition thread.hpp:132
#define ALIB_WARNING(...)
Definition alib.hpp:1268
#define ALIB_ASSERT_RESULT_EQUALS( func, value)
Definition alib.hpp:1286
#define ALIB_STRINGS_APPENDABLE_TYPE_DEF_W(TYPE, IMPL)
Definition tastring.inl:169
#define A_CHAR(STR)
#define ALIB_STRINGS_TO_NARROW( src, dest, bufSize)
#define ALIB_ERROR(...)
Definition alib.hpp:1267
#define ALIB_BOXING_BOOTSTRAP_REGISTER_FAPPEND_FOR_APPENDABLE_TYPE(TAppendable)
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:1271
#define ALIB_STRINGS_APPENDABLE_TYPE_DEF_N(TYPE, IMPL)
Definition tastring.inl:165
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:1272
#define ALIB_DBG(...)
Definition alib.hpp:390
constexpr bool IsNull(const T &t)
ALIB_API MonoAllocator GLOBAL_ALLOCATOR
ALIB_API std::mutex MODULE_LOCK
void threadStart(Thread *thread)
Definition thread.cpp:73
ALIB_API HashMap< MonoAllocator, std::thread::id, Thread * > THREAD_MAP
Directory which assigns system thread IDs to ALib Thread objects.
Definition thread.cpp:62
void Bootstrap()
Definition thread.cpp:93
integer ThreadID
The ALib thread identifier type.
Definition loxpimpl.inl:28
void Shutdown()
Definition thread.cpp:133
Definition alib.cpp:69
LocalString< 64 > String64
Type alias name for TLocalString<character,64>.
NLocalString< 128 > NString128
Type alias name for TLocalString<nchar,128>.
threads::Thread Thread
Type alias in namespace alib.
Definition thread.hpp:379
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256>.
constexpr CString NEW_LINE
A zero-terminated string containing the new-line character sequence.
Definition cstring.hpp:580
NLocalString< 512 > NString512
Type alias name for TLocalString<nchar,512>.
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273
containers::HashMap< TAllocator, TKey, TMapped, THash, TEqual, THashCaching, TRecycling > HashMap
Type alias in namespace alib.
static void Bootstrap(TEnum element, TArgs &&... args) noexcept