| 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 | #include <geogram/basic/process.h> | ||
| 41 | #include <geogram/basic/process_private.h> | ||
| 42 | #include <geogram/basic/logger.h> | ||
| 43 | #include <geogram/basic/environment.h> | ||
| 44 | #include <geogram/basic/string.h> | ||
| 45 | #include <geogram/basic/command_line.h> | ||
| 46 | #include <geogram/basic/stopwatch.h> | ||
| 47 | #include <thread> | ||
| 48 | #include <chrono> | ||
| 49 | |||
| 50 | #ifdef GEO_OPENMP | ||
| 51 | #include <omp.h> | ||
| 52 | #endif | ||
| 53 | |||
| 54 | #ifdef GEO_TBB | ||
| 55 | #include <tbb/parallel_for.h> | ||
| 56 | #include <tbb/task_arena.h> | ||
| 57 | #endif | ||
| 58 | |||
| 59 | namespace { | ||
| 60 | using namespace GEO; | ||
| 61 | |||
| 62 | ThreadManager_var thread_manager_; | ||
| 63 | int running_threads_invocations_ = 0; | ||
| 64 | |||
| 65 | bool multithreading_initialized_ = false; | ||
| 66 | bool multithreading_enabled_ = true; | ||
| 67 | |||
| 68 | index_t max_threads_initialized_ = false; | ||
| 69 | index_t max_threads_ = 0; | ||
| 70 | |||
| 71 | bool fpe_initialized_ = false; | ||
| 72 | bool fpe_enabled_ = false; | ||
| 73 | |||
| 74 | bool cancel_initialized_ = false; | ||
| 75 | bool cancel_enabled_ = false; | ||
| 76 | |||
| 77 | double start_time_ = 0.0; | ||
| 78 | |||
| 79 | /************************************************************************/ | ||
| 80 | |||
| 81 | /** | ||
| 82 | * \brief Process Environment | ||
| 83 | * \details This environment exposes and controls the configuration of the | ||
| 84 | * Process module. | ||
| 85 | */ | ||
| 86 | 251 | class ProcessEnvironment : public Environment { | |
| 87 | protected: | ||
| 88 | /** | ||
| 89 | * \brief Gets a Process property | ||
| 90 | * \details Retrieves the value of the property \p name and stores it | ||
| 91 | * in \p value. The property must be a valid Process property (see | ||
| 92 | * sys:xxx properties in Vorpaline's help). | ||
| 93 | * \param[in] name name of the property | ||
| 94 | * \param[out] value receives the value of the property | ||
| 95 | * \retval true if the property is a valid Process property | ||
| 96 | * \retval false otherwise | ||
| 97 | * \see Environment::get_value() | ||
| 98 | */ | ||
| 99 | 953 | bool get_local_value( | |
| 100 | const std::string& name, std::string& value | ||
| 101 | ) const override { | ||
| 102 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 953 times.
|
953 | if(name == "sys:nb_cores") { |
| 103 | ✗ | value = String::to_string(Process::number_of_cores()); | |
| 104 | ✗ | return true; | |
| 105 | } | ||
| 106 |
2/2✓ Branch 0 taken 355 times.
✓ Branch 1 taken 598 times.
|
953 | if(name == "sys:multithread") { |
| 107 | 355 | value = String::to_string(multithreading_enabled_); | |
| 108 | 355 | return true; | |
| 109 | } | ||
| 110 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 493 times.
|
598 | if(name == "sys:max_threads") { |
| 111 | 210 | value = String::to_string( | |
| 112 | 105 | Process::maximum_concurrent_threads() | |
| 113 | 105 | ); | |
| 114 | 105 | return true; | |
| 115 | } | ||
| 116 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 388 times.
|
493 | if(name == "sys:FPE") { |
| 117 | 105 | value = String::to_string(fpe_enabled_); | |
| 118 | 105 | return true; | |
| 119 | } | ||
| 120 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 283 times.
|
388 | if(name == "sys:cancel") { |
| 121 | 105 | value = String::to_string(cancel_enabled_); | |
| 122 | 105 | return true; | |
| 123 | } | ||
| 124 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 178 times.
|
283 | if(name == "sys:assert") { |
| 125 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 105 times.
|
105 | value = assert_mode() == ASSERT_THROW ? "throw" : "abort"; |
| 126 | 105 | return true; | |
| 127 | } | ||
| 128 |
2/2✓ Branch 0 taken 132 times.
✓ Branch 1 taken 46 times.
|
178 | if(name == "algo:random_seed") { |
| 129 | 132 | value = String::to_string(random_seed_); | |
| 130 | 132 | return true; | |
| 131 | } | ||
| 132 | return false; | ||
| 133 | } | ||
| 134 | |||
| 135 | /** | ||
| 136 | * \brief Sets a Process property | ||
| 137 | * \details Sets the property \p name with value \p value in the | ||
| 138 | * Process. The property must be a valid Process property (see sys:xxx | ||
| 139 | * properties in Vorpaline's help) and \p value must be a legal value | ||
| 140 | * for the property. | ||
| 141 | * \param[in] name name of the property | ||
| 142 | * \param[in] value value of the property | ||
| 143 | * \retval true if the property was successfully set | ||
| 144 | * \retval false otherwise | ||
| 145 | * \see Environment::set_value() | ||
| 146 | */ | ||
| 147 | 12377 | bool set_local_value( | |
| 148 | const std::string& name, const std::string& value | ||
| 149 | ) override { | ||
| 150 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 12126 times.
|
12377 | if(name == "sys:multithread") { |
| 151 | 251 | Process::enable_multithreading(String::to_bool(value)); | |
| 152 | 251 | return true; | |
| 153 | } | ||
| 154 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11875 times.
|
12126 | if(name == "sys:max_threads") { |
| 155 | 251 | Process::set_max_threads(String::to_uint(value)); | |
| 156 | 251 | return true; | |
| 157 | } | ||
| 158 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11624 times.
|
11875 | if(name == "sys:FPE") { |
| 159 | 251 | Process::enable_FPE(String::to_bool(value)); | |
| 160 | 251 | return true; | |
| 161 | } | ||
| 162 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11373 times.
|
11624 | if(name == "sys:cancel") { |
| 163 | 251 | Process::enable_cancel(String::to_bool(value)); | |
| 164 | 251 | return true; | |
| 165 | } | ||
| 166 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 11122 times.
|
11373 | if(name == "sys:assert") { |
| 167 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | if(value == "throw") { |
| 168 | 251 | set_assert_mode(ASSERT_THROW); | |
| 169 | 251 | return true; | |
| 170 | } | ||
| 171 | ✗ | if(value == "abort") { | |
| 172 | ✗ | set_assert_mode(ASSERT_ABORT); | |
| 173 | ✗ | return true; | |
| 174 | } | ||
| 175 | ✗ | if(value == "breakpoint") { | |
| 176 | ✗ | set_assert_mode(ASSERT_BREAKPOINT); | |
| 177 | ✗ | return true; | |
| 178 | } | ||
| 179 | ✗ | Logger::err("Process") | |
| 180 | << "Invalid value for property sys:abort: " | ||
| 181 | << value | ||
| 182 | << std::endl; | ||
| 183 | ✗ | return false; | |
| 184 | } | ||
| 185 |
2/2✓ Branch 0 taken 10884 times.
✓ Branch 1 taken 238 times.
|
11122 | if(name == "algo:random_seed") { |
| 186 | 238 | random_seed_ = String::to_int(value); | |
| 187 | 238 | Numeric::random_reset(random_seed_); | |
| 188 | 238 | return true; | |
| 189 | } | ||
| 190 | return false; | ||
| 191 | } | ||
| 192 | |||
| 193 | /** ProcessEnvironment destructor */ | ||
| 194 | 502 | ~ProcessEnvironment() override { | |
| 195 | 502 | } | |
| 196 | private: | ||
| 197 | int random_seed_ = -1; | ||
| 198 | }; | ||
| 199 | |||
| 200 | /************************************************************************/ | ||
| 201 | |||
| 202 | #ifdef GEO_OPENMP | ||
| 203 | |||
| 204 | /** | ||
| 205 | * \brief OpenMP Thread Manager | ||
| 206 | * \details | ||
| 207 | * OMPThreadManager is an implementation of ThreadManager that uses OpenMP | ||
| 208 | * for running concurrent threads and control critical sections. | ||
| 209 | */ | ||
| 210 | class GEOGRAM_API OMPThreadManager : public ThreadManager { | ||
| 211 | public: | ||
| 212 | /** | ||
| 213 | * \brief Creates and initializes the OpenMP ThreadManager | ||
| 214 | */ | ||
| 215 | ✗ | OMPThreadManager() { | |
| 216 | } | ||
| 217 | |||
| 218 | /** \copydoc GEO::ThreadManager::maximum_concurrent_threads() */ | ||
| 219 | ✗ | virtual index_t maximum_concurrent_threads() { | |
| 220 | ✗ | return Process::number_of_cores(); | |
| 221 | } | ||
| 222 | |||
| 223 | protected: | ||
| 224 | /** \brief OMPThreadManager destructor */ | ||
| 225 | ✗ | virtual ~OMPThreadManager() { | |
| 226 | ✗ | } | |
| 227 | |||
| 228 | /** \copydoc GEO::ThreadManager::run_concurrent_threads() */ | ||
| 229 | ✗ | virtual void run_concurrent_threads( | |
| 230 | ThreadGroup& threads, index_t max_threads | ||
| 231 | ) { | ||
| 232 | // TODO: take max_threads_ into account | ||
| 233 | geo_argused(max_threads); | ||
| 234 | |||
| 235 | ✗ | #pragma omp parallel for schedule(dynamic) | |
| 236 | for(int i = 0; i < int(threads.size()); i++) { | ||
| 237 | index_t ii = index_t(i); | ||
| 238 | set_thread_id(threads[ii],ii); | ||
| 239 | set_current_thread(threads[ii]); | ||
| 240 | threads[ii]->run(); | ||
| 241 | } | ||
| 242 | ✗ | } | |
| 243 | }; | ||
| 244 | |||
| 245 | #endif | ||
| 246 | |||
| 247 | #ifdef GEO_TBB | ||
| 248 | |||
| 249 | /** | ||
| 250 | * \brief TBB Thread Manager | ||
| 251 | * \details | ||
| 252 | * TBBThreadManager is an implementation of ThreadManager that uses TBB | ||
| 253 | * for running concurrent threads and control critical sections. | ||
| 254 | */ | ||
| 255 | class GEOGRAM_API TBBThreadManager : public ThreadManager { | ||
| 256 | public: | ||
| 257 | /** | ||
| 258 | * \brief Creates and initializes the TBB ThreadManager | ||
| 259 | */ | ||
| 260 | TBBThreadManager() { | ||
| 261 | } | ||
| 262 | |||
| 263 | /** \copydoc GEO::ThreadManager::maximum_concurrent_threads() */ | ||
| 264 | virtual index_t maximum_concurrent_threads() { | ||
| 265 | return tbb::this_task_arena::max_concurrency(); | ||
| 266 | } | ||
| 267 | |||
| 268 | protected: | ||
| 269 | /** \brief TBBThreadManager destructor */ | ||
| 270 | virtual ~TBBThreadManager() { | ||
| 271 | } | ||
| 272 | |||
| 273 | /** \copydoc GEO::ThreadManager::run_concurrent_threads() */ | ||
| 274 | virtual void run_concurrent_threads( | ||
| 275 | ThreadGroup& threads, index_t max_threads | ||
| 276 | ) { | ||
| 277 | tbb::task_arena arena(static_cast<std::int32_t>(max_threads)); | ||
| 278 | arena.execute([&threads] { | ||
| 279 | tbb::parallel_for( | ||
| 280 | tbb::blocked_range<std::size_t>(0, threads.size()), | ||
| 281 | [&threads](const tbb::blocked_range<std::size_t>& tbb_range) { | ||
| 282 | for (std::size_t i = tbb_range.begin(); i < tbb_range.end(); ++i) { | ||
| 283 | index_t ii = static_cast<index_t>(i); | ||
| 284 | set_thread_id(threads[ii],ii); | ||
| 285 | set_current_thread(threads[ii]); | ||
| 286 | threads[ii]->run(); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | ); | ||
| 290 | }); | ||
| 291 | |||
| 292 | } | ||
| 293 | }; | ||
| 294 | |||
| 295 | #endif | ||
| 296 | } | ||
| 297 | |||
| 298 | |||
| 299 | namespace { | ||
| 300 | /** | ||
| 301 | * \brief The (thread-local) variable that stores a | ||
| 302 | * pointer to the current thread. | ||
| 303 | * \details It cannot be a static member of class | ||
| 304 | * Thread, because Visual C++ does not accept | ||
| 305 | * to export thread local storage variables in | ||
| 306 | * DLLs. | ||
| 307 | */ | ||
| 308 | thread_local Thread* geo_current_thread_ = nullptr; | ||
| 309 | } | ||
| 310 | |||
| 311 | namespace GEO { | ||
| 312 | |||
| 313 | 11976 | void Thread::set_current(Thread* thread) { | |
| 314 | 11976 | geo_current_thread_ = thread; | |
| 315 | 11976 | } | |
| 316 | |||
| 317 | 4878 | Thread* Thread::current() { | |
| 318 | 4878 | return geo_current_thread_; | |
| 319 | } | ||
| 320 | |||
| 321 | 23968 | Thread::~Thread() { | |
| 322 | 23968 | } | |
| 323 | |||
| 324 | /************************************************************************/ | ||
| 325 | |||
| 326 | 502 | ThreadManager::~ThreadManager() { | |
| 327 | 502 | } | |
| 328 | |||
| 329 | 2766 | void ThreadManager::run_threads(ThreadGroup& threads) { | |
| 330 | 2766 | index_t max_threads = maximum_concurrent_threads(); | |
| 331 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 2766 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2766 times.
|
2766 | if(Process::multithreading_enabled() && max_threads > 1) { |
| 332 | 2766 | run_concurrent_threads(threads, max_threads); | |
| 333 | } else { | ||
| 334 | ✗ | for(index_t i = 0; i < threads.size(); i++) { | |
| 335 | ✗ | threads[i]->run(); | |
| 336 | } | ||
| 337 | } | ||
| 338 | 2766 | } | |
| 339 | |||
| 340 | /************************************************************************/ | ||
| 341 | |||
| 342 | ✗ | MonoThreadingThreadManager::~MonoThreadingThreadManager() { | |
| 343 | ✗ | } | |
| 344 | |||
| 345 | ✗ | void MonoThreadingThreadManager::run_concurrent_threads( | |
| 346 | ThreadGroup& threads, index_t max_threads | ||
| 347 | ) { | ||
| 348 | geo_argused(threads); | ||
| 349 | geo_argused(max_threads); | ||
| 350 | ✗ | geo_assert_not_reached; | |
| 351 | } | ||
| 352 | |||
| 353 | ✗ | index_t MonoThreadingThreadManager::maximum_concurrent_threads() { | |
| 354 | ✗ | return 1; | |
| 355 | } | ||
| 356 | |||
| 357 | /************************************************************************/ | ||
| 358 | |||
| 359 | namespace Process { | ||
| 360 | |||
| 361 | 251 | void initialize(int flags) { | |
| 362 | |||
| 363 | 251 | Environment* env = Environment::instance(); | |
| 364 | 251 | env->add_environment(new ProcessEnvironment); | |
| 365 | |||
| 366 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
|
251 | if(!os_init_threads()) { |
| 367 | #ifdef GEO_OPENMP | ||
| 368 | ✗ | Logger::out("Process") | |
| 369 | << "Using OpenMP threads" | ||
| 370 | << std::endl; | ||
| 371 | ✗ | set_thread_manager(new OMPThreadManager); | |
| 372 | #elif defined(GEO_TBB) | ||
| 373 | Logger::out("Process") | ||
| 374 | << "Using TBB threads" | ||
| 375 | << std::endl; | ||
| 376 | set_thread_manager(new TBBThreadManager); | ||
| 377 | #else | ||
| 378 | Logger::out("Process") | ||
| 379 | << "Multithreading not supported, going monothread" | ||
| 380 | << std::endl; | ||
| 381 | set_thread_manager(new MonoThreadingThreadManager); | ||
| 382 | #endif | ||
| 383 | } | ||
| 384 | |||
| 385 | if( | ||
| 386 |
1/2✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
|
251 | (::getenv("GEO_NO_SIGNAL_HANDLER") == nullptr) && |
| 387 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | ((flags & GEOGRAM_INSTALL_HANDLERS) != 0) |
| 388 | ) { | ||
| 389 | 251 | os_install_signal_handlers(); | |
| 390 | } | ||
| 391 | |||
| 392 | // Initialize Process default values | ||
| 393 | 251 | enable_multithreading(multithreading_enabled_); | |
| 394 | 251 | set_max_threads(number_of_cores()); | |
| 395 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | if (flags & GEOGRAM_INSTALL_FPE) { |
| 396 | 251 | enable_FPE(fpe_enabled_); | |
| 397 | } | ||
| 398 | 251 | enable_cancel(cancel_enabled_); | |
| 399 | |||
| 400 | 251 | start_time_ = Stopwatch::now(); | |
| 401 | 251 | } | |
| 402 | |||
| 403 | ✗ | void show_stats() { | |
| 404 | |||
| 405 | ✗ | Stopwatch::show_stats(); | |
| 406 | |||
| 407 | const size_t K=size_t(1024); | ||
| 408 | const size_t M=K*K; | ||
| 409 | const size_t G=K*M; | ||
| 410 | |||
| 411 | ✗ | size_t max_mem = Process::max_used_memory() ; | |
| 412 | size_t r = max_mem; | ||
| 413 | |||
| 414 | ✗ | size_t mem_G = r / G; | |
| 415 | ✗ | r = r % G; | |
| 416 | ✗ | size_t mem_M = r / M; | |
| 417 | ✗ | r = r % M; | |
| 418 | ✗ | size_t mem_K = r / K; | |
| 419 | ✗ | r = r % K; | |
| 420 | |||
| 421 | std::string s; | ||
| 422 | ✗ | if(mem_G != 0) { | |
| 423 | ✗ | s += String::to_string(mem_G)+"G "; | |
| 424 | } | ||
| 425 | ✗ | if(mem_M != 0) { | |
| 426 | ✗ | s += String::to_string(mem_M)+"M "; | |
| 427 | } | ||
| 428 | ✗ | if(mem_K != 0) { | |
| 429 | ✗ | s += String::to_string(mem_K)+"K "; | |
| 430 | } | ||
| 431 | ✗ | if(r != 0) { | |
| 432 | ✗ | s += String::to_string(r); | |
| 433 | } | ||
| 434 | |||
| 435 | ✗ | Logger::out("Process") << "Maximum used memory: " | |
| 436 | << max_mem << " (" << s << ")" | ||
| 437 | << std::endl; | ||
| 438 | ✗ | } | |
| 439 | |||
| 440 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | void terminate() { |
| 441 | thread_manager_.reset(); | ||
| 442 | 251 | } | |
| 443 | |||
| 444 | ✗ | void brute_force_kill() { | |
| 445 | ✗ | os_brute_force_kill(); | |
| 446 | ✗ | } | |
| 447 | |||
| 448 | 4021 | index_t number_of_cores() { | |
| 449 | static index_t result = 0; | ||
| 450 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 3770 times.
|
4021 | if(result == 0) { |
| 451 | #ifdef GEO_NO_THREAD_LOCAL | ||
| 452 | // Deactivate multithreading if thread_local is | ||
| 453 | // not supported (e.g. with old OS-X). | ||
| 454 | result = 1; | ||
| 455 | #else | ||
| 456 | 251 | result = os_number_of_cores(); | |
| 457 | #endif | ||
| 458 | } | ||
| 459 | 4021 | return result; | |
| 460 | } | ||
| 461 | |||
| 462 | ✗ | size_t used_memory() { | |
| 463 | ✗ | return os_used_memory(); | |
| 464 | } | ||
| 465 | |||
| 466 | ✗ | size_t max_used_memory() { | |
| 467 | ✗ | return os_max_used_memory(); | |
| 468 | } | ||
| 469 | |||
| 470 | ✗ | std::string executable_filename() { | |
| 471 | ✗ | return os_executable_filename(); | |
| 472 | } | ||
| 473 | |||
| 474 | ✗ | void print_stack_trace() { | |
| 475 | ✗ | os_print_stack_trace(); | |
| 476 | ✗ | } | |
| 477 | |||
| 478 | 251 | void set_thread_manager(ThreadManager* thread_manager) { | |
| 479 | 251 | thread_manager_ = thread_manager; | |
| 480 | 251 | } | |
| 481 | |||
| 482 | 2766 | void run_threads(ThreadGroup& threads) { | |
| 483 | 2766 | running_threads_invocations_++; | |
| 484 | 2766 | thread_manager_->run_threads(threads); | |
| 485 | 2766 | running_threads_invocations_--; | |
| 486 | 2766 | } | |
| 487 | |||
| 488 | 17692 | bool is_running_threads() { | |
| 489 | #ifdef GEO_OPENMP | ||
| 490 | return ( | ||
| 491 |
1/2✓ Branch 1 taken 17692 times.
✗ Branch 2 not taken.
|
17692 | omp_in_parallel() || |
| 492 |
2/2✓ Branch 0 taken 6639 times.
✓ Branch 1 taken 11053 times.
|
17692 | (running_threads_invocations_ > 0) |
| 493 | 17692 | ); | |
| 494 | #else | ||
| 495 | return running_threads_invocations_ > 0; | ||
| 496 | #endif | ||
| 497 | } | ||
| 498 | |||
| 499 | 3017 | bool multithreading_enabled() { | |
| 500 | 3017 | return multithreading_enabled_; | |
| 501 | } | ||
| 502 | |||
| 503 | 502 | void enable_multithreading(bool flag) { | |
| 504 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
|
502 | if( |
| 505 | 251 | multithreading_initialized_ && | |
| 506 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
|
251 | multithreading_enabled_ == flag |
| 507 | ) { | ||
| 508 | return; | ||
| 509 | } | ||
| 510 | 251 | multithreading_initialized_ = true; | |
| 511 | 251 | multithreading_enabled_ = flag; | |
| 512 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | if(multithreading_enabled_) { |
| 513 |
1/2✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
|
251 | Logger::out("Process") |
| 514 | << "Multithreading enabled" << std::endl | ||
| 515 |
1/2✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
|
251 | << "Available cores = " << number_of_cores() |
| 516 | << std::endl; | ||
| 517 | // Logger::out("Process") | ||
| 518 | // << "Max. concurrent threads = " | ||
| 519 | // << maximum_concurrent_threads() << std::endl ; | ||
| 520 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
|
251 | if(number_of_cores() == 1) { |
| 521 | ✗ | Logger::warn("Process") | |
| 522 | << "Processor is not a multicore" | ||
| 523 | << "(or multithread is not supported)" | ||
| 524 | << std::endl; | ||
| 525 | } | ||
| 526 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
|
251 | if(thread_manager_ == nullptr) { |
| 527 | ✗ | Logger::warn("Process") | |
| 528 | << "Missing multithreading manager" | ||
| 529 | << std::endl; | ||
| 530 | } | ||
| 531 | } else { | ||
| 532 | ✗ | Logger::out("Process") | |
| 533 | << "Multithreading disabled" << std::endl; | ||
| 534 | } | ||
| 535 | } | ||
| 536 | |||
| 537 | ✗ | index_t max_threads() { | |
| 538 | ✗ | return max_threads_initialized_ | |
| 539 | ✗ | ? max_threads_ | |
| 540 | ✗ | : number_of_cores(); | |
| 541 | } | ||
| 542 | |||
| 543 | 502 | void set_max_threads(index_t num_threads) { | |
| 544 | 502 | if( | |
| 545 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
|
502 | max_threads_initialized_ && |
| 546 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 251 times.
|
251 | max_threads_ == num_threads |
| 547 | ) { | ||
| 548 | return; | ||
| 549 | } | ||
| 550 | 251 | max_threads_initialized_ = true; | |
| 551 |
1/2✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
|
251 | if(num_threads == 0) { |
| 552 | num_threads = 1; | ||
| 553 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 251 times.
|
251 | } else if(num_threads > number_of_cores()) { |
| 554 | ✗ | Logger::warn("Process") | |
| 555 | << "Cannot allocate " << num_threads | ||
| 556 | << " for multithreading" | ||
| 557 | << std::endl; | ||
| 558 | ✗ | num_threads = number_of_cores(); | |
| 559 | } | ||
| 560 | 251 | max_threads_ = num_threads; | |
| 561 |
1/2✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
|
502 | Logger::out("Process") |
| 562 |
1/2✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
|
251 | << "Max used threads = " << max_threads_ |
| 563 | << std::endl; | ||
| 564 | } | ||
| 565 | |||
| 566 | 2377 | index_t maximum_concurrent_threads() { | |
| 567 |
2/4✓ Branch 0 taken 2377 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2377 times.
✗ Branch 3 not taken.
|
2377 | if(!multithreading_enabled_ || thread_manager_ == nullptr) { |
| 568 | return 1; | ||
| 569 | } | ||
| 570 | 2377 | return max_threads_; | |
| 571 | /* | ||
| 572 | // commented out for now, since under Windows, | ||
| 573 | // it seems that maximum_concurrent_threads() does not | ||
| 574 | // report the number of hyperthreaded cores. | ||
| 575 | return | ||
| 576 | geo_min( | ||
| 577 | thread_manager_->maximum_concurrent_threads(), | ||
| 578 | max_threads_ | ||
| 579 | ) ; | ||
| 580 | */ | ||
| 581 | } | ||
| 582 | |||
| 583 | 251 | bool FPE_enabled() { | |
| 584 | 251 | return fpe_enabled_; | |
| 585 | } | ||
| 586 | |||
| 587 | 502 | void enable_FPE(bool flag) { | |
| 588 |
3/4✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 251 times.
|
502 | if(fpe_initialized_ && fpe_enabled_ == flag) { |
| 589 | return; | ||
| 590 | } | ||
| 591 | 251 | fpe_initialized_ = true; | |
| 592 | 251 | fpe_enabled_ = flag; | |
| 593 | 251 | os_enable_FPE(flag); | |
| 594 | } | ||
| 595 | |||
| 596 | 251 | bool cancel_enabled() { | |
| 597 | 251 | return cancel_enabled_; | |
| 598 | } | ||
| 599 | |||
| 600 | 502 | void enable_cancel(bool flag) { | |
| 601 |
3/4✓ Branch 0 taken 251 times.
✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 251 times.
|
502 | if(cancel_initialized_ && cancel_enabled_ == flag) { |
| 602 | return; | ||
| 603 | } | ||
| 604 | 251 | cancel_initialized_ = true; | |
| 605 | 251 | cancel_enabled_ = flag; | |
| 606 | |||
| 607 |
1/2✓ Branch 1 taken 251 times.
✗ Branch 2 not taken.
|
251 | if(os_enable_cancel(flag)) { |
| 608 |
1/2✓ Branch 2 taken 251 times.
✗ Branch 3 not taken.
|
502 | Logger::out("Process") |
| 609 |
2/4✓ Branch 0 taken 251 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 251 times.
✗ Branch 4 not taken.
|
502 | << (flag ? "Cancel mode enabled" : "Cancel mode disabled") |
| 610 | << std::endl; | ||
| 611 | } else { | ||
| 612 | ✗ | Logger::warn("Process") | |
| 613 | << "Cancel mode not implemented" << std::endl; | ||
| 614 | } | ||
| 615 | } | ||
| 616 | } | ||
| 617 | } | ||
| 618 | |||
| 619 | |||
| 620 | namespace { | ||
| 621 | using namespace GEO; | ||
| 622 | |||
| 623 | /** | ||
| 624 | * \brief Used by the implementation of GEO::parallel() | ||
| 625 | * \see GEO::parallel() | ||
| 626 | */ | ||
| 627 | class ParallelThread : public Thread { | ||
| 628 | public: | ||
| 629 | /** | ||
| 630 | * \brief ParallelThread constructor. | ||
| 631 | * \param[in] func a void function with no parameter. | ||
| 632 | */ | ||
| 633 | 6622 | ParallelThread( | |
| 634 | std::function<void(void)> func | ||
| 635 |
1/2✓ Branch 1 taken 6622 times.
✗ Branch 2 not taken.
|
6622 | ) : func_(func) { |
| 636 | 6622 | } | |
| 637 | |||
| 638 | /** | ||
| 639 | * \copydoc Thread::run() | ||
| 640 | */ | ||
| 641 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6622 times.
|
6622 | void run() override { |
| 642 | func_(); | ||
| 643 | 6622 | } | |
| 644 | private: | ||
| 645 | std::function<void()> func_; | ||
| 646 | }; | ||
| 647 | |||
| 648 | |||
| 649 | /** | ||
| 650 | * \brief Used by the implementation of GEO::parallel_for() | ||
| 651 | * \see GEO::parallel_for() | ||
| 652 | */ | ||
| 653 | class ParallelForThread : public Thread { | ||
| 654 | public: | ||
| 655 | |||
| 656 | /** | ||
| 657 | * \param[in] func a void function that takes an index_t | ||
| 658 | * \param[in] from the first iteration index | ||
| 659 | * \param[in] to one position past the last interation index | ||
| 660 | * \param[in] step iteration step | ||
| 661 | */ | ||
| 662 | ParallelForThread( | ||
| 663 | std::function<void(index_t)> func, | ||
| 664 | index_t from, index_t to, index_t step=1 | ||
| 665 |
6/12✓ Branch 1 taken 1952 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1952 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 640 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 640 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1911 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1911 times.
✗ Branch 17 not taken.
|
4503 | ) : func_(func), from_(from), to_(to), step_(step) { |
| 666 | ✗ | } | |
| 667 | |||
| 668 | /** | ||
| 669 | * \copydoc Thread::run() | ||
| 670 | */ | ||
| 671 | 4503 | void run() override { | |
| 672 |
2/2✓ Branch 0 taken 2964539 times.
✓ Branch 1 taken 4503 times.
|
2969042 | for(index_t i = from_; i < to_; i += step_) { |
| 673 | 2964539 | func_(i); | |
| 674 | } | ||
| 675 | 4503 | } | |
| 676 | private: | ||
| 677 | std::function<void(index_t)> func_; | ||
| 678 | index_t from_; | ||
| 679 | index_t to_; | ||
| 680 | index_t step_; | ||
| 681 | }; | ||
| 682 | |||
| 683 | /** | ||
| 684 | * \brief Used by the implementation of GEO::parallel_for_slice() | ||
| 685 | * \see GEO::parallel_for_slice() | ||
| 686 | */ | ||
| 687 | class ParallelForSliceThread : public Thread { | ||
| 688 | public: | ||
| 689 | |||
| 690 | /** | ||
| 691 | * \param[in] func a void function that takes two index_t arguments | ||
| 692 | * \param[in] from the first iteration index | ||
| 693 | * \param[in] to one position past the last interation index | ||
| 694 | */ | ||
| 695 | 687 | ParallelForSliceThread( | |
| 696 | std::function<void(index_t,index_t)> func, | ||
| 697 | index_t from, index_t to | ||
| 698 |
1/2✓ Branch 1 taken 687 times.
✗ Branch 2 not taken.
|
687 | ) : func_(func), from_(from), to_(to) { |
| 699 | 687 | } | |
| 700 | |||
| 701 | /** | ||
| 702 | * \copydoc Thread::run() | ||
| 703 | */ | ||
| 704 | 687 | void run() override { | |
| 705 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 687 times.
|
687 | func_(from_, to_); |
| 706 | 687 | } | |
| 707 | private: | ||
| 708 | std::function<void(index_t,index_t)> func_; | ||
| 709 | index_t from_; | ||
| 710 | index_t to_; | ||
| 711 | }; | ||
| 712 | |||
| 713 | } | ||
| 714 | |||
| 715 | namespace GEO { | ||
| 716 | |||
| 717 | 1128 | void parallel_for( | |
| 718 | index_t from, index_t to, std::function<void(index_t)> func, | ||
| 719 | index_t threads_per_core, bool interleaved | ||
| 720 | ) { | ||
| 721 | #ifdef GEO_OS_WINDOWS | ||
| 722 | // TODO: This is a limitation of WindowsThreadManager, to be fixed. | ||
| 723 | threads_per_core = 1; | ||
| 724 | #endif | ||
| 725 | |||
| 726 | index_t nb_threads = std::min( | ||
| 727 | 1128 | to - from, | |
| 728 | 1128 | Process::maximum_concurrent_threads() * threads_per_core | |
| 729 | ); | ||
| 730 | |||
| 731 | nb_threads = std::max(index_t(1), nb_threads); | ||
| 732 | |||
| 733 | 1128 | index_t batch_size = (to - from) / nb_threads; | |
| 734 |
2/4✗ Branch 1 not taken.
✓ Branch 2 taken 1128 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1128 times.
|
1128 | if(Process::is_running_threads() || nb_threads == 1) { |
| 735 | ✗ | for(index_t i = from; i < to; i++) { | |
| 736 | ✗ | func(i); | |
| 737 | } | ||
| 738 | } else { | ||
| 739 | ThreadGroup threads; | ||
| 740 |
2/2✓ Branch 0 taken 640 times.
✓ Branch 1 taken 488 times.
|
1128 | if(interleaved) { |
| 741 |
2/2✓ Branch 0 taken 1952 times.
✓ Branch 1 taken 488 times.
|
2440 | for(index_t i = 0; i < nb_threads; i++) { |
| 742 | threads.push_back( | ||
| 743 | 1952 | new ParallelForThread( | |
| 744 | func, from + i, to, nb_threads | ||
| 745 |
2/6✓ Branch 1 taken 1952 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1952 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
3904 | ) |
| 746 | ); | ||
| 747 | } | ||
| 748 | } else { | ||
| 749 | index_t cur = from; | ||
| 750 |
2/2✓ Branch 0 taken 2551 times.
✓ Branch 1 taken 640 times.
|
3191 | for(index_t i = 0; i < nb_threads; i++) { |
| 751 |
2/2✓ Branch 0 taken 640 times.
✓ Branch 1 taken 1911 times.
|
2551 | if(i == nb_threads - 1) { |
| 752 | threads.push_back( | ||
| 753 | 640 | new ParallelForThread( | |
| 754 | func, cur, to | ||
| 755 |
2/6✓ Branch 1 taken 640 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 640 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
1280 | ) |
| 756 | ); | ||
| 757 | } else { | ||
| 758 | threads.push_back( | ||
| 759 | 1911 | new ParallelForThread( | |
| 760 | func, cur, cur + batch_size | ||
| 761 |
2/6✓ Branch 1 taken 1911 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1911 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
3822 | ) |
| 762 | ); | ||
| 763 | } | ||
| 764 | 2551 | cur += batch_size; | |
| 765 | } | ||
| 766 | } | ||
| 767 |
1/2✓ Branch 1 taken 1128 times.
✗ Branch 2 not taken.
|
1128 | Process::run_threads(threads); |
| 768 | 1128 | } | |
| 769 | 1128 | } | |
| 770 | |||
| 771 | |||
| 772 | 191 | void parallel_for_slice( | |
| 773 | index_t from, index_t to, std::function<void(index_t, index_t)> func, | ||
| 774 | index_t threads_per_core | ||
| 775 | ) { | ||
| 776 | #ifdef GEO_OS_WINDOWS | ||
| 777 | // TODO: This is a limitation of WindowsThreadManager, to be fixed. | ||
| 778 | threads_per_core = 1; | ||
| 779 | #endif | ||
| 780 | |||
| 781 | index_t nb_threads = std::min( | ||
| 782 | 191 | to - from, | |
| 783 | 191 | Process::maximum_concurrent_threads() * threads_per_core | |
| 784 | ); | ||
| 785 | |||
| 786 | nb_threads = std::max(index_t(1), nb_threads); | ||
| 787 | |||
| 788 | 191 | index_t batch_size = (to - from) / nb_threads; | |
| 789 |
3/4✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 178 times.
✓ Branch 4 taken 13 times.
|
191 | if(Process::is_running_threads() || nb_threads == 1) { |
| 790 | 13 | func(from, to); | |
| 791 | } else { | ||
| 792 | ThreadGroup threads; | ||
| 793 | index_t cur = from; | ||
| 794 |
2/2✓ Branch 0 taken 687 times.
✓ Branch 1 taken 178 times.
|
865 | for(index_t i = 0; i < nb_threads; i++) { |
| 795 |
2/2✓ Branch 0 taken 178 times.
✓ Branch 1 taken 509 times.
|
687 | if(i == nb_threads - 1) { |
| 796 | threads.push_back( | ||
| 797 | 178 | new ParallelForSliceThread( | |
| 798 | func, cur, to | ||
| 799 |
3/8✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
356 | ) |
| 800 | ); | ||
| 801 | } else { | ||
| 802 | threads.push_back( | ||
| 803 | 509 | new ParallelForSliceThread( | |
| 804 | func, cur, cur + batch_size | ||
| 805 |
3/8✓ Branch 1 taken 509 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 509 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 509 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
1018 | ) |
| 806 | ); | ||
| 807 | } | ||
| 808 | 687 | cur += batch_size; | |
| 809 | } | ||
| 810 |
1/2✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
|
178 | Process::run_threads(threads); |
| 811 | 178 | } | |
| 812 | 191 | } | |
| 813 | |||
| 814 | 473 | void parallel( | |
| 815 | std::function<void()> f1, | ||
| 816 | std::function<void()> f2 | ||
| 817 | ) { | ||
| 818 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
|
473 | if(Process::is_running_threads()) { |
| 819 | f1(); | ||
| 820 | f2(); | ||
| 821 | } else { | ||
| 822 | ThreadGroup threads; | ||
| 823 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f1)); |
| 824 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f2)); |
| 825 |
1/2✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
|
473 | Process::run_threads(threads); |
| 826 | 473 | } | |
| 827 | 473 | } | |
| 828 | |||
| 829 | |||
| 830 | 473 | void parallel( | |
| 831 | std::function<void()> f1, | ||
| 832 | std::function<void()> f2, | ||
| 833 | std::function<void()> f3, | ||
| 834 | std::function<void()> f4 | ||
| 835 | ) { | ||
| 836 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
|
473 | if(Process::is_running_threads()) { |
| 837 | f1(); | ||
| 838 | f2(); | ||
| 839 | f3(); | ||
| 840 | f4(); | ||
| 841 | } else { | ||
| 842 | ThreadGroup threads; | ||
| 843 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f1)); |
| 844 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f2)); |
| 845 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f3)); |
| 846 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f4)); |
| 847 |
1/2✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
|
473 | Process::run_threads(threads); |
| 848 | 473 | } | |
| 849 | 473 | } | |
| 850 | |||
| 851 | |||
| 852 | 473 | void parallel( | |
| 853 | std::function<void()> f1, | ||
| 854 | std::function<void()> f2, | ||
| 855 | std::function<void()> f3, | ||
| 856 | std::function<void()> f4, | ||
| 857 | std::function<void()> f5, | ||
| 858 | std::function<void()> f6, | ||
| 859 | std::function<void()> f7, | ||
| 860 | std::function<void()> f8 | ||
| 861 | ) { | ||
| 862 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 473 times.
|
473 | if(Process::is_running_threads()) { |
| 863 | f1(); | ||
| 864 | f2(); | ||
| 865 | f3(); | ||
| 866 | f4(); | ||
| 867 | f5(); | ||
| 868 | f6(); | ||
| 869 | f7(); | ||
| 870 | f8(); | ||
| 871 | } else { | ||
| 872 | ThreadGroup threads; | ||
| 873 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f1)); |
| 874 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f2)); |
| 875 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f3)); |
| 876 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f4)); |
| 877 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f5)); |
| 878 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f6)); |
| 879 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f7)); |
| 880 |
3/8✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 473 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 473 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
473 | threads.push_back(new ParallelThread(f8)); |
| 881 |
1/2✓ Branch 1 taken 473 times.
✗ Branch 2 not taken.
|
473 | Process::run_threads(threads); |
| 882 | 473 | } | |
| 883 | 473 | } | |
| 884 | |||
| 885 | namespace Process { | ||
| 886 | ✗ | void sleep(index_t microseconds) { | |
| 887 | ✗ | std::this_thread::sleep_for( | |
| 888 | ✗ | std::chrono::microseconds(microseconds) | |
| 889 | ); | ||
| 890 | ✗ | } | |
| 891 | } | ||
| 892 | } | ||
| 893 |