| 45 | } |
| 46 | |
| 47 | void ThreadPool::startThreads(size_t num) { |
| 48 | const scoped_lock lock{mThreadsMutex}; |
| 49 | if (mShuttingDown) { |
| 50 | return; |
| 51 | } |
| 52 | |
| 53 | mNumThreads += num; |
| 54 | for (size_t i = mThreads.size(); i < mNumThreads; ++i) { |
| 55 | mThreads.emplace_back([this] { |
| 56 | const auto id = this_thread::get_id(); |
| 57 | // tlog::debug("Spawning thread pool thread {}", id); |
| 58 | |
| 59 | while (true) { |
| 60 | QueuedTask task = {}; |
| 61 | while (!mTaskQueueSemaphore.wait()) {} |
| 62 | |
| 63 | { |
| 64 | const scoped_lock taskQueueLock{mTaskQueueMutex}; |
| 65 | TEV_ASSERT(!mTaskQueue.empty(), "Task queue was empty after semaphore wait."); |
| 66 | |
| 67 | task = mTaskQueue.pop(); |
| 68 | } |
| 69 | |
| 70 | task.fun(); |
| 71 | mNumTasksInSystem--; |
| 72 | |
| 73 | if (task.stopToken) { |
| 74 | break; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | const scoped_lock threadsLock{mThreadsMutex}; |
| 79 | |
| 80 | // Remove oneself from the thread pool. NOTE: at this point, the lock is still held, so modifying mThreads is safe. |
| 81 | // tlog::debug("Shutting down thread pool thread {}", id); |
| 82 | |
| 83 | const auto it = ranges::find(mThreads, id, [](const auto& t) { return t.get_id(); }); |
| 84 | TEV_ASSERT(it != mThreads.end(), "Thread not found in thread pool."); |
| 85 | |
| 86 | // Thread must be detached, otherwise running our own constructor while still running would result in errors. |
| 87 | thread self = std::move(*it); |
| 88 | mThreads.erase(it); |
| 89 | |
| 90 | self.detach(); |
| 91 | }); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | void ThreadPool::shutdownThreads(size_t num) { |
| 96 | mNumThreads -= num; |