GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/process.h
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 7 7 100.0%
Functions: 1 1 100.0%
Branches: 17 28 60.7%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_PROCESS
41 #define GEOGRAM_BASIC_PROCESS
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/thread_sync.h>
45 #include <geogram/basic/counted.h>
46 #include <geogram/basic/smart_pointer.h>
47 #include <functional>
48
49 /**
50 * \file geogram/basic/process.h
51 * \brief Function and classes for process manipulation
52 */
53
54 namespace GEO {
55
56 /**
57 * \brief Platform-independent base class for running threads.
58 * \details
59 * A Thread object manages one thread of control within the program.
60 * Thread%s begin executing with run(). Operational threads can be created
61 * by creating a derived class and reimplement function run().
62 *
63 * Thread%s are reference-counted objects. Their allocation and
64 * destruction can be automatically managed with Thread_var.
65 */
66 class GEOGRAM_API Thread : public Counted {
67 public:
68
69 /**
70 * \brief Thread constructor.
71 */
72
5/10
✓ Branch 1 taken 2144 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 652 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1947 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 683 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6762 times.
✗ Branch 14 not taken.
12208 Thread() : id_(0) {
73 }
74
75 /**
76 * \brief Starts the thread execution.
77 */
78 virtual void run() = 0;
79
80 /**
81 * \brief Gets the identifier of this thread.
82 * \return the identifier of the thread, i.e.
83 * an unsigned integer in the range [0, N-1]
84 * where N denotes the number of currently
85 * running threads.
86 */
87 index_t id() const {
88
10/16
✓ Branch 0 taken 102852 times.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 4038 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4038 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 9 taken 81 times.
✓ Branch 10 taken 4038 times.
✓ Branch 12 taken 1688 times.
✓ Branch 13 taken 40100 times.
✓ Branch 14 taken 148233 times.
✓ Branch 15 taken 162155 times.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
471579 return id_;
89 }
90
91 /**
92 * \brief Gets the current thread.
93 * \return A pointer to the instance of the
94 * currently running thread. If not running multiple
95 * threads, returns nullptr.
96 */
97 static Thread* current();
98
99 /**
100 * \brief Gets the identifier of the current thread.
101 * \return the identifier of the current thread, i.e.
102 * an unsigned integer in the range [0, N-1]
103 * where N denotes the number of currently
104 * running threads. If not running multiple threads,
105 * returns 0.
106 */
107 2417 static index_t current_id() {
108
2/2
✓ Branch 1 taken 2406 times.
✓ Branch 2 taken 11 times.
2417 return current() == nullptr ? 0 : current()->id();
109 }
110
111 protected:
112 /** Thread destructor */
113 ~Thread() override;
114
115
116 private:
117 /**
118 * \brief Sets the identifier of this thread.
119 * \details This function is meant to be called
120 * by the thread manager for each created thread.
121 * \param[in] id_in the identifier of this thread.
122 */
123 void set_id(index_t id_in) {
124 12200 id_ = id_in;
125 }
126
127 /**
128 * \brief Specifies the current instance, used by current().
129 * \details Stores the specified thread in the thread-local-storage
130 * static variable so that current() can retrieve it.
131 * Should be called by the ThreadManager right before launching
132 * the threads.
133 * \param[in] thread a pointer to the thread currently executed
134 */
135 static void set_current(Thread* thread);
136
137 index_t id_;
138
139 // ThreadManager needs to access set_current() and
140 // set_id().
141 friend class ThreadManager;
142 };
143
144 /** Smart pointer that contains a Thread object */
145 typedef SmartPointer<Thread> Thread_var;
146
147 /**
148 * \brief Collection of Thread%s
149 * \details ThreadGroup is a std::vector of Thread_var it provides the
150 * same operations for adding, removing or accessing thread elements.
151 * ThreadGroup takes ownership of Thread elements when they are added to
152 * the group, so there's is no need to delete Threads when the group is
153 * deleted.
154 */
155 typedef std::vector<Thread_var> ThreadGroup;
156
157 /**
158 * \brief Typed collection of Thread%s.
159 * \details
160 * TypedThreadGroup is a ThreadGroup that provides a typed accessor with
161 * operator[]().
162 * \tparam THREAD the type of Thread%s in the collection
163 */
164 template <class THREAD>
165 32 class TypedThreadGroup : public ThreadGroup {
166 public:
167 /**
168 * \brief Creates an empty group of Thread%s
169 * \details Thread elements can be added with the std::vector
170 * operation push_back()
171 */
172 TypedThreadGroup() {
173 }
174
175 /**
176 * \brief Gets a thread element by index
177 * \param[in] i index of the element
178 * \return a pointer to the \p THREAD at position \p i in the
179 * thread group
180 */
181 THREAD* operator[] (index_t i) {
182 geo_debug_assert(i < size());
183 Thread* result = ThreadGroup::operator[] (i);
184 return static_cast<THREAD*>(result);
185 }
186 };
187
188 /**
189 * \brief Platform-independent base class for running concurrent threads.
190 * \details
191 * The ThreadManager manager provides a platform-independent abstract
192 * interface for running concurrent Threads and managing critical
193 * sections.
194 *
195 * The ThreadManager is derived in multiple platform-specific or
196 * technology-specific implementations.
197 *
198 * Platform-specific implementations:
199 * - POSIX Thread manager (Unix)
200 * - Windows Threads manager (Windows)
201 * - Windows ThreadPool manager (Windows)
202 *
203 * Technology-specific implementations:
204 * - OpenMP-based manager
205 *
206 * Which ThreadManager to use is determined at runtime by
207 * Process::initialize() according to the current platform or the current
208 * available technology.
209 *
210 * \note For internal use only.
211 * \see Process::set_thread_manager()
212 */
213 class GEOGRAM_API ThreadManager : public Counted {
214 public:
215 /**
216 * \brief Runs a group of Thread%s.
217 * \details
218 * This start the execution of the threads
219 * contained in vector \p threads.
220 *
221 * If the threads cannot be executed in a concurrent environment
222 * (multi-threading is disabled or the number of maximum threads is 1),
223 * then the threads are executed sequentially. Otherwise the function
224 * run_concurrent_threads() is called to execute the threads
225 * concurrently. The execution terminates when the last thread
226 * terminates.
227 *
228 * \param[in] threads the vector of threads to be executed.
229 * \see maximum_concurrent_threads()
230 * \see run_concurrent_threads()
231 * \see Process::max_threads()
232 */
233 virtual void run_threads(ThreadGroup& threads);
234
235 /**
236 * \brief Gets the maximum number of possible concurrent threads
237 * \return The maximum number of possible concurrent threads allowed
238 * by this manager. It depends on the physical number of cores
239 * (including hyper-threading or not) and the technology implemented
240 * by this manager.
241 * \see Process::number_of_cores()
242 */
243 virtual index_t maximum_concurrent_threads() = 0;
244
245 protected:
246 /**
247 * \brief Runs a group of Thread%s concurrently.
248 * \details This start the concurrent execution of the threads
249 * contained in vector \p threads, using the given number of threads
250 * \p max_threads. The execution terminates when the last thread
251 * terminates.
252 * \param[in] threads the vector of threads to be executed.
253 * \param[in] max_threads maximum number of threads allowed for this
254 * execution. It is always greater than one
255 */
256 virtual void run_concurrent_threads(
257 ThreadGroup& threads, index_t max_threads
258 ) = 0;
259
260
261 /**
262 * \brief Sets the id of a thread.
263 * \details This function is called right before starting
264 * the threads. Each thread will have an id in [0, N-1]
265 * where N denotes the number of running threads.
266 * \param[in] thread the thread
267 * \param[in] id the id
268 */
269 static void set_thread_id(Thread* thread, index_t id) {
270 thread->set_id(id);
271 }
272
273 /**
274 * \brief Specifies the current instance, used by current().
275 * \details Stores the specified thread in the thread-local-storage
276 * static variable so that current() can retrieve it.
277 * Should be called by the ThreadManager right before launching
278 * the threads.
279 * \param[in] thread a pointer to the thread currently executed
280 */
281 static void set_current_thread(Thread* thread) {
282 12200 Thread::set_current(thread);
283 }
284
285 /** ThreadManager destructor */
286 ~ThreadManager() override;
287 };
288
289 /** Smart pointer that contains a ThreadManager object */
290 typedef SmartPointer<ThreadManager> ThreadManager_var;
291
292 /**
293 * \brief Single thread ThreadManager
294 * \details MonoThreadingThreadManager implements a ThreadManager for
295 * single thread environments.
296 */
297 class GEOGRAM_API MonoThreadingThreadManager : public ThreadManager {
298 public:
299 /**
300 * \copydoc ThreadManager::maximum_concurrent_threads()
301 * \note This implementation always returns 1.
302 */
303 index_t maximum_concurrent_threads() override;
304
305 protected:
306 /** MonoThreadingThreadManager destructor */
307 ~MonoThreadingThreadManager() override;
308
309 /**
310 * \copydoc ThreadManager::run_concurrent_threads()
311 * \note This implementation always executes threads sequentially.
312 */
313 void run_concurrent_threads(
314 ThreadGroup& threads, index_t max_threads
315 ) override;
316 };
317
318 /**
319 * \brief Abstraction layer for process management and multi-threading.
320 */
321 namespace Process {
322
323 /**
324 * \brief Initializes GeogramLib
325 * \param[in] flags the flags passed to GEO::initialize()
326 * \details This function must be called once before using
327 * any functionality of GeogramLib.
328 */
329 void GEOGRAM_API initialize(int flags);
330
331 /**
332 * \brief Terminates GeogramLib
333 * \details This function is called automatically when the program
334 * exits, so it should never be called directly.
335 */
336 void GEOGRAM_API terminate();
337
338
339 /**
340 * \brief Sleeps for a period of time.
341 * \param[in] microseconds the time to sleep,
342 * in microseconds.
343 */
344 void GEOGRAM_API sleep(index_t microseconds);
345
346 /**
347 * \brief Displays statistics about the current process
348 * \details Displays the maximum used amount of memory.
349 */
350 void GEOGRAM_API show_stats();
351
352 /**
353 * \brief Terminates the current process.
354 */
355 void GEOGRAM_API brute_force_kill();
356
357 /**
358 * \brief Returns the maximum number of threads that can be running
359 * simultaneously.
360 * \retval The number of cores if multi-threading is supported
361 * \retval 1 otherwise.
362 */
363 index_t GEOGRAM_API maximum_concurrent_threads();
364
365 /**
366 * \brief Runs a set of threads simultaneously
367 * \details Launches the execution of the threads contained in the
368 * vector \p threads and waits for the completion of all of them.
369 */
370 void GEOGRAM_API run_threads(ThreadGroup& threads);
371
372 /**
373 * \brief Gets the number of available cores
374 * \return The number of available cores including the "virtual ones" if
375 * hyper-threading is activated.
376 */
377 index_t GEOGRAM_API number_of_cores();
378
379 /**
380 * \brief Sets the thread manager (internal use).
381 * \details This sets the ThreadManager to use for concurrent thread
382 * execution. This function is called internally by
383 * Process::initialize() and should not be called explicitly.
384 * \note For internal use only
385 */
386 void GEOGRAM_API set_thread_manager(ThreadManager* thread_manager);
387
388 /**
389 * \brief Checks whether threads are running.
390 * \retval true if concurrent threads are currently running as an
391 * effect to Process::run_threads().
392 * \retval false otherwise.
393 * \see Process::run_threads()
394 */
395 bool GEOGRAM_API is_running_threads();
396
397 /**
398 * \brief Enables/disables floating point exceptions
399 * \details If FPEs are enabled, then floating point exceptions
400 * raise a SIGFPE signal, otherwise they generate NaNs. FPEs can also
401 * be configured by setting the value of the property "sys:FPE" with
402 * Environment::set_value().
403 * \param[in] flag set to \c true to enable FPEs, \c false to disable.
404 * \see FPE_enabled()
405 */
406 void GEOGRAM_API enable_FPE(bool flag);
407
408 /**
409 * \brief Gets the status of floating point exceptions
410 * \retval true if FPE are enabled
411 * \retval false otherwise
412 * \see enable_FPE()
413 */
414 bool GEOGRAM_API FPE_enabled();
415
416 /**
417 * \brief Enables/disables multi-threaded computations
418 * Multi-threading can also be configured by setting the value of the
419 * property "sys:multithread" with Environment::set_value().
420 * \param[in] flag set to \c true to enable multi-threading, \c false
421 * to disable.
422 * \see multithreading_enabled()
423 */
424 void GEOGRAM_API enable_multithreading(bool flag);
425
426 /**
427 * \brief Gets the status of multi-threading
428 * \retval true if multi-threading is enabled
429 * \retval false otherwise
430 * \see enable_multithreading()
431 */
432 bool GEOGRAM_API multithreading_enabled();
433
434 /**
435 * \brief Limits the number of concurrent threads to use
436 * \details The number of threads can also be configured by setting
437 * the value of the property "sys:max_threads" with
438 * Environment::set_value().
439 * \param[in] num_threads maximum number of threads to use.
440 * \see max_threads()
441 */
442 void GEOGRAM_API set_max_threads(index_t num_threads);
443
444 /**
445 * \brief Gets the number of allowed concurrent threads
446 * \see set_max_threads()
447 */
448 index_t GEOGRAM_API max_threads();
449
450 /**
451 * \brief Enables interruption of cancelable tasks
452 * \details This allows to interrupt cancelable tasks by typing
453 * CTRL-C in the terminal. This sets a specific handler on the
454 * interrupt signal that calls Progress::cancel() is there is a
455 * running cancelable task. If no task is running, the program is
456 * interrupted. The cancel mode can also be configured by setting the
457 * value of the property "sys:cancel" with
458 * Environment::set_value().
459 * \param[in] flag set to \c true to enable cancel mode, \c false
460 * to disable.
461 * \see cancel_enabled()
462 */
463 void GEOGRAM_API enable_cancel(bool flag);
464
465 /**
466 * \brief Gets the status of the cancel mode
467 * \retval true if the cancel mode is enabled
468 * \retval false otherwise
469 * \see enable_cancel()
470 */
471 bool GEOGRAM_API cancel_enabled();
472
473 /**
474 * \brief Gets the currently used memory.
475 * \return the used memory in bytes
476 */
477 size_t GEOGRAM_API used_memory();
478
479 /**
480 * \brief Gets the maximum used memory.
481 * \return the maximum used memory in bytes
482 */
483 size_t GEOGRAM_API max_used_memory();
484
485 /**
486 * \brief Gets the full path to the currently
487 * running program.
488 */
489 std::string GEOGRAM_API executable_filename();
490
491 /**
492 * \brief Prints a stack trace to the standard error.
493 */
494 void print_stack_trace();
495 }
496
497 /**
498 * \brief Executes a loop with concurrent threads.
499 * \details
500 * Executes a parallel for loop from index \p to index \p to, calling
501 * functional object \p func at each iteration.
502 *
503 * Calling parallel_for(from, to, func) is equivalent
504 * to the following loop, computed in parallel:
505 * \code
506 * for(index_t i = from; i < to; i++) {
507 * func(i)
508 * }
509 * \endcode
510 *
511 * When applicable, iterations are executed by concurrent threads:
512 * the range of the loop is split in to several contiguous
513 * sub-ranges, each of them being executed by a separate thread.
514 *
515 * If parameter \p interleaved is set to true, the loop range is
516 * decomposed in interleaved index sets. Interleaved execution may
517 * improve cache coherency.
518 *
519 * \param[in] func function that takes an index_t.
520 * \param[in] from the first iteration index
521 * \param[in] to one position past the last iteration index
522 * \param[in] threads_per_core number of threads to allocate per physical
523 * core (default is 1).
524 * \param[in] interleaved if set to \c true, indices are allocated to
525 * threads with an interleaved pattern.
526 */
527 void GEOGRAM_API parallel_for(
528 index_t from, index_t to, std::function<void(index_t)> func,
529 index_t threads_per_core = 1,
530 bool interleaved = false
531 );
532
533 /**
534 * \brief Executes a loop with concurrent threads.
535 *
536 * \details
537 * When applicable, iterations are executed by concurrent
538 * threads: the range of the loop is split in to several contiguous
539 * sub-ranges, each of them being executed by a separate thread.
540 *
541 * Calling parallel_for(func, from, to) is equivalent
542 * to the following loop, computed in parallel:
543 * \code
544 * func(from, i1);
545 * func(i1, i2);
546 * ...
547 * func(in, to);
548 * \endcode
549 * where i1,i2,...in are automatically generated. Typically one interval
550 * per physical core is generated.
551 *
552 * \param[in] func functional object that accepts two arguments of
553 * type index_t.
554 * \param[in] from first iteration index of the loop
555 * \param[in] to one position past the last iteration index
556 * \param[in] threads_per_core number of threads to allocate per physical
557 * core (default is 1).
558 */
559 void GEOGRAM_API parallel_for_slice(
560 index_t from, index_t to, std::function<void(index_t, index_t)> func,
561 index_t threads_per_core = 1
562 );
563
564 /**
565 * \brief Calls functions in parallel.
566 * \details Can be typically used with lambdas that capture this. See
567 * mesh/mesh_reorder.cpp and points/kd_tree.cpp for examples.
568 * \param[in] f1 , f2 functions to be called in parallel.
569 */
570 void GEOGRAM_API parallel(
571 std::function<void()> f1,
572 std::function<void()> f2
573 );
574
575 /**
576 * \brief Calls functions in parallel.
577 * \details Can be typically used with lambdas that capture this. See
578 * mesh/mesh_reorder.cpp and points/kd_tree.cpp for examples.
579 * \param[in] f1 , f2 , f3 , f4 functions to be called in parallel.
580 */
581 void GEOGRAM_API parallel(
582 std::function<void()> f1,
583 std::function<void()> f2,
584 std::function<void()> f3,
585 std::function<void()> f4
586 );
587
588 /**
589 * \brief Calls functions in parallel.
590 * \details Can be typically used with lambdas that capture this. See
591 * mesh/mesh_reorder.cpp and points/kd_tree.cpp for examples.
592 * \param[in] f1 , f2 , f3 , f4 , f5 , f6 , f7 , f8 functions
593 * to be called in parallel.
594 */
595 void GEOGRAM_API parallel(
596 std::function<void()> f1,
597 std::function<void()> f2,
598 std::function<void()> f3,
599 std::function<void()> f4,
600 std::function<void()> f5,
601 std::function<void()> f6,
602 std::function<void()> f7,
603 std::function<void()> f8
604 );
605
606 }
607
608 #endif
609