src/util/ThreadPool.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright (c) 2021-2026 ChilliBits. All rights reserved. | ||
| 2 | |||
| 3 | #include "ThreadPool.h" | ||
| 4 | |||
| 5 | #include <cassert> | ||
| 6 | #include <utility> | ||
| 7 | |||
| 8 | namespace spice::compiler { | ||
| 9 | |||
| 10 |
1/2✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 25 not taken.
|
4 | ThreadPool::ThreadPool(size_t threadCount) { |
| 11 |
1/2✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 4 times.
|
4 | assert(threadCount > 0); |
| 12 |
1/2✓ Branch 10 → 11 taken 4 times.
✗ Branch 10 → 17 not taken.
|
4 | workers.reserve(threadCount); |
| 13 |
2/2✓ Branch 14 → 12 taken 14 times.
✓ Branch 14 → 15 taken 4 times.
|
18 | for (size_t i = 0; i < threadCount; i++) |
| 14 |
1/2✓ Branch 12 → 13 taken 14 times.
✗ Branch 12 → 16 not taken.
|
28 | workers.emplace_back([this] { workerLoop(); }); |
| 15 | 4 | } | |
| 16 | |||
| 17 | 4 | ThreadPool::~ThreadPool() { | |
| 18 | { | ||
| 19 | 4 | std::lock_guard lock(mutex); | |
| 20 | 4 | shuttingDown = true; | |
| 21 | 4 | } | |
| 22 | 4 | taskAvailable.notify_all(); | |
| 23 |
2/2✓ Branch 19 → 7 taken 14 times.
✓ Branch 19 → 20 taken 4 times.
|
22 | for (std::thread &worker : workers) |
| 24 | 14 | worker.join(); | |
| 25 | 4 | } | |
| 26 | |||
| 27 | /** | ||
| 28 | * Enqueue a task for execution on one of the worker threads | ||
| 29 | * | ||
| 30 | * @param task Task to execute | ||
| 31 | */ | ||
| 32 | 1305 | void ThreadPool::submit(std::function<void()> task) { | |
| 33 | { | ||
| 34 |
1/2✓ Branch 2 → 3 taken 1305 times.
✗ Branch 2 → 14 not taken.
|
1305 | std::lock_guard lock(mutex); |
| 35 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1305 times.
|
1305 | assert(!shuttingDown); |
| 36 |
1/2✓ Branch 7 → 8 taken 1305 times.
✗ Branch 7 → 11 not taken.
|
2610 | tasks.emplace(nextTaskIndex++, std::move(task)); |
| 37 | 1305 | pendingCount++; | |
| 38 | 1305 | } | |
| 39 | 1305 | taskAvailable.notify_one(); | |
| 40 | 1305 | } | |
| 41 | |||
| 42 | /** | ||
| 43 | * Block until all submitted tasks are done and re-throw the exception of the first failing task, if there was one. | ||
| 44 | * Afterwards the pool is reset and can be used for the next batch of tasks. | ||
| 45 | */ | ||
| 46 | 5 | void ThreadPool::waitForAll() { | |
| 47 | 5 | std::exception_ptr failure; | |
| 48 | { | ||
| 49 |
1/2✓ Branch 3 → 4 taken 5 times.
✗ Branch 3 → 17 not taken.
|
5 | std::unique_lock lock(mutex); |
| 50 |
1/2✓ Branch 4 → 5 taken 5 times.
✗ Branch 4 → 15 not taken.
|
15 | allTasksDone.wait(lock, [this] { return pendingCount == 0; }); |
| 51 | // Reset for the next batch | ||
| 52 | 5 | failure = std::exchange(firstException, nullptr); | |
| 53 | 5 | canceled = false; | |
| 54 | 5 | nextTaskIndex = 0; | |
| 55 | 5 | } | |
| 56 |
2/2✓ Branch 10 → 11 taken 2 times.
✓ Branch 10 → 13 taken 3 times.
|
5 | if (failure) |
| 57 | 4 | std::rethrow_exception(failure); | |
| 58 | 5 | } | |
| 59 | |||
| 60 | /** | ||
| 61 | * Body of a worker thread: take tasks off the queue until the pool shuts down | ||
| 62 | */ | ||
| 63 | 14 | void ThreadPool::workerLoop() { | |
| 64 | while (true) { | ||
| 65 | 1311 | Task task; | |
| 66 | bool skip; | ||
| 67 | { | ||
| 68 |
1/2✓ Branch 4 → 5 taken 1319 times.
✗ Branch 4 → 36 not taken.
|
1313 | std::unique_lock lock(mutex); |
| 69 |
5/6✓ Branch 2 → 3 taken 1448 times.
✓ Branch 2 → 5 taken 14 times.
✓ Branch 4 → 5 taken 1305 times.
✓ Branch 4 → 6 taken 143 times.
✓ Branch 5 → 6 taken 1319 times.
✗ Branch 5 → 34 not taken.
|
2781 | taskAvailable.wait(lock, [this] { return shuttingDown || !tasks.empty(); }); |
| 70 |
2/2✓ Branch 7 → 8 taken 14 times.
✓ Branch 7 → 11 taken 1305 times.
|
1319 | if (tasks.empty()) { |
| 71 |
1/2✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 14 times.
|
14 | assert(shuttingDown); |
| 72 | 27 | return; | |
| 73 | } | ||
| 74 | 2610 | task = std::move(tasks.front()); | |
| 75 | 1305 | tasks.pop(); | |
| 76 | // Do not start new work after another task has already failed | ||
| 77 | 1305 | skip = canceled; | |
| 78 |
2/2✓ Branch 18 → 19 taken 1305 times.
✓ Branch 18 → 21 taken 14 times.
|
1319 | } |
| 79 | |||
| 80 |
2/2✓ Branch 20 → 22 taken 1113 times.
✓ Branch 20 → 23 taken 192 times.
|
1305 | if (!skip) { |
| 81 | try { | ||
| 82 |
2/2✓ Branch 22 → 23 taken 1108 times.
✓ Branch 22 → 37 taken 2 times.
|
1113 | task.job(); |
| 83 | 2 | } catch (...) { | |
| 84 |
1/2✓ Branch 39 → 40 taken 2 times.
✗ Branch 39 → 52 not taken.
|
2 | std::lock_guard lock(mutex); |
| 85 | // Keep the exception of the earliest submitted task, to make error reporting independent of the scheduling | ||
| 86 |
2/6✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 2 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 44 not taken.
✓ Branch 45 → 46 taken 2 times.
✗ Branch 45 → 50 not taken.
|
2 | if (!firstException || task.index < firstExceptionTaskIndex) { |
| 87 | 2 | firstException = std::current_exception(); | |
| 88 | 2 | firstExceptionTaskIndex = task.index; | |
| 89 | } | ||
| 90 | 2 | canceled = true; | |
| 91 |
1/2✓ Branch 51 → 23 taken 2 times.
✗ Branch 51 → 56 not taken.
|
2 | } |
| 92 | } | ||
| 93 | |||
| 94 | { | ||
| 95 |
1/2✓ Branch 23 → 24 taken 1305 times.
✗ Branch 23 → 55 not taken.
|
1302 | std::lock_guard lock(mutex); |
| 96 | 1305 | pendingCount--; | |
| 97 |
2/2✓ Branch 24 → 25 taken 59 times.
✓ Branch 24 → 26 taken 1246 times.
|
1305 | if (pendingCount == 0) |
| 98 | 59 | allTasksDone.notify_all(); | |
| 99 | 1305 | } | |
| 100 |
2/2✓ Branch 29 → 30 taken 1297 times.
✓ Branch 29 → 32 taken 13 times.
|
1319 | } |
| 101 | } | ||
| 102 | |||
| 103 | } // namespace spice::compiler | ||
| 104 |