LCOV - code coverage report
Current view: top level - muse/framework/audio/common - realtimethreadpool.h Coverage Total Hit
Test: filtered_coverage.info Lines: 53.0 % 83 44
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : #include "audioworkgroup.h"
       4              : #include "concurrency/threadutils.h"
       5              : #include "runtime.h"
       6              : #include "concurrentqueue.h"
       7              : #include "global/functional/inplace_function.h"
       8              : #include "lightweightsemaphore.h"
       9              : #include "thirdparty/kors_logger/src/log_base.h"
      10              : 
      11              : #include <atomic>
      12              : #include <exception>
      13              : #include <memory>
      14              : #include <mutex>
      15              : #include <set>
      16              : #include <string>
      17              : #include <thread>
      18              : #include <vector>
      19              : 
      20              : namespace muse::audio {
      21              : class RealtimeThreadPool
      22              : {
      23              :     struct InflightSemaphoreRelease {
      24            0 :         explicit InflightSemaphoreRelease(muse::LightweightSemaphore& semaphore)
      25            0 :             : m_semaphore(semaphore) {}
      26              : 
      27              :         InflightSemaphoreRelease(const InflightSemaphoreRelease&) = delete;
      28              :         InflightSemaphoreRelease& operator=(const InflightSemaphoreRelease&) = delete;
      29              : 
      30            0 :         ~InflightSemaphoreRelease() noexcept
      31              :         {
      32            0 :             m_semaphore.signal();
      33            0 :         }
      34              : 
      35              :         muse::LightweightSemaphore& m_semaphore;
      36              :     };
      37              : 
      38              : public:
      39              :     static constexpr int maxTaskCount = 10000;
      40              :     using Task = muse::functional::inplace_function<void (), 64>;
      41           24 :     RealtimeThreadPool(
      42              :         std::string name,
      43              :         int num_of_workers = std::thread::hardware_concurrency())
      44           24 :     {
      45           24 :         num_of_workers = std::max(0, num_of_workers);
      46           24 :         m_workers.reserve(num_of_workers);
      47              :         try {
      48           72 :             for (size_t i = 0; i < static_cast<size_t>(num_of_workers); ++i) {
      49           48 :                 auto worker = std::make_unique<Worker>();
      50           48 :                 Worker* workerPtr = worker.get();
      51           48 :                 const size_t workerIndex = i;
      52           96 :                 worker->m_thread = std::make_unique<std::thread>(
      53           96 :                     [this, workerPtr, workerIndex, name] {
      54           48 :                     muse::runtime::setThreadName(name + "_" + std::to_string(workerIndex));
      55           48 :                     AudioWorkgroupToken workgroupToken;
      56              :                     for (;;) {
      57           48 :                         Task task;
      58              : 
      59           48 :                         m_queue.wait_dequeue(task);
      60              :                         {
      61           47 :                             std::lock_guard lock(workerPtr->m_workgroupMutex);
      62           48 :                             workerPtr->m_workgroup.join(workgroupToken);
      63           47 :                         }
      64              : 
      65           47 :                         if (this->m_should_stop) {
      66           93 :                             return;
      67              :                         }
      68              : 
      69            0 :                         InflightSemaphoreRelease release(m_inflightSemaphore);
      70            0 :                         task();
      71           46 :                     }
      72           94 :                 });
      73           48 :                 m_workers.push_back(std::move(worker));
      74           48 :                 muse::setThreadPriority(*m_workers.back()->m_thread, ThreadPriority::High);
      75           48 :             }
      76            0 :         } catch (...) {
      77            0 :             terminate();
      78            0 :             throw;
      79            0 :         }
      80           24 :     }
      81              : 
      82           27 :     void setAudioWorkgroup(muse::audio::AudioWorkGroup audioworkgroup)
      83              :     {
      84           81 :         for (auto& worker : m_workers) {
      85           54 :             std::lock_guard lock(worker->m_workgroupMutex);
      86           54 :             worker->m_workgroup = audioworkgroup;
      87           54 :         }
      88           27 :     }
      89              : 
      90            0 :     bool enqueue(const Task& func)
      91              :     {
      92            0 :         m_inflightSemaphore.wait();
      93            0 :         if (!m_queue.enqueue(func)) {
      94            0 :             m_inflightSemaphore.signal();
      95            0 :             return false;
      96              :         }
      97            0 :         return true;
      98              :     }
      99              : 
     100            0 :     void participateAndWait()
     101              :     {
     102            0 :         Task task;
     103            0 :         while (m_queue.try_dequeue(task)) {
     104            0 :             InflightSemaphoreRelease release(m_inflightSemaphore);
     105            0 :             task();
     106            0 :         }
     107            0 :         waitUntilFinished();
     108            0 :         m_inflightSemaphore.signal(maxTaskCount);
     109            0 :     }
     110              : 
     111            0 :     std::set<std::thread::id> threadIdSet() const
     112              :     {
     113            0 :         std::set<std::thread::id> result;
     114              : 
     115            0 :         for (const auto& worker : m_workers) {
     116            0 :             result.insert(worker->m_thread->get_id());
     117              :         }
     118              : 
     119            0 :         return result;
     120            0 :     }
     121              : 
     122           27 :     int getNumberOfWorkers() const
     123              :     {
     124           27 :         return static_cast<int>(m_workers.size());
     125              :     }
     126              : 
     127           24 :     ~RealtimeThreadPool()
     128              :     {
     129           24 :         terminate();
     130           24 :     }
     131              : 
     132              : private:
     133            0 :     void waitUntilFinished()
     134              :     {
     135            0 :         auto actuallyAwaited = m_inflightSemaphore.waitMany(maxTaskCount);
     136            0 :         for (size_t i = 0;
     137            0 :              i < static_cast<size_t>(maxTaskCount - actuallyAwaited); ++i) {
     138            0 :             m_inflightSemaphore.wait();
     139              :         }
     140            0 :     }
     141              : 
     142           24 :     void terminate()
     143              :     {
     144           24 :         m_should_stop = true;
     145           72 :         for (size_t i = 0; i < m_workers.size(); ++i) {
     146           48 :             IF_ASSERT_FAILED(m_queue.enqueue([] {})) {
     147              :                 // this is _extremely_ unlikely to fail. But if it does the threads would hang indefinitely.
     148            0 :                 std::terminate();
     149              :             }
     150              :         }
     151              : 
     152           72 :         for (auto& worker : m_workers) {
     153           48 :             if (worker->m_thread && worker->m_thread->joinable()) {
     154           48 :                 worker->m_thread->join();
     155              :             }
     156              :         }
     157           24 :     }
     158              : 
     159              :     struct Worker {
     160              :         std::unique_ptr<std::thread> m_thread;
     161              :         AudioWorkGroup m_workgroup;
     162              :         std::mutex m_workgroupMutex;
     163              :     };
     164              : 
     165              :     muse::BlockingConcurrentQueue<Task > m_queue;
     166              :     std::vector<std::unique_ptr<Worker> > m_workers;
     167              :     muse::LightweightSemaphore m_inflightSemaphore { maxTaskCount };
     168              :     std::atomic<bool> m_should_stop{ false };
     169              : }; // namespace muse::audio
     170              : } // namespace muse::audio
        

Generated by: LCOV version 2.0-1