|
13 | 13 | #include <functional> |
14 | 14 | #include <mutex> |
15 | 15 | #include <queue> |
| 16 | +#include <readerwriterqueue.h> |
16 | 17 | #include <thread> |
17 | 18 |
|
18 | 19 | namespace SQLiteNested { |
@@ -88,7 +89,7 @@ class ThreadPool { |
88 | 89 | public: |
89 | 90 | ThreadPool(size_t max_threads, size_t max_jobs) |
90 | 91 | : max_threads_(max_threads), max_jobs_(max_jobs), ser_queue_(job_greater_) {} |
91 | | - ~ThreadPool() { |
| 92 | + virtual ~ThreadPool() { |
92 | 93 | { |
93 | 94 | std::lock_guard<std::mutex> lock(mutex_); |
94 | 95 | shutdown_ = true; |
@@ -140,4 +141,58 @@ class ThreadPool { |
140 | 141 | } |
141 | 142 | }; |
142 | 143 |
|
| 144 | +// Adds lock-free EnqueueFast() for use on critical paths. |
| 145 | +// - EnqueueFast() never blocks (!) |
| 146 | +// - Only one thread should ever use it |
| 147 | +class ThreadPoolWithEnqueueFast : public ThreadPool { |
| 148 | + // concept: foreground thread adds job onto a lock-free queue, which a single background thread |
| 149 | + // consumes to Enqueue() |
| 150 | + |
| 151 | + struct EnqueueFastJob { |
| 152 | + bool shutdown = false; |
| 153 | + void *x = nullptr; |
| 154 | + std::function<void *(void *) noexcept> par; |
| 155 | + std::function<void(void *) noexcept> ser; |
| 156 | + }; |
| 157 | + |
| 158 | + moodycamel::BlockingReaderWriterQueue<EnqueueFastJob> fast_queue_; |
| 159 | + std::unique_ptr<std::thread> worker_thread_; |
| 160 | + |
| 161 | + void EnqueueFastWorker() { |
| 162 | + EnqueueFastJob job; |
| 163 | + while (true) { |
| 164 | + fast_queue_.wait_dequeue(job); |
| 165 | + if (job.shutdown) { |
| 166 | + break; |
| 167 | + } |
| 168 | + this->Enqueue(job.x, job.par, job.ser); |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + public: |
| 173 | + ThreadPoolWithEnqueueFast(size_t max_threads, size_t max_jobs) |
| 174 | + : ThreadPool(max_threads, max_jobs), fast_queue_(max_jobs) {} |
| 175 | + |
| 176 | + ~ThreadPoolWithEnqueueFast() { |
| 177 | + if (worker_thread_) { |
| 178 | + EnqueueFastJob job; |
| 179 | + job.shutdown = true; |
| 180 | + fast_queue_.enqueue(job); |
| 181 | + worker_thread_->join(); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + void EnqueueFast(void *x, std::function<void *(void *) noexcept> par, |
| 186 | + std::function<void(void *) noexcept> ser) { |
| 187 | + EnqueueFastJob job; |
| 188 | + job.x = x; |
| 189 | + job.par = par; |
| 190 | + job.ser = ser; |
| 191 | + fast_queue_.enqueue(job); |
| 192 | + if (!worker_thread_) { |
| 193 | + worker_thread_.reset(new std::thread([this]() { this->EnqueueFastWorker(); })); |
| 194 | + } |
| 195 | + } |
| 196 | +}; |
| 197 | + |
143 | 198 | } // namespace SQLiteNested |
0 commit comments