Arcane  4.1.12.0
Developer documentation
Loading...
Searching...
No Matches
TBBThreadImplementation.cc
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* TBBThreadImplementation.cc (C) 2000-2026 */
9/* */
10/* Implementation of threads using TBB (Intel Threads Building Blocks). */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arcane/utils/IThreadImplementationService.h"
15#include "arcane/utils/IThreadBarrier.h"
16#include "arcane/utils/NotImplementedException.h"
17#include "arcane/utils/IFunctor.h"
18#include "arcane/utils/Mutex.h"
19#include "arcane/utils/PlatformUtils.h"
20#include "arccore/base/internal/DependencyInjection.h"
21
22#include "arcane/parallel/thread/ArcaneThreadMisc.h"
23
24#include <tbb/tbb.h>
25#define ARCANE_TBB_USE_STDTHREAD
26#include <thread>
27#include <mutex>
28#include <new>
29
30// NOTE:
31// This implementation has not been the default implementation since the end of 2025.
32// The default implementation now uses the STL.
33// If everything is okay, we can delete this implementation at the end of 2026, for example.
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
37
38namespace Arcane
39{
40
41/*---------------------------------------------------------------------------*/
42/*---------------------------------------------------------------------------*/
43
44typedef std::thread::id ThreadId;
45typedef std::thread ThreadType;
46// Attempts to convert a std::thread::id into an 'Int64'.
47// There is no portable way to do this, so we are doing something
48// that is not necessarily clean. In the long run, it would be preferable to remove
49// the IThreadImplementation::currentThread() method.
50inline Int64 arcaneGetThisThreadId()
51{
52 Int64 v = static_cast<Int64>(std::hash<std::thread::id>{}(std::this_thread::get_id()));
53 return v;
54}
55
56/*---------------------------------------------------------------------------*/
57/*---------------------------------------------------------------------------*/
58
60{
61 public:
62
63 void lock()
64 {
65 m_mutex.lock();
66 }
67 void unlock()
68 {
69 m_mutex.unlock();
70 }
71
72 private:
73
74 std::mutex m_mutex;
75};
76
77/*---------------------------------------------------------------------------*/
78/*---------------------------------------------------------------------------*/
79
80class TBBBarrier
81: public IThreadBarrier
82{
83 public:
84
85 TBBBarrier() = default;
86
87 void destroy() override { delete this; }
88
89 void init(Integer nb_thread) override
90 {
91 m_nb_thread = nb_thread;
92 m_nb_thread_finished = 0;
93 m_timestamp = 0;
94 };
95
96 void wait() override
97 {
98 Int32 ts = m_timestamp;
99 int remaining_thread = m_nb_thread - m_nb_thread_finished.fetch_add(1) - 1;
100 if (remaining_thread > 0) {
101
102 int count = 1;
103 while (m_timestamp == ts) {
104 arcaneDoCPUPause(count);
105 if (count < 200)
106 count *= 2;
107 else {
108 //count = 0;
109 //__TBB_Yield();
110 //TODO: maybe make the main (__TBB_Yield()) if too
111 // iterations.
112 }
113 }
114 }
115 m_nb_thread_finished = 0;
116 ++m_timestamp;
117 }
118
119 private:
120
121 Int32 m_nb_thread = 0;
122 std::atomic<Int32> m_nb_thread_finished = 0;
123 std::atomic<Int32> m_timestamp = 0;
124};
125
126/*---------------------------------------------------------------------------*/
127/*---------------------------------------------------------------------------*/
128
129extern "C" IThreadBarrier*
130createGlibThreadBarrier();
131
132/*---------------------------------------------------------------------------*/
133/*---------------------------------------------------------------------------*/
134
138class TBBThreadImplementation
141{
143
144 void addReference() override { ReferenceCounterImpl::addReference(); }
145 void removeReference() override { ReferenceCounterImpl::removeReference(); }
146
147 public:
148
149 class StartFunc
150 {
151 public:
152
153 explicit StartFunc(IFunctor* f)
154 : m_f(f)
155 {}
156 void operator()() const { m_f->executeFunctor(); }
157 IFunctor* m_f = nullptr;
158 };
159
160 public:
161
162 TBBThreadImplementation()
163 {
164 if (!platform::getEnvironmentVariable("ARCANE_SPINLOCK_BARRIER").null())
165 m_use_tbb_barrier = true;
166 m_std_thread_implementation = Arccore::Concurrency::createStdThreadImplementation();
167 }
168
169 ~TBBThreadImplementation() override
170 {
171 //std::cout << "DESTROYING TBB IMPLEMENTATION\n";
172 GlobalMutex::destroy();
173 if (m_global_mutex_impl)
174 this->destroyMutex(m_global_mutex_impl);
175 }
176
177 public:
178
179 void build()
180 {
181 }
182
183 void initialize() override
184 {
185 m_global_mutex_impl = createMutex();
186 GlobalMutex::init(m_global_mutex_impl);
187 }
188
189 public:
190
191 ThreadImpl* createThread(IFunctor* f) override
192 {
193 return reinterpret_cast<ThreadImpl*>(new ThreadType(StartFunc(f)));
194 }
195 void joinThread(ThreadImpl* t) override
196 {
197 auto* tt = reinterpret_cast<ThreadType*>(t);
198 tt->join();
199 }
200 void destroyThread(ThreadImpl* t) override
201 {
202 auto* tt = reinterpret_cast<ThreadType*>(t);
203 delete tt;
204 }
205
206 void createSpinLock(Int64* spin_lock_addr) override
207 {
208 void* addr = spin_lock_addr;
209 new (addr) tbb::spin_mutex();
210 }
211 void lockSpinLock(Int64* spin_lock_addr, Int64* scoped_spin_lock_addr) override
212 {
213 auto* s = reinterpret_cast<tbb::spin_mutex*>(spin_lock_addr);
214 auto* sl = new (scoped_spin_lock_addr) tbb::spin_mutex::scoped_lock();
215 sl->acquire(*s);
216 }
217 void unlockSpinLock(Int64* spin_lock_addr, Int64* scoped_spin_lock_addr) override
218 {
219 ARCANE_UNUSED(spin_lock_addr);
220 auto* s = reinterpret_cast<tbb::spin_mutex::scoped_lock*>(scoped_spin_lock_addr);
221 s->release();
222 //TODO: destroy the scoped_lock.
223 }
224
225 MutexImpl* createMutex() override
226 {
227 auto* m = new TBBMutexImpl();
228 return reinterpret_cast<MutexImpl*>(m);
229 }
230 void destroyMutex(MutexImpl* mutex) override
231 {
232 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
233 delete tm;
234 }
235 void lockMutex(MutexImpl* mutex) override
236 {
237 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
238 tm->lock();
239 }
240 void unlockMutex(MutexImpl* mutex) override
241 {
242 auto* tm = reinterpret_cast<TBBMutexImpl*>(mutex);
243 tm->unlock();
244 }
245
246 Int64 currentThread() override
247 {
248 Int64 v = arcaneGetThisThreadId();
249 return v;
250 }
251
252 IThreadBarrier* createBarrier() override
253 {
254 // We must use TBB only if requested, because it uses
255 // active waiting which can quickly bring the machine to its knees
256 // if the total number of threads exceeds the number of cores
257 // of the machine.
258 if (m_use_tbb_barrier)
259 return new TBBBarrier();
260 return m_std_thread_implementation->createBarrier();
261 }
262
263 private:
264
265 bool m_use_tbb_barrier = false;
266 MutexImpl* m_global_mutex_impl = nullptr;
267 Ref<IThreadImplementation> m_std_thread_implementation;
268};
269
270/*---------------------------------------------------------------------------*/
271/*---------------------------------------------------------------------------*/
272
273class TBBThreadImplementationService
275{
276 public:
277
278 TBBThreadImplementationService() = default;
279
280 public:
281
282 void build() {}
283
284 public:
285
286 Ref<IThreadImplementation> createImplementation() override
287 {
289 }
290};
291
292/*---------------------------------------------------------------------------*/
293/*---------------------------------------------------------------------------*/
294
295ARCANE_DI_REGISTER_PROVIDER(TBBThreadImplementationService,
296 DependencyInjection::ProviderProperty("TBBThreadImplementationService"),
297 ARCANE_DI_INTERFACES(IThreadImplementationService),
298 ARCANE_DI_EMPTY_CONSTRUCTOR());
299
300/*---------------------------------------------------------------------------*/
301/*---------------------------------------------------------------------------*/
302
303} // End namespace Arcane
304
305/*---------------------------------------------------------------------------*/
306/*---------------------------------------------------------------------------*/
#define ARCCORE_DEFINE_REFERENCE_COUNTED_INCLASS_METHODS()
Macro to define methods managing counters of references.
static void init(MutexImpl *p)
Initializes the global mutex. Internal to Arccore. Must be allocated by new.
Definition Mutex.cc:60
Reference to an instance.
Thread-safe implementation of a reference counter.
void destroy() override
Destroys the barrier.
void wait() override
Blocks and waits until all threads call this method.
void init(Integer nb_thread) override
Initializes the barrier for nb_thread.
Implementation of threads using TBB (Intel Threads Building Blocks).
String getEnvironmentVariable(const String &name)
Environment variable named name.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
std::int64_t Int64
Signed integer type of 64 bits.
void arcaneDoCPUPause(Int32 count)
Uses the CPU 'pause' instruction if possible.
Int32 Integer
Type representing an integer.
auto makeRef(InstanceType *t) -> Ref< InstanceType >
Creates a reference on a pointer.
std::int32_t Int32
Signed integer type of 32 bits.