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