ALib C++ Library
Library Version: 2511 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
thread.cpp
1//##################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2025 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6//##################################################################################################
7#include "alib_precompile.hpp"
8#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
9# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
10#endif
11#if ALIB_C20_MODULES
12 module;
13#endif
14//========================================= Global Fragment ========================================
16#if !ALIB_SINGLE_THREADED
17# include <condition_variable>
18# include <atomic>
19#endif // !ALIB_SINGLE_THREADED
20
21#include <unordered_map>
22#include <future>
23#if ALIB_DEBUG
24# include <vector>
25# include <any>
26#endif
27//============================================== Module ============================================
28#if ALIB_C20_MODULES
29 module ALib.Threads;
30 import ALib.Lang;
31#if ALIB_CONTAINERS
32 import ALib.Containers.HashTable;
33#endif
34# if ALIB_BOXING
35 import ALib.Boxing;
36# endif
37# if ALIB_STRINGS
38 import ALib.Strings;
39# endif
40# if ALIB_MONOMEM
41 import ALib.Monomem;
42# endif
43# if ALIB_ENUMRECORDS
44 import ALib.EnumRecords;
45 import ALib.EnumRecords.Bootstrap;
46# endif
47# if ALIB_RESOURCES
48 import ALib.Resources;
49# endif
50#else
51# include "ALib.Threads.H"
52# include "ALib.Lang.H"
54# include "ALib.Boxing.H"
55# include "ALib.Strings.H"
56# include "ALib.Monomem.H"
57# include "ALib.EnumRecords.H"
59# include "ALib.Resources.H"
60#endif
61//========================================== Implementation ========================================
62#if !ALIB_SINGLE_THREADED
63namespace alib {
64
65//==================================================================================================
66/// This is the reference documentation of namespace threads of the \aliblink, which
67/// holds types of library module \alib_threads.
68///
69/// \attention
70/// At the moment the compiler-symbol \ref ALIB_SINGLE_THREADED is set with an \alibbuild,
71/// this module will remain in the build, but only as skeletons. Also, the corresponding
72/// preprocessor macros, like \ref ALIB_LOCK, are emptied.<br>
73/// This allows writing dual-use code which compiles in either mode, without checking
74/// symbol \b ALIB_SINGLE_THREADED in the using code too often.
75///
76/// Further documentation is provided with
77/// - \ref alib_mod_threads "ALib Module Threads - Programmer's Manual".
78/// - Details of CMake variable \ref ALIB_CMAKE_SKIP_THREAD_LIB_SEARCH.
79//==================================================================================================
80namespace threads {
81
82//##################################################################################################
83// Anonymous data
84//##################################################################################################
85#if !DOXYGEN
86namespace {
87
88 ThreadID nextSystemThreadId = static_cast<ThreadID>(-1);
89 std::atomic<ThreadID> nextThreadIdx(1);
90 Thread* MAIN_THREAD = nullptr;
91
92 #if ALIB_MONOMEM && ALIB_CONTAINERS
94 #define LOCK_THREADS ALIB_LOCK_RECURSIVE_WITH( monomem::GLOBAL_ALLOCATOR_LOCK )
95 #else
96 std::unordered_map< std::thread::id, Thread*> THREAD_MAP;
97 Lock MODULE_LOCK;
98 #define LOCK_THREADS ALIB_LOCK_WITH( MODULE_LOCK );
99 #endif
100} // anonymous namespace
101#endif
102
103
104//##################################################################################################
105// Details
106//##################################################################################################
107/// Details of namespace #alib::threads.
108namespace detail {
109
110void threadStart( Thread* thread ) {
111 THIS_THREAD= thread;
113 thread->Run();
114 thread->state = Thread::State::Done;
115}
116
117# include "ALib.Lang.CIFunctions.H"
118
119} // namespace alib::threads[::detail];
120
121using namespace detail;
122
123//##################################################################################################
124// Namespace functions
125//##################################################################################################
126#if ALIB_DEBUG && !DOXYGEN
127namespace{ unsigned initFlag= 0; }
128#endif // !DOXYGEN
129
131 #if ALIB_MONOMEM && ALIB_CONTAINERS
132 THREAD_MAP.Reserve( qty, lang::ValueReference::Absolute );
133 #else
134 THREAD_MAP.reserve( size_t(qty) );
135 #endif
136}
137void bootstrap() {
138 ALIB_ASSERT_ERROR( initFlag == 0, "THREADS", "This method must not be invoked twice." )
139 ALIB_DBG(initFlag= 0x92A3EF61);
140
141 // already invoked?
142 if( MAIN_THREAD )
143 return;
144
145 Thread* mainThread= new Thread();
146 mainThread->id= ThreadID(-1);
147 mainThread->SetName(A_CHAR("MAIN_THREAD"));
148 mainThread->state = Thread::State::Running;
149
150 #if ALIB_MONOMEM && ALIB_CONTAINERS
151 THREAD_MAP.EmplaceUnique( std::this_thread::get_id(), mainThread );
152 #else
153 THREAD_MAP.insert( std::make_pair( std::this_thread::get_id(), mainThread) );
154 #endif
155
156 MAIN_THREAD= mainThread; // This flags the availability of the module
157}
158
159
160void shutdown() {
161 ALIB_ASSERT_ERROR( initFlag == 0x92A3EF61, "THREADS", "Not initialized when calling shutdown." )
162 ALIB_DBG(initFlag= 1);
163
164 // already invoked?
165 if( MAIN_THREAD == nullptr )
166 return;
167
168 {
169 LOCK_THREADS
170
171 // we should have exactly one thread and this is the system thread
172 #if ALIB_MONOMEM && ALIB_CONTAINERS
173 auto qtyThreads= THREAD_MAP.Size();
174 #else
175 auto qtyThreads= THREAD_MAP.size();
176 #endif
177
178 if( qtyThreads != 1 ) {
179 #if ALIB_DEBUG
180 std::vector<std::any> msgs;
181 msgs.reserve( 50 );
182 msgs.emplace_back( "ALib Termination: Still {} threads running.\n" );
183 msgs.emplace_back( qtyThreads);
184 int tNr= 0;
185 for( auto& it : THREAD_MAP ) {
186 msgs.emplace_back(" {}: {},\tState::{}\n");
187 msgs.emplace_back(++tNr);
188 msgs.emplace_back(it.second);
189 msgs.emplace_back(it.second->state);
190 }
191 assert::raise( ALIB_CALLER_PRUNED, 1, "THREADS", msgs );
192 #endif
193
194 delete MAIN_THREAD;
195 MAIN_THREAD= nullptr;
196 return;
197 }
198
199 Thread* lastThread= THREAD_MAP.begin()->second;
200 ALIB_ASSERT_WARNING( lastThread->id == static_cast<ThreadID>(-1), "THREADS",
201 "Last thread {} is not the main system thread detected in bootstrap.", lastThread->id )
202 delete lastThread;
203
204 MAIN_THREAD= nullptr;
205 #if ALIB_MONOMEM && ALIB_CONTAINERS
206 THREAD_MAP.Reset();
207 #endif
208} }
209
210# include "ALib.Lang.CIMethods.H"
211
212//##################################################################################################
213// class Thread
214//##################################################################################################
215thread_local Thread* THIS_THREAD = nullptr;
216
217Thread::Thread( Runnable* target , const character* pName )
218: runnable(target)
219, name(pName)
220{
221 // get myself an ID
222 id= nextThreadIdx++;
223}
224
227 "Thread \"{}\" destructed, while it was never started.", this )
228
229 if( c11Thread != nullptr ) {
230 ALIB_WARNING( "THREADS",
231 "Thread \"{}\" was not terminated before destruction.\n"
232 "Use Thread::Join() to avoid this message. Joining now...", this )
233 Join();
234} }
235
236#if defined(_MSC_VER)
238 if (THIS_THREAD == nullptr )
239 THIS_THREAD= Get( std::this_thread::get_id() );
240 return THIS_THREAD;
241}
242#endif
243
245 if( c11Thread != nullptr ) {
247 "Terminating thread \"{}\" which is not in state 'Done'. State: {}.",
248 this, state )
249
250 // join
251 if( c11Thread->joinable() )
252 c11Thread->join();
253 else
254 ALIB_WARNING( "THREADS", "Thread \"{}\" not joinable. State is '{}'.", this, state )
255
256 // erase from thread map
257 {
258 LOCK_THREADS
259
260 #if ALIB_MONOMEM && ALIB_CONTAINERS
261 ALIB_ASSERT_RESULT_EQUALS( THREAD_MAP.erase( nativeID ) , 1)
262 #else
263 ALIB_ASSERT_RESULT_EQUALS( THREAD_MAP.erase( nativeID ) , 1)
264 #endif
265 }
266
267 delete c11Thread;
268 c11Thread= nullptr;
270 }
272 // c11Thread == nullptr
273 else {
274 if( state == State::Terminated )
275 ALIB_WARNING( "THREADS",
276 "Double invocation of Thread::Terminate for thread \"{}\".", this )
277 else
278 ALIB_WARNING( "THREADS",
279 "Terminating thread \"{}\" which is not started or not managed by ALIB.",
280 this )
281} }
282
283
284
286 if ( c11Thread != nullptr ) {
287 ALIB_ERROR( "THREADS", "Thread with ID {} was already started.", GetID() )
288 return;
289 }
290
291 if ( id <= 0 ) {
292 ALIB_ERROR( "THREADS", "System threads cannot be started. (ID={}).", GetID() )
293 return;
294 }
295
297 {
298 LOCK_THREADS // <- locks on monomem::GLOBAL_ALLOCATOR_LOCK or, without monomem, on singleton "MODULE_LOCK"
299 c11Thread= new std::thread( threadStart, this );
300 nativeID= c11Thread->get_id();
301
302 #if ALIB_MONOMEM && ALIB_CONTAINERS
303 THREAD_MAP.EmplaceUnique( nativeID, this );
304 #else
305 THREAD_MAP.insert( std::make_pair( nativeID, this) );
306 #endif
307} }
308
309//##################################################################################################
310// static methods
311//##################################################################################################
312# include "ALib.Lang.CIFunctions.H"
313Thread* Thread::GetMain() { return MAIN_THREAD; }
314Thread* Thread::Get( std::thread::id nativeID ) {
315 // check for nulled nativeID or not initialized
316 if( lang::IsNull(nativeID) || !MAIN_THREAD )
317 return nullptr ;
318
319 Thread* result= nullptr;
320
321 // search current in map
322 {
323 LOCK_THREADS
324 #if ALIB_MONOMEM && ALIB_CONTAINERS
325 auto search= THREAD_MAP.Find( nativeID );
326 #else
327 auto search= THREAD_MAP.find( nativeID );
328 #endif
329
330 // found
331 if ( search != THREAD_MAP.end() )
332 result= search->second;
333
334 // not found, this is a system thread!
335 else {
336 result = new Thread( reinterpret_cast<Runnable*>(-1), nullptr );
337 result->id = nextSystemThreadId--;
338 result->state = Thread::State::Running; // we just "guess" here, as documented
339 result->SetName(A_CHAR("<SYSTEM_THREAD>")); // with method Thread::GetState()
340 #if ALIB_MONOMEM && ALIB_CONTAINERS
341 THREAD_MAP.EmplaceUnique( nativeID, result );
342 #else
343 THREAD_MAP.insert( std::make_pair( nativeID, result) );
344 #endif
345 } }
346
347 return result;
348}
349
350}} // namespace [alib::threads]
351
352#endif // !ALIB_SINGLE_THREADED
@ Running
The thread's Run method is currently processed.
Definition thread.inl:141
@ Started
Method Start was invoked but not running, yet.
Definition thread.inl:140
@ Terminated
The thread is terminated.
Definition thread.inl:144
static Thread * GetCurrent()
Definition thread.inl:298
std::thread::id nativeID
The internal C++ thread id.
Definition thread.inl:156
const character * name
The name of the thread.
Definition thread.inl:165
ThreadID GetID() const
Definition thread.inl:227
Thread(const character *pName=A_CHAR(""))
Definition thread.inl:181
virtual ALIB_DLL void Start()
Definition thread.cpp:285
virtual void SetName(const character *newName)
Definition thread.inl:247
virtual void Run() override
Definition thread.inl:217
std::thread * c11Thread
The internal C++ thread object.
Definition thread.inl:153
ALIB_DLL void destruct()
Definition thread.cpp:225
virtual ALIB_DLL void Join()
Definition thread.cpp:244
State state
Internal flag to indicate if the thread is alive.
Definition thread.inl:168
Runnable * runnable
The runnable to execute.
Definition thread.inl:159
ThreadID id
The id of the thread.
Definition thread.inl:162
static ALIB_DLL Thread * GetMain()
Definition thread.cpp:313
static ALIB_DLL Thread * Get(std::thread::id nativeID)
Definition thread.cpp:314
#define ALIB_ASSERT_RESULT_EQUALS( func, value)
Definition alib.inl:1083
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
Definition alib.inl:1063
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1067
#define ALIB_ERROR(domain,...)
Definition alib.inl:1062
#define ALIB_DBG(...)
Definition alib.inl:853
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1066
#define ALIB_CALLER_PRUNED
Definition alib.inl:1024
void raise(const CallerInfo &ci, int type, std::string_view domain, const std::span< std::any > &args)
Definition assert.cpp:565
constexpr bool IsNull(const T &t)
Definition tmp.inl:48
@ Absolute
Referring to an absolute value.
ALIB_DLL TMonoAllocator< lang::HeapAllocator > GLOBAL_ALLOCATOR
Details of namespace alib::threads.
Definition thread.cpp:108
void threadStart(Thread *thread)
Definition thread.cpp:110
void BootstrapThreadMap(integer qty)
Definition thread.cpp:130
integer ThreadID
The ALib thread identifier type.
Definition thread.inl:23
void bootstrap()
Definition thread.cpp:137
thread_local Thread * THIS_THREAD
A thread-local pointer to the ALib representation of the actual thread.
Definition thread.cpp:215
void shutdown()
Definition thread.cpp:160
lang::integer integer
Type alias in namespace alib.
Definition integers.inl:149
containers::HashMap< TAllocator, TKey, TMapped, THash, TEqual, THashCaching, TRecycling > HashMap
Type alias in namespace alib.
threads::Thread Thread
Type alias in namespace alib.
Definition thread.inl:387
characters::character character
Type alias in namespace alib.