ALib C++ Library
Library Version: 2402 R1
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 !defined(ALIB_DOX)
10#if !defined(HPP_ALIB_THREADS_DETAIL_THREADMAP)
12#endif
13
14#if !defined (HPP_ALIB_STRINGS_LOCALSTRING)
16#endif
17
18#if !defined (_GLIBCXX_CONDITION_VARIABLE) && !defined(_CONDITION_VARIABLE_)
19# include <condition_variable>
20#endif
21
22#if !defined (_GLIBCXX_ATOMIC) && !defined(_ATOMIC_)
23# include <atomic>
24#endif
25
26
27#if ALIB_ENUMS
28# if !defined(HPP_ALIB_ENUMS_RECORDBOOTSTRAP)
30# endif
31# if !defined(HPP_ALIB_ENUMS_SERIALIZATION)
33# endif
34#endif
35
36
37#endif // !defined(ALIB_DOX)
38
39namespace alib {
40
41/** ************************************************************************************************
42 * This is the reference documentation of sub-namespace \c threads of the \aliblink, which
43 * holds types of library module \alib_threads.
44 *
45 * \attention
46 * This module must not be omitted from an \alibdist if the using software does not make
47 * direct use of this module. If omitted, all other \alibmods will <b>silently (!)</b> drop
48 * the protection of their resources against multi-threaded access.<br>
49 *
50 * Further documentation is provided with
51 * - \ref alib_mod_threads "ALib Module Threads - Programmer's Manual".
52 * - Chapter \ref alib_manual_modules_impact_singlethreaded of the Programmer's Manual of \alib.
53 *
54 **************************************************************************************************/
55namespace threads {
56
57// #################################################################################################
58// Anonymous data
59// #################################################################################################
60#if !defined(ALIB_DOX)
61namespace {
62
63 ThreadID nextSystemThreadId = static_cast<ThreadID>(-1);
64 std::atomic<ThreadID> nextThreadIdx(1);
65 Thread* mainThread = nullptr;
66
67} // anonymous namespace
68#endif
69
70
71// #################################################################################################
72// Details
73// #################################################################################################
74/**
75 * Details of namespace #alib::threads.
76 */
77namespace detail {
78
79#if ALIB_MONOMEM
80 HashMap <std::thread::id, Thread *> threadMap(&monomem::GlobalAllocator);
81 #define LOCK_THREADS ALIB_OWN( monomem::GlobalAllocatorLock )
82
83#else
84 std::unordered_map<std::thread::id, Thread *> threadMap;
85 std::mutex moduleLock;
86 #define LOCK_THREADS std::unique_lock<std::mutex> uniqueLock( moduleLock );
87#endif
88
89void threadStart( Thread* thread )
90{
92 thread->Run();
94}
95
96Thread* getThread(std::thread::id c11ID )
97{
98 ALIB_ASSERT_ERROR( mainThread, "THREADS", "ALib Module Threads not initialized." )
99 Thread* result= nullptr;
100
101 // search current in map
102 {
103 LOCK_THREADS
104#if ALIB_MONOMEM
105 auto search= threadMap.Find( c11ID );
106#else
107 auto search= threadMap.find( c11ID );
108#endif
109
110 // found
111 if ( search != threadMap.end() )
112 result= search->second;
113
114 // not found, this is a system thread!
115 else
116 {
117 result = new Thread( reinterpret_cast<Runnable*>(-1), nullptr );
118 result->id = nextSystemThreadId--;
119 result->state = Thread::State::Running; // we just "guess" here, as documented
120 result->SetName( String64(A_CHAR("SYS_")) << result->id ); // with method Thread::GetState()
121#if ALIB_MONOMEM
122 threadMap.EmplaceUnique( c11ID, result );
123#else
124 threadMap.insert( std::make_pair( c11ID, result) );
125#endif
126 }
127 }
128
129 return result;
130}
131
132
133} // namespace alib::threads[::detail];
134
135using namespace detail;
136
137// #################################################################################################
138// Namespace functions
139// #################################################################################################
141{
142 // already invoked?
143 if( mainThread )
144 return;
145
146 mainThread= new Thread();
147 mainThread->id= static_cast<ThreadID>(-1);
148 mainThread->SetName(A_CHAR("MAIN_THREAD"));
149 mainThread->state = Thread::State::Running;
150
151#if ALIB_MONOMEM
152 threadMap.EmplaceUnique( std::this_thread::get_id(), mainThread );
153#else
154 threadMap.insert( std::make_pair( std::this_thread::get_id(), mainThread) );
155#endif
156
157 ALIB_ASSERT_ERROR( mainThread->GetId() == static_cast<ThreadID>(-1), "THREADS",
158 "Error initializing threads. Probably forbidden repeated initialization from different thread." )
159
160 // Assign enum records (not resourced, because Threads is not a Camp Module)
161 #if ALIB_ENUMS && ALIB_BOXING && !ALIB_CAMP
163 {
169 } );
170
171 #if ALIB_BOXING
173 #endif
174 #endif
175
176}
177
178
180{
181 // already invoked?
182 if( mainThread == nullptr )
183 return;
184
185 {
186 LOCK_THREADS
187
188 // we should have exactly one thread and this is the system thread
189 #if ALIB_MONOMEM
190 auto qtyThreads= threadMap.Size();
191 #else
192 auto qtyThreads= threadMap.size();
193 #endif
194
195 if( qtyThreads != 1 )
196 {
197 #if ALIB_DEBUG
198 NString4K dbgThreadList("ALib Termination: More than one thread running: ");
199 dbgThreadList << static_cast<integer>(qtyThreads) << NewLine();
200 int tNr= 0;
201 for( auto it : threadMap )
202 dbgThreadList << ++tNr << ": " << it.second->GetName()
203 << ",\tState::" <<
204 #if ALIB_ENUMS && ALIB_BOXING
205 it.second->state
206 #else
207 int(it.second->state )
208 #endif
209 << NewLine();
210 ALIB_WARNING( dbgThreadList.Terminate() )
211 #endif
212
213 delete mainThread;
214 mainThread= nullptr;
215 return;
216 }
217
218 Thread* lastThread= threadMap.begin()->second;
219 ALIB_ASSERT_WARNING( lastThread->id == static_cast<ThreadID>(-1), "THREADS",
220 "threads::Shutdown: last thread is not the main system thread detected "
221 "in threads::Bootstrap" )
222 delete lastThread;
223
224 mainThread= nullptr;
225 }
226}
227
228// #################################################################################################
229// class Thread
230// #################################################################################################
231Thread::Thread( Runnable* target , const String& pName )
232: runnable(target)
233, name(pName)
234{
235 // get myself an ID
236 id= nextThreadIdx++;
237 if ( name.IsEmpty() )
238 name << '(' << id << ')';
239}
240
242{
243 if( c11Thread !=nullptr )
244 {
245 NString512 msg;
246 msg << "Thread \"" << GetName()
247 << "\" was not terminated before destruction. Use Thread::Terminate() to "
248 "avoid this message.";
249 ALIB_WARNING( msg.Terminate() )
250 Terminate();
251 }
252}
253
255{
256 if( c11Thread !=nullptr )
257 {
258 if( state != State::Stopped )
259 {
260 NString512 msg;
261 msg << "Terminating thread \"" << GetName() << "\" which is not in state 'Stopped'. State: '"
262 #if ALIB_ENUMS && ALIB_BOXING
263 << state << "'.";
264 #else
265 << int(state) << "'.";
266 #endif
267 ALIB_WARNING( "THREADS", msg.Terminate() )
268 }
269
270 // join
271 if( c11Thread->joinable() )
272 c11Thread->join();
273 else
274 {
275 NString512 msg;
276 msg << "Thread \"" << GetName() << "\" not joinable. State: '"
277 #if ALIB_ENUMS && ALIB_BOXING
278 << state << "'.";
279 #else
280 << int(state) << "'.";
281 #endif
282
283 ALIB_WARNING( "THREADS", msg.Terminate() )
284 }
285
286 // erase from thread map
287 {
288 LOCK_THREADS
289
290 #if ALIB_MONOMEM
292 #else
294 #endif
295 }
296
297 delete c11Thread;
298 c11Thread= nullptr;
300 }
301 else
302 {
303 if( state == State::Terminated )
304 ALIB_WARNING( "THREADS",
305 "Double invocation of Thread::Terminate for thread {!Q}",
307 else
308 ALIB_WARNING( "THREADS",
309 "Terminating thread {!Q} which is not started or not managed by ALIB",
311 }
312}
313
314
315
317{
318 if ( c11Thread != nullptr )
319 {
320 ALIB_ERROR( "THREADS", NString128("Thread already started. ID: ")
321 << static_cast<integer>(GetId()) )
322 return;
323 }
324
325 if ( id <= 0 )
326 {
327 ALIB_ERROR( "THREADS", NString128("System threads can not be started. ID: ")
328 << static_cast<integer>(GetId()) )
329 return;
330 }
331
333
334 {
335 LOCK_THREADS // <- locks on monomem::GlobalAllocatorLock or, without memory, on singleton "moduleLock"
336 c11Thread= new std::thread( threadStart, this );
337 c11ID= c11Thread->get_id();
338
339 #if ALIB_MONOMEM
340 threadMap.EmplaceUnique( c11ID, this );
341 #else
342 threadMap.insert( std::make_pair( c11ID, this) );
343 #endif
344 }
345}
346
347
348// #################################################################################################
349// static methods
350// #################################################################################################
352{
353 return mainThread;
354}
355
356
357}} // namespace [alib::threads]
const TChar * Terminate() const
Definition astring.hpp:758
constexpr bool IsEmpty() const
Definition string.hpp:414
virtual ALIB_API void Start()
Definition thread.cpp:316
std::thread::id c11ID
Definition thread.hpp:143
virtual void SetName(const String &newName)
Definition thread.hpp:235
virtual ALIB_API ~Thread() override
Definition thread.cpp:241
virtual ThreadID GetId()
Definition thread.hpp:217
@ Running
The thread's Run method is currently processed.
@ Started
Method Start was invoked but not running, yet.
@ Terminated
The thread is terminated.
virtual const CString GetName()
Definition thread.hpp:226
std::thread * c11Thread
Definition thread.hpp:140
static ALIB_API Thread * GetMain()
Definition thread.cpp:351
virtual ALIB_API void Terminate()
Definition thread.cpp:254
ALIB_API Thread(const String &pName=EmptyString())
Definition thread.hpp:170
virtual void Run() override
Definition thread.hpp:205
#define ALIB_WARNING(...)
Definition alib.hpp:981
#define ALIB_ASSERT_RESULT_EQUALS( func, value)
Definition alib.hpp:999
#define A_CHAR(STR)
#define ALIB_ERROR(...)
Definition alib.hpp:980
#define ALIB_BOXING_BOOTSTRAP_REGISTER_FAPPEND_FOR_APPENDABLE_TYPE(TAppendable)
#define ALIB_ASSERT_ERROR(cond,...)
Definition alib.hpp:984
#define ALIB_ASSERT_WARNING(cond,...)
Definition alib.hpp:985
MonoAllocator GlobalAllocator(8 *1024)
ALIB_API HashMap< std::thread::id, Thread * > threadMap
ALIB_API std::mutex moduleLock
void threadStart(Thread *thread)
Definition thread.cpp:89
ALIB_API Thread * getThread(std::thread::id c11ID)
Definition thread.cpp:96
void Bootstrap()
Definition thread.cpp:140
integer ThreadID
Definition loxpimpl.inl:34
void Shutdown()
Definition thread.cpp:179
Definition alib.cpp:57
LocalString< 64 > String64
Type alias name for TLocalString<character,64> .
NLocalString< 128 > NString128
Type alias name for TLocalString<nchar,128> .
constexpr CString NewLine()
Definition cstring.hpp:528
threads::Thread Thread
Type alias in namespace alib.
Definition thread.hpp:390
NLocalString< 256 > NString256
Type alias name for TLocalString<nchar,256> .
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286
static void Bootstrap(TEnum element, TArgs &&... args) noexcept