Arcane  4.2.2.0
User documentation
Loading...
Searching...
No Matches
ConcurrencyUtils.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/* ConcurrencyUtils.cc (C) 2000-2026 */
9/* */
10/* Classes managing concurrency (tasks, parallel loops, ...) */
11/*---------------------------------------------------------------------------*/
12/*---------------------------------------------------------------------------*/
13
14#include "arccore/concurrency/ITaskImplementation.h"
15
16#include "arccore/concurrency/Task.h"
17#include "arccore/concurrency/ParallelFor.h"
18#include "arccore/concurrency/internal/TaskFactoryInternal.h"
19
20#include "arccore/base/Observable.h"
21
22#include <mutex>
23
24/*---------------------------------------------------------------------------*/
25/*---------------------------------------------------------------------------*/
26
27namespace Arcane
28{
29
30/*---------------------------------------------------------------------------*/
31/*---------------------------------------------------------------------------*/
32
33class SerialTask
34: public ITask
35{
36 public:
37
38 typedef TaskFunctor<SerialTask> TaskType;
39
40 public:
41
42 static const int FUNCTOR_CLASS_SIZE = sizeof(TaskType);
43
44 public:
45
46 SerialTask(ITaskFunctor* f)
47 : m_functor(f)
48 {
49 // \a f must be an instance of TaskFunctor<SerialTask>.
50 // we copy it into a pre-dimensioned buffer to avoid
51 // having to perform a heap allocation via new
52 // classic. We therefore use placement new.
53
54 m_functor = f->clone(functor_buf, FUNCTOR_CLASS_SIZE);
55 }
56
57 public:
58
59 void launchAndWait() override
60 {
61 if (m_functor) {
62 ITaskFunctor* tmp_f = m_functor;
63 m_functor = nullptr;
64 TaskContext task_context(this);
65 tmp_f->executeFunctor(task_context);
66 delete this;
67 }
68 }
70 {
71 for (Integer i = 0, n = tasks.size(); i < n; ++i)
72 tasks[i]->launchAndWait();
73 }
74 ITask* _createChildTask(ITaskFunctor* functor) override
75 {
76 return new SerialTask(functor);
77 }
78 void launch() override
79 {
80 if (m_functor) {
81 ITaskFunctor* tmp_f = m_functor;
82 m_functor = nullptr;
83 TaskContext task_context(this);
84 tmp_f->executeFunctor(task_context);
85 }
86 }
87 void wait() override
88 {
89 delete this;
90 }
91
92 private:
93
94 ITaskFunctor* m_functor;
95 char functor_buf[FUNCTOR_CLASS_SIZE];
96};
97
98/*---------------------------------------------------------------------------*/
99/*---------------------------------------------------------------------------*/
100
102: public ITaskImplementation
103{
104 public:
105
106 static NullTaskImplementation singleton;
107
108 public:
109
110 void initialize([[maybe_unused]] Int32 nb_thread) override
111 {
112 }
113 void terminate() override
114 {
115 }
117 {
118 return new SerialTask(f);
119 }
121 [[maybe_unused]] Integer block_size, IRangeFunctor* f) override
122 {
123 f->executeFunctor(begin, size);
124 }
126 [[maybe_unused]] const ParallelLoopOptions& options,
127 IRangeFunctor* f) override
128 {
129 f->executeFunctor(begin, size);
130 }
131 void executeParallelFor(Integer begin, Integer size, IRangeFunctor* f) override
132 {
133 f->executeFunctor(begin, size);
134 }
135 void executeParallelFor(const ParallelFor1DLoopInfo& loop_info) override
136 {
137 loop_info.functor()->executeFunctor(loop_info.beginIndex(), loop_info.size());
138 }
140 [[maybe_unused]] const ForLoopRunInfo& run_info,
141 IMDRangeFunctor<1>* functor) override
142 {
143 functor->executeFunctor(loop_ranges);
144 }
146 [[maybe_unused]] const ForLoopRunInfo& run_info,
147 IMDRangeFunctor<2>* functor) override
148 {
149 functor->executeFunctor(loop_ranges);
150 }
152 [[maybe_unused]] const ForLoopRunInfo& run_info,
153 IMDRangeFunctor<3>* functor) override
154 {
155 functor->executeFunctor(loop_ranges);
156 }
158 [[maybe_unused]] const ForLoopRunInfo& run_info,
159 IMDRangeFunctor<4>* functor) override
160 {
161 functor->executeFunctor(loop_ranges);
162 }
163 bool isActive() const override
164 {
165 return false;
166 }
168 {
169 return 0;
170 }
171 Int32 currentTaskIndex() const override
172 {
173 return 0;
174 }
175
176 void printInfos(std::ostream& o) const final
177 {
178 o << "NullTaskImplementation";
179 }
180};
181
182/*---------------------------------------------------------------------------*/
183/*---------------------------------------------------------------------------*/
184
185NullTaskImplementation NullTaskImplementation::singleton;
186ITaskImplementation* TaskFactory::m_impl = &NullTaskImplementation::singleton;
187Int32 TaskFactory::m_verbose_level = 0;
188
189/*---------------------------------------------------------------------------*/
190/*---------------------------------------------------------------------------*/
191
192namespace
193{
194 IObservable* global_created_thread_observable = 0;
195 IObservable* global_destroyed_thread_observable = 0;
196 std::mutex global_observable_mutex;
197
198 IObservable*
199 _checkCreateGlobalThreadObservable()
200 {
201 if (!global_created_thread_observable)
202 global_created_thread_observable = new Observable();
203 return global_created_thread_observable;
204 }
205} // namespace
206
207/*---------------------------------------------------------------------------*/
208/*---------------------------------------------------------------------------*/
209
210void TaskFactoryInternal::
211setImplementation(ITaskImplementation* task_impl)
212{
213 if (TaskFactory::m_impl && TaskFactory::m_impl != &NullTaskImplementation::singleton)
214 ARCCORE_FATAL("TaskFactory already has an implementation");
215 TaskFactory::m_impl = task_impl;
216}
217
218void TaskFactoryInternal::
219addThreadCreateObserver(IObserver* o)
220{
221 std::scoped_lock slock(global_observable_mutex);
222 _checkCreateGlobalThreadObservable();
223 global_created_thread_observable->attachObserver(o);
224}
225
226void TaskFactoryInternal::
227removeThreadCreateObserver(IObserver* o)
228{
229 std::scoped_lock slock(global_observable_mutex);
230 _checkCreateGlobalThreadObservable();
231 global_created_thread_observable->detachObserver(o);
232}
233
234void TaskFactoryInternal::
235notifyThreadCreated()
236{
237 std::scoped_lock slock(global_observable_mutex);
238 if (global_created_thread_observable)
239 global_created_thread_observable->notifyAllObservers();
240}
241
242/*---------------------------------------------------------------------------*/
243/*---------------------------------------------------------------------------*/
244
245/*---------------------------------------------------------------------------*/
246/*---------------------------------------------------------------------------*/
247
248void TaskFactory::
249_internalSetImplementation(ITaskImplementation* task_impl)
250{
251 TaskFactoryInternal::setImplementation(task_impl);
252}
253
254/*---------------------------------------------------------------------------*/
255/*---------------------------------------------------------------------------*/
256
259{
260 std::scoped_lock slock(global_observable_mutex);
261 return _checkCreateGlobalThreadObservable();
262}
263
264/*---------------------------------------------------------------------------*/
265/*---------------------------------------------------------------------------*/
266
269{
270 if (!global_destroyed_thread_observable)
271 global_destroyed_thread_observable = new Observable();
272 return global_destroyed_thread_observable;
273}
274
275/*---------------------------------------------------------------------------*/
276/*---------------------------------------------------------------------------*/
277
278void TaskFactory::
279terminate()
280{
281 // The one that placed the implementation manages its destruction.
282 if (m_impl == &NullTaskImplementation::singleton)
283 return;
284 if (m_impl)
285 m_impl->terminate();
286 m_impl = &NullTaskImplementation::singleton;
287}
288
289/*---------------------------------------------------------------------------*/
290/*---------------------------------------------------------------------------*/
291
292} // namespace Arcane
293
294/*---------------------------------------------------------------------------*/
295/*---------------------------------------------------------------------------*/
296
297/*!
298 * \file ConcurrencyUtils.h
299 *
300 * \brief Classes, Types, and macros for managing concurrency.
301 *
302 * For more information, refer to the page \ref arcanedoc_parallel_concurrency
303 */
304
305/*---------------------------------------------------------------------------*/
306/*---------------------------------------------------------------------------*/
#define ARCCORE_FATAL(...)
Macro throwing a FatalErrorException.
Constant view of an array of type T.
constexpr Integer size() const noexcept
Number of elements in the array.
Loop execution information.
Interface of a functor on a multi-dimensional iteration interval of dimension RankValue.
Interface of a functor on an iteration interval.
virtual void executeFunctor(Int32 begin, Int32 size)=0
Executes the associated method.
virtual void executeFunctor(const TaskContext &tc)=0
Executes the associated method.
Interface for a concurrent task.
Definition Task.h:194
void executeParallelFor(const ComplexForLoopRanges< 4 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 4 > *functor) override
Executes a 4D loop in parallel.
void printInfos(std::ostream &o) const final
Prints information about the runtime used.
Int32 currentTaskIndex() const override
Implementation of TaskFactory::currentTaskIndex().
void executeParallelFor(Integer begin, Integer size, IRangeFunctor *f) override
Executes the functor f in parallel.
void executeParallelFor(const ComplexForLoopRanges< 3 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 3 > *functor) override
Executes a 3D loop in parallel.
void executeParallelFor(const ComplexForLoopRanges< 1 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 1 > *functor) override
Executes a 1D loop in parallel.
void executeParallelFor(const ComplexForLoopRanges< 2 > &loop_ranges, const ForLoopRunInfo &run_info, IMDRangeFunctor< 2 > *functor) override
Executes a 2D loop in parallel.
void executeParallelFor(const ParallelFor1DLoopInfo &loop_info) override
Executes the loop loop_info in parallel.
ITask * createRootTask(ITaskFunctor *f) override
Creates a root task. The implementation must copy the value of f, which is either a TaskFunctor or a ...
bool isActive() const override
Indicates if the implementation is active.
void executeParallelFor(Integer begin, Integer size, Integer block_size, IRangeFunctor *f) override
Executes the functor f in parallel.
Int32 currentTaskThreadIndex() const override
Implementation of TaskFactory::currentTaskThreadIndex().
void executeParallelFor(Integer begin, Integer size, const ParallelLoopOptions &options, IRangeFunctor *f) override
Executes the functor f in parallel.
Characteristics of a multi-thread 1D loop.
Definition ParallelFor.h:35
Execution options for a parallel loop in multi-threading.
void wait() override
Method allowing to wait the end of the task.
void launchAndWait() override
Launches the task and blocks until it finishes.
void launch() override
Method allowing to launch the task.
void launchAndWait(ConstArrayView< ITask * > tasks) override
Launches the child tasks tasks and blocks until they finish.
Execution context of a task.
Definition Task.h:50
static IObservable * destroyThreadObservable()
Observable called when a thread is destroyed for a task.
static IObservable * createThreadObservable()
Observable called when a thread is created for a task.
-- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature --
Int32 Integer
Type representing an integer.
std::int32_t Int32
Signed integer type of 32 bits.