| 31 | */ |
| 32 | template <typename T, typename R = std::remove_cvref_t<decltype(std::declval<T>()().value())>> |
| 33 | class CCheckQueue |
| 34 | { |
| 35 | private: |
| 36 | //! Mutex to protect the inner state |
| 37 | Mutex m_mutex; |
| 38 | |
| 39 | //! Worker threads block on this when out of work |
| 40 | std::condition_variable m_worker_cv; |
| 41 | |
| 42 | //! Master thread blocks on this when out of work |
| 43 | std::condition_variable m_master_cv; |
| 44 | |
| 45 | //! The queue of elements to be processed. |
| 46 | //! As the order of booleans doesn't matter, it is used as a LIFO (stack) |
| 47 | std::vector<T> queue GUARDED_BY(m_mutex); |
| 48 | |
| 49 | //! The number of workers (including the master) that are idle. |
| 50 | int nIdle GUARDED_BY(m_mutex){0}; |
| 51 | |
| 52 | //! The total number of workers (including the master). |
| 53 | int nTotal GUARDED_BY(m_mutex){0}; |
| 54 | |
| 55 | //! The temporary evaluation result. |
| 56 | std::optional<R> m_result GUARDED_BY(m_mutex); |
| 57 | |
| 58 | /** |
| 59 | * Number of verifications that haven't completed yet. |
| 60 | * This includes elements that are no longer queued, but still in the |
| 61 | * worker's own batches. |
| 62 | */ |
| 63 | unsigned int nTodo GUARDED_BY(m_mutex){0}; |
| 64 | |
| 65 | //! The maximum number of elements to be processed in one batch |
| 66 | const unsigned int nBatchSize; |
| 67 | |
| 68 | std::vector<std::thread> m_worker_threads; |
| 69 | bool m_request_stop GUARDED_BY(m_mutex){false}; |
| 70 | |
| 71 | /// \anchor checkqueue |
| 72 | /** Internal function that does bulk of the verification work. If fMaster, return the final result. */ |
| 73 | std::optional<R> Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) |
| 74 | { |
| 75 | std::condition_variable& cond = fMaster ? m_master_cv : m_worker_cv; |
| 76 | std::vector<T> vChecks; |
| 77 | vChecks.reserve(nBatchSize); |
| 78 | unsigned int nNow = 0; |
| 79 | std::optional<R> local_result; |
| 80 | bool do_work; |
| 81 | do { |
| 82 | { |
| 83 | WAIT_LOCK(m_mutex, lock); |
| 84 | // first do the clean-up of the previous loop run (allowing us to do it in the same critsect) |
| 85 | if (nNow) { |
| 86 | if (local_result.has_value() && !m_result.has_value()) { |
| 87 | std::swap(local_result, m_result); |
| 88 | } |
| 89 | nTodo -= nNow; |
| 90 | if (nTodo == 0 && !fMaster) { |
nothing calls this directly
no test coverage detected