ALib C++ Library
Library Version: 2510 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
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;
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{
112 THIS_THREAD= thread;
114 thread->Run();
115 thread->state = Thread::State::Done;
116}
117
118# include "ALib.Lang.CIFunctions.H"
119
120} // namespace alib::threads[::detail];
121
122using namespace detail;
123
124// #################################################################################################
125// Namespace functions
126// #################################################################################################
127#if ALIB_DEBUG && !DOXYGEN
128 namespace{ unsigned int initFlag= 0; }
129#endif // !DOXYGEN
130
132 #if ALIB_MONOMEM && ALIB_CONTAINERS
133 THREAD_MAP.Reserve( qty, lang::ValueReference::Absolute );
134 #else
135 THREAD_MAP.reserve( size_t(qty) );
136 #endif
137}
138void bootstrap() {
139 ALIB_ASSERT_ERROR( initFlag == 0, "THREADS", "This method must not be invoked twice." )
140 ALIB_DBG(initFlag= 0x92A3EF61);
141
142 // already invoked?
143 if( MAIN_THREAD )
144 return;
145
146 Thread* mainThread= new Thread();
147 mainThread->id= ThreadID(-1);
148 mainThread->SetName(A_CHAR("MAIN_THREAD"));
149 mainThread->state = Thread::State::Running;
150
151 #if ALIB_MONOMEM && ALIB_CONTAINERS
152 THREAD_MAP.EmplaceUnique( std::this_thread::get_id(), mainThread );
153 #else
154 THREAD_MAP.insert( std::make_pair( std::this_thread::get_id(), mainThread) );
155 #endif
156
157 MAIN_THREAD= mainThread; // This flags the availability of the module
158}
159
160
162{
163 ALIB_ASSERT_ERROR( initFlag == 0x92A3EF61, "THREADS", "Not initialized when calling shutdown." )
164 ALIB_DBG(initFlag= 1);
165
166 // already invoked?
167 if( MAIN_THREAD == nullptr )
168 return;
169
170 {
171 LOCK_THREADS
172
173 // we should have exactly one thread and this is the system thread
174 #if ALIB_MONOMEM && ALIB_CONTAINERS
175 auto qtyThreads= THREAD_MAP.Size();
176 #else
177 auto qtyThreads= THREAD_MAP.size();
178 #endif
179
180 if( qtyThreads != 1 )
181 {
182 #if ALIB_DEBUG
183 std::vector<std::any> msgs;
184 msgs.reserve( 50 );
185 msgs.emplace_back( "ALib Termination: Still {} threads running.\n" );
186 msgs.emplace_back( qtyThreads);
187 int tNr= 0;
188 for( auto& it : THREAD_MAP ) {
189 msgs.emplace_back(" {}: {},\tState::{}\n");
190 msgs.emplace_back(++tNr);
191 msgs.emplace_back(it.second);
192 msgs.emplace_back(it.second->state);
193 }
194 assert::raise( ALIB_CALLER_PRUNED, 1, "THREADS", msgs );
195 #endif
196
197 delete MAIN_THREAD;
198 MAIN_THREAD= nullptr;
199 return;
200 }
201
202 Thread* lastThread= THREAD_MAP.begin()->second;
203 ALIB_ASSERT_WARNING( lastThread->id == static_cast<ThreadID>(-1), "THREADS",
204 "Last thread {} is not the main system thread detected in bootstrap.", lastThread->id )
205 delete lastThread;
206
207 MAIN_THREAD= nullptr;
208 #if ALIB_MONOMEM && ALIB_CONTAINERS
209 THREAD_MAP.Reset();
210 #endif
211 }
212}
213
214# include "ALib.Lang.CIMethods.H"
215
216// #################################################################################################
217// class Thread
218// #################################################################################################
219thread_local Thread* THIS_THREAD = nullptr;
220
221Thread::Thread( Runnable* target , const character* pName )
222: runnable(target)
223, name(pName)
224{
225 // get myself an ID
226 id= nextThreadIdx++;
227}
228
230{
232 "Thread \"{}\" destructed, while it was never started.", this )
233
234 if( c11Thread != nullptr )
235 {
236 ALIB_WARNING( "THREADS",
237 "Thread \"{}\" was not terminated before destruction.\n"
238 "Use Thread::Join() to avoid this message. Joining now...", this )
239 Join();
240 }
241}
242
243#if defined(_MSC_VER)
245 if (THIS_THREAD == nullptr )
246 THIS_THREAD= Get( std::this_thread::get_id() );
247 return THIS_THREAD;
248}
249#endif
250
252{
253 if( c11Thread != nullptr )
254 {
256 "Terminating thread \"{}\" which is not in state 'Done'. State: {}.",
257 this, state )
258
259 // join
260 if( c11Thread->joinable() )
261 c11Thread->join();
262 else
263 ALIB_WARNING( "THREADS", "Thread \"{}\" not joinable. State is '{}'.", this, state )
264
265 // erase from thread map
266 {
267 LOCK_THREADS
268
269 #if ALIB_MONOMEM && ALIB_CONTAINERS
270 ALIB_ASSERT_RESULT_EQUALS( THREAD_MAP.erase( nativeID ) , 1)
271 #else
272 ALIB_ASSERT_RESULT_EQUALS( THREAD_MAP.erase( nativeID ) , 1)
273 #endif
274 }
275
276 delete c11Thread;
277 c11Thread= nullptr;
279 }
280
281 // c11Thread == nullptr
282 else
283 {
284 if( state == State::Terminated )
285 ALIB_WARNING( "THREADS",
286 "Double invocation of Thread::Terminate for thread \"{}\".", this )
287 else
288 ALIB_WARNING( "THREADS",
289 "Terminating thread \"{}\" which is not started or not managed by ALIB.",
290 this )
291 }
292}
293
294
295
297{
298 if ( c11Thread != nullptr )
299 {
300 ALIB_ERROR( "THREADS", "Thread with ID {} was already started.", GetID() )
301 return;
302 }
303
304 if ( id <= 0 )
305 {
306 ALIB_ERROR( "THREADS", "System threads cannot be started. (ID={}).", GetID() )
307 return;
308 }
309
311 {
312 LOCK_THREADS // <- locks on monomem::GLOBAL_ALLOCATOR_LOCK or, without monomem, on singleton "MODULE_LOCK"
313 c11Thread= new std::thread( threadStart, this );
314 nativeID= c11Thread->get_id();
315
316 #if ALIB_MONOMEM && ALIB_CONTAINERS
317 THREAD_MAP.EmplaceUnique( nativeID, this );
318 #else
319 THREAD_MAP.insert( std::make_pair( nativeID, this) );
320 #endif
321 }
322}
323
324// #################################################################################################
325// static methods
326// #################################################################################################
327# include "ALib.Lang.CIFunctions.H"
328Thread* Thread::GetMain() { return MAIN_THREAD; }
329Thread* Thread::Get( std::thread::id nativeID )
330{
331 // check for nulled nativeID or not initialized
332 if( lang::IsNull(nativeID) || !MAIN_THREAD )
333 return nullptr ;
334
335 Thread* result= nullptr;
336
337 // search current in map
338 {
339 LOCK_THREADS
340 #if ALIB_MONOMEM && ALIB_CONTAINERS
341 auto search= THREAD_MAP.Find( nativeID );
342 #else
343 auto search= THREAD_MAP.find( nativeID );
344 #endif
345
346 // found
347 if ( search != THREAD_MAP.end() )
348 result= search->second;
349
350 // not found, this is a system thread!
351 else
352 {
353 result = new Thread( reinterpret_cast<Runnable*>(-1), nullptr );
354 result->id = nextSystemThreadId--;
355 result->state = Thread::State::Running; // we just "guess" here, as documented
356 result->SetName(A_CHAR("<SYSTEM_THREAD>")); // with method Thread::GetState()
357 #if ALIB_MONOMEM && ALIB_CONTAINERS
358 THREAD_MAP.EmplaceUnique( nativeID, result );
359 #else
360 THREAD_MAP.insert( std::make_pair( nativeID, result) );
361 #endif
362 }
363 }
364
365 return result;
366}
367
368}} // namespace [alib::threads]
369
370#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:300
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:296
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:229
virtual ALIB_DLL void Join()
Definition thread.cpp:251
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:328
static ALIB_DLL Thread * Get(std::thread::id nativeID)
Definition thread.cpp:329
#define ALIB_ASSERT_RESULT_EQUALS( func, value)
Definition alib.inl:1066
#define A_CHAR(STR)
#define ALIB_WARNING(domain,...)
Definition alib.inl:1046
#define ALIB_ASSERT_WARNING(cond, domain,...)
Definition alib.inl:1050
#define ALIB_ERROR(domain,...)
Definition alib.inl:1045
#define ALIB_DBG(...)
Definition alib.inl:836
#define ALIB_ASSERT_ERROR(cond, domain,...)
Definition alib.inl:1049
#define ALIB_CALLER_PRUNED
Definition alib.inl:1007
void raise(const CallerInfo &ci, int type, std::string_view domain, const std::span< std::any > &args)
Definition assert.cpp:575
constexpr bool IsNull(const T &t)
Definition tmp.inl:49
@ 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:131
integer ThreadID
The ALib thread identifier type.
Definition thread.inl:23
void bootstrap()
Definition thread.cpp:138
thread_local Thread * THIS_THREAD
A thread-local pointer to the ALib representation of the actual thread.
Definition thread.cpp:219
void shutdown()
Definition thread.cpp:161
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:389
characters::character character
Type alias in namespace alib.