LCOV - code coverage report
Current view: top level - muse/framework/global - timer.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 1.2 % 80 1
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2025 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "timer.h"
      24              : 
      25              : #include <condition_variable>
      26              : #include <functional>
      27              : #include <queue>
      28              : #include <thread>
      29              : #include <atomic>
      30              : #include <mutex>
      31              : 
      32              : namespace muse {
      33              : class TimerScheduler
      34              : {
      35              : public:
      36              :     using Clock = std::chrono::steady_clock;
      37              :     using TimePoint = Clock::time_point;
      38              :     using Duration = Clock::duration;
      39              :     using Callback = std::function<void ()>;
      40              : 
      41            0 :     static TimerScheduler& instance()
      42              :     {
      43           50 :         static TimerScheduler scheduler;
      44            0 :         return scheduler;
      45              :     }
      46              : 
      47              :     struct TimerHandle {
      48              :         std::atomic_bool active { true };
      49              :     };
      50              : 
      51              :     using TimerHandlePtr = std::shared_ptr<TimerHandle>;
      52              : 
      53            0 :     TimerHandlePtr schedule(Duration delay, Callback callback, bool repeat = false)
      54              :     {
      55            0 :         auto handle = std::make_shared<TimerHandle>();
      56              : 
      57              :         {
      58            0 :             std::lock_guard lock(m_mutex);
      59            0 :             m_queue.push({
      60            0 :                     Clock::now() + delay,
      61              :                     delay,
      62              :                     repeat,
      63            0 :                     std::move(callback),
      64              :                     handle
      65              :                 });
      66            0 :         }
      67              : 
      68            0 :         m_cv.notify_one();
      69            0 :         return handle;
      70            0 :     }
      71              : 
      72            0 :     void cancel(const TimerHandlePtr& handle)
      73              :     {
      74            0 :         if (handle) {
      75            0 :             handle->active = false;
      76              :         }
      77            0 :         m_cv.notify_one();
      78            0 :     }
      79              : 
      80              : private:
      81            0 :     TimerScheduler()
      82            0 :         : m_running(true),
      83            0 :         m_thread([this] { run(); })
      84            0 :     {}
      85              : 
      86            0 :     ~TimerScheduler()
      87              :     {
      88              :         {
      89            0 :             std::lock_guard lock(m_mutex);
      90            0 :             m_running = false;
      91            0 :         }
      92            0 :         m_cv.notify_one();
      93            0 :         m_thread.join();
      94            0 :     }
      95              : 
      96              :     struct Entry {
      97              :         TimePoint time;
      98              :         Duration interval;
      99              :         bool repeat = false;
     100              :         Callback callback;
     101              :         TimerHandlePtr handle;
     102              : 
     103            0 :         bool operator>(const Entry& other) const
     104              :         {
     105            0 :             return time > other.time;
     106              :         }
     107              :     };
     108              : 
     109            0 :     void run()
     110              :     {
     111            0 :         std::unique_lock lock(m_mutex);
     112              : 
     113            0 :         while (m_running) {
     114            0 :             if (m_queue.empty()) {
     115            0 :                 m_cv.wait(lock);
     116            0 :                 continue;
     117              :             }
     118              : 
     119            0 :             auto& next = m_queue.top();
     120            0 :             if (m_cv.wait_until(lock, next.time) == std::cv_status::timeout) {
     121            0 :                 auto entry = m_queue.top();
     122            0 :                 m_queue.pop();
     123              : 
     124            0 :                 if (entry.handle->active) {
     125            0 :                     lock.unlock();
     126            0 :                     entry.callback();
     127            0 :                     lock.lock();
     128              : 
     129            0 :                     if (entry.repeat && entry.handle->active) {
     130            0 :                         entry.time = Clock::now() + entry.interval;
     131            0 :                         m_queue.push(entry);
     132              :                     }
     133              :                 }
     134            0 :             }
     135              :         }
     136            0 :     }
     137              : 
     138              :     std::priority_queue<Entry,
     139              :                         std::vector<Entry>,
     140              :                         std::greater<> > m_queue;
     141              : 
     142              :     bool m_running = false;
     143              :     std::mutex m_mutex;
     144              :     std::condition_variable m_cv;
     145              :     std::thread m_thread;
     146              : };
     147              : }
     148              : 
     149              : using namespace muse;
     150              : 
     151            0 : Timer::Timer(time_t interval)
     152            0 :     : m_interval(interval) {}
     153              : 
     154            0 : Timer::~Timer()
     155              : {
     156            0 :     stop();
     157            0 : }
     158              : 
     159            0 : void Timer::onTimeout(const async::Asyncable* receiver, Callback callback)
     160              : {
     161            0 :     m_notification.onNotify(receiver, std::move(callback), async::Asyncable::Mode::SetReplace);
     162            0 : }
     163              : 
     164            0 : void Timer::start()
     165              : {
     166            0 :     if (m_handle) {
     167            0 :         return;
     168              :     }
     169              : 
     170            0 :     m_started = std::chrono::steady_clock::now();
     171              : 
     172            0 :     auto realHandle = TimerScheduler::instance().schedule(
     173            0 :         m_interval,
     174            0 :         [this]() { m_notification.notify(); },
     175              :         true
     176            0 :         );
     177              : 
     178            0 :     m_handle = std::static_pointer_cast<void>(realHandle);
     179            0 : }
     180              : 
     181            0 : void Timer::stop()
     182              : {
     183            0 :     if (!m_handle) {
     184            0 :         return;
     185              :     }
     186              : 
     187            0 :     auto realHandle = std::static_pointer_cast<TimerScheduler::TimerHandle>(m_handle);
     188            0 :     TimerScheduler::instance().cancel(realHandle);
     189            0 :     m_handle = nullptr;
     190            0 : }
     191              : 
     192            0 : bool Timer::isActive() const
     193              : {
     194            0 :     return m_handle != nullptr;
     195              : }
     196              : 
     197            0 : float Timer::secondsSinceStart() const
     198              : {
     199            0 :     std::chrono::duration<float, std::ratio<1> > diff(std::chrono::steady_clock::now() - m_started);
     200            0 :     return diff.count();
     201              : }
        

Generated by: LCOV version 2.0-1