LCOV - code coverage report
Current view: top level - src/base - message_loop.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 667 674 99.0 %
Date: 2026-06-27 19:56:04 Functions: 92 93 98.9 %
Branches: 611 918 66.6 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./base/message_loop.h"
      25                 :            : 
      26                 :            : #include <atomic>
      27                 :            : #include <deque>
      28                 :            : #include <memory>
      29                 :            : #include <mutex>
      30                 :            : #include <optional>
      31                 :            : #include <queue>
      32                 :            : #include <string>
      33                 :            : #include <thread>
      34                 :            : #include <tuple>
      35                 :            : #include <unordered_set>
      36                 :            : #include <utility>
      37                 :            : #include <vector>
      38                 :            : 
      39                 :            : #include "./base/condition_variable.h"
      40                 :            : #include "./base/logger.h"
      41                 :            : #include "./base/memory_pool.h"
      42                 :            : #include "./base/memory_resource.h"
      43                 :            : #include "./base/mpmc_queue.h"
      44                 :            : #include "./base/utils.h"
      45                 :            : 
      46                 :            : #ifdef _WIN32
      47                 :            : #include <Windows.h>
      48                 :            : #undef max
      49                 :            : #endif
      50                 :            : 
      51                 :            : namespace vlink {
      52                 :            : 
      53                 :            : static constexpr size_t kMaxTaskSize = 10000U;
      54                 :            : static constexpr size_t kMaxTimerSize = 100U;
      55                 :            : static constexpr uint32_t kMaxElapsedTime = 0U;
      56                 :            : static constexpr int kMaxLockfreePushRetry = 32;
      57                 :            : 
      58                 :            : template <typename TypeT, typename TimeT, typename ReturnT>
      59                 :       4643 : static ReturnT get_current_time() noexcept {
      60                 :       4643 :   const auto& duration = std::chrono::duration_cast<TimeT>(TypeT::now().time_since_epoch());
      61                 :            : 
      62                 :       4643 :   return static_cast<ReturnT>(duration.count());
      63                 :            : }
      64                 :            : 
      65                 :            : // MessageLoopGlobal
      66                 :            : struct MessageLoopGlobal final {
      67                 :            :   std::atomic<int> instance_index{0};
      68                 :            : 
      69                 :        624 :   static MessageLoopGlobal& get() {
      70                 :            :     static MessageLoopGlobal instance;
      71                 :            : 
      72                 :        624 :     return instance;
      73                 :            :   }
      74                 :            : 
      75                 :            :  private:
      76                 :            :   MessageLoopGlobal() = default;
      77                 :            : };
      78                 :            : 
      79                 :            : // MessageLoop::Impl
      80                 :            : struct MessageLoop::Impl final {  // NOLINT(clang-analyzer-optin.performance.Padding)
      81                 :            :   using NormalTaskTuple = std::tuple<uint32_t, bool, MessageLoop::Callback>;
      82                 :            :   using LockfreeTaskTuple = std::tuple<uint32_t, MessageLoop::Callback>;
      83                 :            :   using PriorityTaskTuple = std::tuple<uint32_t, uint32_t, uint32_t, bool, MessageLoop::Callback>;
      84                 :            : 
      85                 :            :   struct PriorityCompare final {
      86                 :          7 :     bool operator()(const PriorityTaskTuple& lhs, const PriorityTaskTuple& rhs) const {
      87                 :          7 :       return priority_key(lhs) > priority_key(rhs);
      88                 :            :     }
      89                 :            :   };
      90                 :            : 
      91                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
      92                 :            :   using NormalQueue = std::pmr::deque<NormalTaskTuple>;
      93                 :            :   using LockfreeQueue = MpmcQueue<LockfreeTaskTuple>;
      94                 :            :   using PriorityQueue = std::priority_queue<PriorityTaskTuple, std::pmr::vector<PriorityTaskTuple>, PriorityCompare>;
      95                 :            : #else
      96                 :            :   using NormalQueue = std::deque<NormalTaskTuple>;
      97                 :            :   using LockfreeQueue = MpmcQueue<LockfreeTaskTuple>;
      98                 :            :   using PriorityQueue = std::priority_queue<PriorityTaskTuple, std::vector<PriorityTaskTuple>, PriorityCompare>;
      99                 :            : #endif
     100                 :            : 
     101                 :         16 :   static uint64_t priority_key(const PriorityTaskTuple& task) {
     102                 :         16 :     return (static_cast<uint64_t>(std::get<0>(task)) << 32) | std::get<1>(task);
     103                 :            :   }
     104                 :            : 
     105                 :          1 :   static bool priority_before(const PriorityTaskTuple& lhs, const PriorityTaskTuple& rhs) {
     106                 :          1 :     return priority_key(lhs) < priority_key(rhs);
     107                 :            :   }
     108                 :            : 
     109                 :            :   alignas(64) std::atomic_bool is_running{false};
     110                 :            :   alignas(64) std::atomic_bool quit_flag{false};
     111                 :            :   alignas(64) std::atomic_bool force_quit_flag{false};
     112                 :            :   alignas(64) std::atomic_bool is_busy{false};
     113                 :            :   alignas(64) std::atomic_bool wakeup_pending{false};
     114                 :            :   alignas(64) std::atomic_bool lockfree_needs_reset{false};
     115                 :            :   std::shared_ptr<MessageLoop::AliveState> alive_state{MemoryResource::make_shared<MessageLoop::AliveState>()};
     116                 :            : 
     117                 :            :   std::atomic<std::thread::id> thread_id;
     118                 :            :   alignas(64) std::atomic_size_t lockfree_task_count{0U};
     119                 :            :   alignas(64) std::atomic_size_t lockfree_producer_count{0U};
     120                 :            : 
     121                 :            : #ifdef _WIN32
     122                 :            :   std::atomic<HANDLE> thread_handle{nullptr};
     123                 :            : #endif
     124                 :            : 
     125                 :            :   std::string name;
     126                 :            :   MessageLoop::Type type{MessageLoop::kNormalType};
     127                 :            :   std::atomic<MessageLoop::Strategy> strategy{MessageLoop::kOptimizationStrategy};
     128                 :            : 
     129                 :            :   uint32_t task_seq{0};
     130                 :            :   std::optional<NormalQueue> normal_queue;
     131                 :            :   std::optional<LockfreeQueue> lockfree_queue;
     132                 :            :   std::optional<PriorityQueue> priority_droppable_queue;
     133                 :            :   std::optional<PriorityQueue> priority_protected_queue;
     134                 :            : 
     135                 :            :   MessageLoop::Callback begin_callback;
     136                 :            :   MessageLoop::Callback end_callback;
     137                 :            :   MessageLoop::Callback idle_callback;
     138                 :            : 
     139                 :            :   std::thread thread;
     140                 :            :   std::unordered_set<Timer*> timer_set;
     141                 :            :   std::mutex mtx;
     142                 :            :   ConditionVariable cv;
     143                 :            : };
     144                 :            : 
     145                 :            : // MessageLoop
     146                 :        565 : MessageLoop::MessageLoop() : impl_(std::make_unique<Impl>()) {
     147                 :        565 :   impl_->name =
     148   [ +  -  +  - ]:       1695 :       "MessageLoop_" + std::to_string(MessageLoopGlobal::get().instance_index.fetch_add(1, std::memory_order_relaxed));
     149                 :            : 
     150         [ +  - ]:        565 :   MemoryPool::global_instance();
     151                 :            : 
     152                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
     153   [ +  -  +  - ]:        565 :   impl_->normal_queue.emplace(&MemoryResource::global_instance());
     154                 :            : #else
     155                 :            :   impl_->normal_queue.emplace();
     156                 :            : #endif
     157                 :        565 : }
     158                 :            : 
     159                 :         59 : MessageLoop::MessageLoop(Type type) : impl_(std::make_unique<Impl>()) {
     160                 :         59 :   impl_->name =
     161   [ +  -  +  - ]:        177 :       "MessageLoop_" + std::to_string(MessageLoopGlobal::get().instance_index.fetch_add(1, std::memory_order_relaxed));
     162                 :         59 :   impl_->type = type;
     163                 :            : 
     164         [ +  - ]:         59 :   MemoryPool::global_instance();
     165                 :            : 
     166         [ +  + ]:         59 :   if (impl_->type == kNormalType) {
     167                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
     168   [ +  -  +  - ]:         28 :     impl_->normal_queue.emplace(&MemoryResource::global_instance());
     169                 :            : #else
     170                 :            :     impl_->normal_queue.emplace();
     171                 :            : #endif
     172         [ +  + ]:         31 :   } else if (impl_->type == kLockfreeType) {
     173         [ +  - ]:         14 :     size_t max_task_size = get_max_task_count();  // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
     174         [ +  - ]:         14 :     impl_->lockfree_queue.emplace(max_task_size);
     175         [ +  - ]:         17 :   } else if (impl_->type == kPriorityType) {
     176                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
     177   [ +  -  +  - ]:         17 :     impl_->priority_droppable_queue.emplace(&MemoryResource::global_instance());
     178   [ +  -  +  - ]:         17 :     impl_->priority_protected_queue.emplace(&MemoryResource::global_instance());
     179                 :            : #else
     180                 :            :     impl_->priority_droppable_queue.emplace();
     181                 :            :     impl_->priority_protected_queue.emplace();
     182                 :            : #endif
     183                 :            :   }
     184                 :         59 : }
     185                 :            : 
     186                 :        624 : MessageLoop::~MessageLoop() {
     187                 :            :   // NOLINTBEGIN
     188                 :            :   {
     189                 :        624 :     std::lock_guard lock(impl_->alive_state->mtx);
     190                 :        624 :     impl_->alive_state->alive.store(false, std::memory_order_release);
     191                 :        624 :   }
     192                 :            : 
     193         [ +  + ]:        624 :   if VUNLIKELY (impl_->is_running.load(std::memory_order_acquire)) {
     194                 :         10 :     CLOG_W("MessageLoop is still running(%s).", impl_->name.c_str());
     195                 :          5 :     quit();
     196                 :          5 :     wait_for_quit(1000, false);
     197                 :            :   }
     198                 :            : 
     199         [ +  + ]:        624 :   if (impl_->thread.joinable()) {
     200                 :        353 :     impl_->thread.join();
     201                 :            :   }
     202                 :            : 
     203                 :            : #ifdef _WIN32
     204                 :            :   {
     205                 :            :     std::unique_lock lock(impl_->mtx);
     206                 :            :     HANDLE thread_handle = impl_->thread_handle.exchange(nullptr, std::memory_order_acq_rel);
     207                 :            : 
     208                 :            :     if (thread_handle != nullptr) {
     209                 :            :       ::CloseHandle(thread_handle);
     210                 :            :     }
     211                 :            :   }
     212                 :            : #endif
     213                 :            : 
     214                 :        624 :   std::vector<Timer*> timers_to_delete;
     215                 :            : 
     216                 :            :   {
     217                 :        624 :     std::unique_lock lock(impl_->mtx);
     218                 :            : 
     219         [ +  + ]:        627 :     for (auto iter = impl_->timer_set.begin(); iter != impl_->timer_set.end();) {
     220                 :          3 :       Timer* timer = *iter;
     221                 :          3 :       timer->clear();
     222                 :            : 
     223         [ +  + ]:          3 :       if (timer->is_once_type()) {
     224                 :          1 :         timers_to_delete.emplace_back(timer);
     225                 :          1 :         iter = impl_->timer_set.erase(iter);
     226                 :            :       } else {
     227                 :          2 :         ++iter;
     228                 :            :       }
     229                 :            :     }
     230                 :        624 :   }
     231                 :            : 
     232         [ +  + ]:        625 :   for (auto* timer : timers_to_delete) {
     233                 :          1 :     timer->~Timer();
     234                 :          1 :     MemoryPool::global_instance().deallocate(timer, sizeof(Timer), alignof(Timer));
     235                 :            :   }
     236                 :            :   // NOLINTEND
     237                 :        624 : }
     238                 :            : 
     239                 :         45 : MessageLoop::Type MessageLoop::get_type() const { return impl_->type; }
     240                 :            : 
     241                 :        412 : void MessageLoop::set_name(const std::string& name) { impl_->name = name; }
     242                 :            : 
     243                 :          2 : const std::string& MessageLoop::get_name() const { return impl_->name; }
     244                 :            : 
     245                 :          4 : MessageLoop::Strategy MessageLoop::get_strategy() const { return impl_->strategy.load(std::memory_order_acquire); }
     246                 :            : 
     247                 :         17 : void MessageLoop::set_strategy(Strategy strategy) { impl_->strategy.store(strategy, std::memory_order_release); }
     248                 :            : 
     249                 :          3 : void MessageLoop::register_begin_handler(Callback&& callback) {
     250         [ +  + ]:          3 :   if VUNLIKELY (impl_->is_running.load(std::memory_order_acquire)) {
     251   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop is running and cannot be registered(%s).", impl_->name.c_str());
     252                 :          1 :     return;
     253                 :            :   }
     254                 :            : 
     255                 :          2 :   impl_->begin_callback = std::move(callback);
     256                 :            : }
     257                 :            : 
     258                 :          3 : void MessageLoop::register_end_handler(Callback&& callback) {
     259         [ +  + ]:          3 :   if VUNLIKELY (impl_->is_running.load(std::memory_order_acquire)) {
     260   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop is running and cannot be registered(%s).", impl_->name.c_str());
     261                 :          1 :     return;
     262                 :            :   }
     263                 :            : 
     264                 :          2 :   impl_->end_callback = std::move(callback);
     265                 :            : }
     266                 :            : 
     267                 :          2 : void MessageLoop::register_idle_handler(Callback&& callback) {
     268         [ +  + ]:          2 :   if VUNLIKELY (impl_->is_running.load(std::memory_order_acquire)) {
     269   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop is running and cannot be registered(%s).", impl_->name.c_str());
     270                 :          1 :     return;
     271                 :            :   }
     272                 :            : 
     273                 :          1 :   impl_->idle_callback = std::move(callback);
     274                 :            : }
     275                 :            : 
     276                 :          3 : bool MessageLoop::run() {
     277                 :          3 :   bool expected = false;
     278                 :            : 
     279         [ -  + ]:          3 :   if VUNLIKELY (!impl_->is_running.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
     280                 :            :                                                            std::memory_order_acquire)) {
     281                 :            :     CLOG_W("MessageLoop has already run(%s).", impl_->name.c_str());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     282                 :            :     return false;                                                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     283                 :            :   }
     284                 :            : 
     285                 :            : #ifdef _WIN32
     286                 :            :   {
     287                 :            :     std::unique_lock lock(impl_->mtx);
     288                 :            :     HANDLE thread_handle = impl_->thread_handle.exchange(nullptr, std::memory_order_acq_rel);
     289                 :            : 
     290                 :            :     if (thread_handle != nullptr) {
     291                 :            :       ::CloseHandle(thread_handle);
     292                 :            :     }
     293                 :            :   }
     294                 :            : #endif
     295                 :            : 
     296   [ -  +  -  -  :          3 :   if (impl_->type == kLockfreeType && impl_->lockfree_needs_reset.exchange(false, std::memory_order_acq_rel)) {
                   -  + ]
     297                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     298                 :            :     std::lock_guard lock(impl_->mtx);
     299                 :            :     const auto max_task_count = get_max_task_count();
     300                 :            :     impl_->lockfree_queue.emplace(max_task_count);
     301                 :            :     impl_->lockfree_task_count.store(0U, std::memory_order_release);
     302                 :            :   }
     303                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     304                 :            : 
     305                 :          3 :   impl_->quit_flag.store(false, std::memory_order_release);
     306                 :          3 :   impl_->force_quit_flag.store(false, std::memory_order_release);
     307                 :            : 
     308         [ +  - ]:          3 :   do_consume();
     309                 :            : 
     310                 :          3 :   return true;
     311                 :            : }
     312                 :            : 
     313                 :        357 : bool MessageLoop::async_run() {
     314                 :        357 :   bool expected = false;
     315                 :            : 
     316         [ +  + ]:        357 :   if VUNLIKELY (!impl_->is_running.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
     317                 :            :                                                            std::memory_order_acquire)) {
     318   [ +  -  +  - ]:          4 :     CLOG_W("MessageLoop has already run(%s).", impl_->name.c_str());
     319                 :          2 :     return false;
     320                 :            :   }
     321                 :            : 
     322         [ +  + ]:        355 :   if (impl_->thread.joinable()) {
     323         [ +  - ]:          2 :     impl_->thread.join();
     324                 :            :   }
     325                 :            : 
     326                 :            : #ifdef _WIN32
     327                 :            :   {
     328                 :            :     std::unique_lock lock(impl_->mtx);
     329                 :            :     HANDLE thread_handle = impl_->thread_handle.exchange(nullptr, std::memory_order_acq_rel);
     330                 :            : 
     331                 :            :     if (thread_handle != nullptr) {
     332                 :            :       ::CloseHandle(thread_handle);
     333                 :            :     }
     334                 :            :   }
     335                 :            : #endif
     336                 :            : 
     337   [ +  +  +  +  :        355 :   if (impl_->type == kLockfreeType && impl_->lockfree_needs_reset.exchange(false, std::memory_order_acq_rel)) {
                   +  + ]
     338         [ +  - ]:          1 :     std::lock_guard lock(impl_->mtx);
     339         [ +  - ]:          1 :     const auto max_task_count = get_max_task_count();
     340         [ +  - ]:          1 :     impl_->lockfree_queue.emplace(max_task_count);
     341                 :          1 :     impl_->lockfree_task_count.store(0U, std::memory_order_release);
     342                 :          1 :   }
     343                 :            : 
     344                 :        355 :   impl_->quit_flag.store(false, std::memory_order_release);
     345                 :        355 :   impl_->force_quit_flag.store(false, std::memory_order_release);
     346                 :            : 
     347         [ +  - ]:        710 :   impl_->thread = std::thread([this]() { do_consume(); });
     348                 :            : 
     349         [ +  - ]:        355 :   if (!impl_->name.empty()) {
     350                 :        355 :     Utils::set_thread_name(impl_->name, &impl_->thread);
     351                 :            :   }
     352                 :            : 
     353                 :        355 :   return true;
     354                 :            : }
     355                 :            : 
     356                 :          1 : bool MessageLoop::spin() { return run(); }
     357                 :            : 
     358                 :          8 : bool MessageLoop::spin_once(bool block) {
     359   [ +  -  +  -  :          8 :   if VUNLIKELY (!is_in_same_thread() && impl_->thread_id.load(std::memory_order_acquire) != std::thread::id()) {
             +  +  +  + ]
     360   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop spin_once called from different thread than run/async_run (%s).", impl_->name.c_str());
     361                 :          1 :     return false;
     362                 :            :   }
     363                 :            : 
     364         [ +  + ]:          7 :   if (impl_->type == kNormalType) {
     365                 :          5 :     return process_normal_task(block);
     366         [ -  + ]:          2 :   } else if (impl_->type == kLockfreeType) {
     367                 :            :     return process_lockfree_task(block);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     368         [ +  - ]:          2 :   } else if (impl_->type == kPriorityType) {
     369                 :          2 :     return process_priority_task(block);
     370                 :            :   } else {
     371                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     372                 :            :   }
     373                 :            : }
     374                 :            : 
     375                 :        661 : bool MessageLoop::quit(bool force) {
     376   [ +  +  +  +  :        661 :   if VUNLIKELY (!force && (!impl_->is_running.load(std::memory_order_acquire) ||
          +  +  +  +  +  
                      + ]
     377                 :            :                            impl_->quit_flag.load(std::memory_order_acquire))) {
     378                 :          6 :     return false;
     379                 :            :   }
     380                 :            : 
     381                 :            :   {
     382         [ +  - ]:        655 :     std::lock_guard lock(impl_->mtx);
     383                 :        655 :     impl_->quit_flag.store(true, std::memory_order_release);
     384                 :        655 :     impl_->force_quit_flag.store(force, std::memory_order_release);
     385                 :        655 :   }
     386                 :            : 
     387         [ +  + ]:        655 :   if (impl_->type == kLockfreeType) {
     388         [ +  - ]:          9 :     std::unique_lock lock(impl_->mtx);
     389                 :         27 :     impl_->cv.wait(lock, [this] { return impl_->lockfree_producer_count.load(std::memory_order_acquire) == 0U; });
     390                 :          9 :   }
     391                 :            : 
     392                 :        655 :   drop_pending_tasks();
     393                 :            : 
     394         [ +  + ]:        655 :   if (impl_->type == kLockfreeType) {
     395                 :          9 :     impl_->lockfree_queue->notify_to_quit();
     396                 :          9 :     impl_->lockfree_needs_reset.store(true, std::memory_order_release);
     397                 :            :   }
     398                 :            : 
     399                 :        655 :   impl_->cv.notify_all();
     400                 :            : 
     401                 :        655 :   return true;
     402                 :            : }
     403                 :            : 
     404                 :        654 : bool MessageLoop::wait_for_quit(int ms, bool check) {
     405         [ +  - ]:        654 :   std::unique_lock lock(impl_->mtx);
     406                 :            : 
     407                 :            : #ifdef _WIN32
     408                 :            :   HANDLE thread_handle = impl_->thread_handle.load(std::memory_order_acquire);
     409                 :            : 
     410                 :            :   if (thread_handle != nullptr) {
     411                 :            :     DWORD thread_status = STILL_ACTIVE;
     412                 :            :     if (::GetExitCodeThread(thread_handle, &thread_status) && thread_status != STILL_ACTIVE) {
     413                 :            :       impl_->is_running.store(false, std::memory_order_release);
     414                 :            :       impl_->is_busy.store(false, std::memory_order_release);
     415                 :            :       return true;
     416                 :            :     }
     417                 :            :   }
     418                 :            : #endif
     419                 :            : 
     420   [ +  +  +  -  :        654 :   if VUNLIKELY (check && is_in_same_thread()) {
             +  +  +  + ]
     421   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop wait_for_quit in work thread(%s).", impl_->name.c_str());
     422                 :          1 :     return false;
     423                 :            :   }
     424                 :            : 
     425                 :        997 :   auto predicate = [this]() -> bool { return !impl_->is_running.load(std::memory_order_acquire); };
     426                 :            : 
     427         [ +  + ]:        653 :   if (ms == Timer::kInfinite) {
     428                 :        491 :     impl_->cv.wait(lock, std::move(predicate));
     429                 :        491 :     return true;
     430                 :            :   }
     431                 :            : 
     432                 :        162 :   return impl_->cv.wait_for(lock, std::chrono::milliseconds(ms), std::move(predicate));
     433                 :        654 : }
     434                 :            : 
     435                 :       3100 : bool MessageLoop::post_task(Callback&& callback) { return push_task(std::move(callback), kNoPriority); }
     436                 :            : 
     437                 :         24 : TaskHandle MessageLoop::post_task_handle(Callback&& callback, const PostTaskOptions& options) {
     438         [ +  - ]:         24 :   auto handle = TaskHandle::make_task_handle(options.cancellation_token);
     439         [ +  - ]:         24 :   TaskHandle::mark_task_queued(handle);
     440                 :            : 
     441         [ +  + ]:         24 :   if (handle.state() == TaskExecutionState::kCancelled) {
     442                 :          1 :     return handle;
     443                 :            :   }
     444                 :            : 
     445   [ +  +  +  -  :         23 :   if VUNLIKELY (impl_->type == kLockfreeType && options.drop_policy == TaskDropPolicy::kProtected) {
                   +  + ]
     446   [ +  -  +  - ]:          2 :     CLOG_W("MessageLoop: TaskDropPolicy::kProtected is ignored by lock-free queues (%s).", impl_->name.c_str());
     447                 :            :   }
     448                 :            : 
     449         [ +  - ]:         23 :   auto tracked = TaskHandle::make_tracked_task(handle, std::move(callback));
     450                 :         23 :   const bool droppable = options.drop_policy == TaskDropPolicy::kDroppable;
     451                 :            : 
     452   [ +  -  +  +  :         23 :   if VUNLIKELY (!push_task(std::move(tracked), kNoPriority, droppable, options.overflow_policy, &handle) &&
             -  +  -  + ]
     453                 :            :                 !handle.is_done()) {
     454                 :            :     TaskHandle::mark_task_rejected(handle);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     455                 :            :   }
     456                 :            : 
     457                 :         23 :   return handle;
     458                 :         23 : }
     459                 :            : 
     460                 :         14 : bool MessageLoop::post_task_with_priority(Callback&& callback, uint16_t priority) {
     461         [ +  + ]:         14 :   if VUNLIKELY (priority == kNoPriority) {
     462   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop: Task priority cannot be zero (%s).", impl_->name.c_str());
     463                 :          1 :     return false;
     464                 :            :   }
     465                 :            : 
     466         [ +  + ]:         13 :   if VUNLIKELY (impl_->type != kPriorityType) {
     467   [ +  -  +  - ]:          6 :     CLOG_E("MessageLoop: Task priority is not supported (%s).", impl_->name.c_str());
     468                 :          3 :     return false;
     469                 :            :   }
     470                 :            : 
     471                 :         10 :   return push_task(std::move(callback), priority);
     472                 :            : }
     473                 :            : 
     474                 :          8 : TaskHandle MessageLoop::post_task_with_priority_handle(Callback&& callback, uint16_t priority,
     475                 :            :                                                        const PostTaskOptions& options) {
     476         [ +  - ]:          8 :   auto handle = TaskHandle::make_task_handle(options.cancellation_token);
     477         [ +  - ]:          8 :   TaskHandle::mark_task_queued(handle);
     478                 :            : 
     479         [ -  + ]:          8 :   if (handle.state() == TaskExecutionState::kCancelled) {
     480                 :            :     return handle;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     481                 :            :   }
     482                 :            : 
     483         [ +  + ]:          8 :   if VUNLIKELY (priority == kNoPriority) {
     484   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop: Task priority cannot be zero (%s).", impl_->name.c_str());
     485         [ +  - ]:          1 :     TaskHandle::mark_task_rejected(handle);
     486                 :          1 :     return handle;
     487                 :            :   }
     488                 :            : 
     489         [ +  + ]:          7 :   if VUNLIKELY (impl_->type != kPriorityType) {
     490   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop: Task priority is not supported (%s).", impl_->name.c_str());
     491         [ +  - ]:          1 :     TaskHandle::mark_task_rejected(handle);
     492                 :          1 :     return handle;
     493                 :            :   }
     494                 :            : 
     495         [ +  - ]:          6 :   auto tracked = TaskHandle::make_tracked_task(handle, std::move(callback));
     496                 :          6 :   const bool droppable = options.drop_policy == TaskDropPolicy::kDroppable;
     497                 :            : 
     498   [ +  -  +  +  :          6 :   if VUNLIKELY (!push_task(std::move(tracked), priority, droppable, options.overflow_policy, &handle) &&
             -  +  -  + ]
     499                 :            :                 !handle.is_done()) {
     500                 :            :     TaskHandle::mark_task_rejected(handle);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     501                 :            :   }
     502                 :            : 
     503                 :          6 :   return handle;
     504                 :          6 : }
     505                 :            : 
     506                 :       3327 : bool MessageLoop::wakeup() {
     507         [ +  + ]:       3327 :   if VUNLIKELY (!impl_->is_running.load(std::memory_order_acquire)) {
     508                 :        145 :     return false;
     509                 :            :   }
     510                 :            : 
     511                 :       3181 :   bool expected = false;
     512                 :            : 
     513         [ +  + ]:       3181 :   if (!impl_->wakeup_pending.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
     514                 :            :                                                      std::memory_order_acquire)) {
     515                 :       2378 :     return true;
     516                 :            :   }
     517                 :            : 
     518                 :            :   // Pair the first pending wakeup with the wait mutex so a concurrent waiter cannot miss the notify.
     519                 :            :   {
     520         [ +  - ]:        804 :     std::lock_guard lock(impl_->mtx);
     521                 :        804 :   }
     522                 :        804 :   impl_->cv.notify_all();
     523                 :            : 
     524                 :        804 :   return true;
     525                 :            : }
     526                 :            : 
     527                 :        158 : void MessageLoop::reset_lockfree_capacity() {
     528         [ +  + ]:        158 :   if (impl_->type != kLockfreeType) {
     529                 :        158 :     return;
     530                 :            :   }
     531                 :            : 
     532         [ +  - ]:          1 :   std::lock_guard lock(impl_->mtx);
     533                 :            : 
     534         [ +  - ]:          1 :   if VUNLIKELY (impl_->is_running.load(std::memory_order_acquire)) {
     535   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop: reset_lockfree_capacity called while running (%s).", impl_->name.c_str());
     536                 :          1 :     return;
     537                 :            :   }
     538                 :            : 
     539         [ #  # ]:          0 :   size_t max_task_size = get_max_task_count();
     540         [ #  # ]:          0 :   impl_->lockfree_queue.emplace(max_task_size);
     541                 :          0 :   impl_->lockfree_task_count.store(0U, std::memory_order_release);
     542                 :          0 :   impl_->lockfree_needs_reset.store(false, std::memory_order_release);
     543         [ -  + ]:          1 : }
     544                 :            : 
     545                 :        219 : bool MessageLoop::is_running() const { return impl_->is_running.load(std::memory_order_acquire); }
     546                 :            : 
     547                 :       1314 : bool MessageLoop::is_ready_to_quit() const { return impl_->quit_flag.load(std::memory_order_acquire); }
     548                 :            : 
     549                 :        138 : bool MessageLoop::is_busy() const { return impl_->is_busy.load(std::memory_order_acquire); }
     550                 :            : 
     551                 :         17 : size_t MessageLoop::get_task_count() const {
     552         [ +  - ]:         17 :   std::lock_guard lock(impl_->mtx);
     553                 :            : 
     554         [ +  + ]:         17 :   if (impl_->type == kNormalType) {
     555                 :          9 :     return impl_->normal_queue->size();
     556         [ +  + ]:          8 :   } else if (impl_->type == kLockfreeType) {
     557                 :          6 :     return impl_->lockfree_task_count.load(std::memory_order_acquire);
     558         [ +  - ]:          5 :   } else if (impl_->type == kPriorityType) {
     559                 :          5 :     return impl_->priority_droppable_queue->size() + impl_->priority_protected_queue->size();
     560                 :            :   }
     561                 :            : 
     562                 :            :   return 0;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     563                 :         17 : }
     564                 :            : 
     565                 :        571 : bool MessageLoop::wait_for_idle(int ms, bool check) {
     566         [ +  - ]:        571 :   std::unique_lock lock(impl_->mtx);
     567                 :            : 
     568                 :            : #ifdef _WIN32
     569                 :            :   HANDLE thread_handle = impl_->thread_handle.load(std::memory_order_acquire);
     570                 :            : 
     571                 :            :   if (thread_handle != nullptr) {
     572                 :            :     DWORD thread_status = STILL_ACTIVE;
     573                 :            :     if (::GetExitCodeThread(thread_handle, &thread_status) && thread_status != STILL_ACTIVE) {
     574                 :            :       impl_->is_running.store(false, std::memory_order_release);
     575                 :            :       impl_->is_busy.store(false, std::memory_order_release);
     576                 :            :       return true;
     577                 :            :     }
     578                 :            :   }
     579                 :            : #endif
     580                 :            : 
     581   [ +  +  +  -  :        571 :   if VUNLIKELY (check && is_in_same_thread()) {
             +  +  +  + ]
     582   [ +  -  +  - ]:          2 :     CLOG_E("MessageLoop wait_for_idle in work thread(%s).", impl_->name.c_str());
     583                 :          1 :     return false;
     584                 :            :   }
     585                 :            : 
     586                 :       2513 :   auto predicate = [this]() -> bool {
     587         [ +  + ]:        667 :     if (impl_->type == kNormalType) {
     588                 :        647 :       return !impl_->is_running.load(std::memory_order_acquire)
     589         [ +  + ]:       1142 :                  ? impl_->normal_queue->empty()
     590   [ +  +  +  + ]:       1142 :                  : !impl_->is_busy.load(std::memory_order_acquire) && impl_->normal_queue->empty();
     591         [ +  + ]:         20 :     } else if (impl_->type == kLockfreeType) {
     592                 :          9 :       const bool empty = impl_->lockfree_task_count.load(std::memory_order_acquire) == 0U;
     593                 :          9 :       return !impl_->is_running.load(std::memory_order_acquire)
     594         [ +  + ]:         16 :                  ? empty
     595   [ +  -  +  + ]:         16 :                  : !impl_->is_busy.load(std::memory_order_acquire) && empty;
     596         [ +  - ]:         11 :     } else if (impl_->type == kPriorityType) {
     597   [ +  +  +  - ]:         11 :       const bool empty = impl_->priority_droppable_queue->empty() && impl_->priority_protected_queue->empty();
     598                 :         11 :       return !impl_->is_running.load(std::memory_order_acquire)
     599         [ +  + ]:         20 :                  ? empty
     600   [ +  +  +  - ]:         20 :                  : !impl_->is_busy.load(std::memory_order_acquire) && empty;
     601                 :            :     }
     602                 :            : 
     603                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     604                 :        570 :   };
     605                 :            : 
     606         [ +  + ]:        570 :   if (ms == Timer::kInfinite) {
     607                 :        356 :     impl_->cv.wait(lock, std::move(predicate));
     608                 :        356 :     return true;
     609                 :            :   }
     610                 :            : 
     611                 :        214 :   return impl_->cv.wait_for(lock, std::chrono::milliseconds(ms), std::move(predicate));
     612                 :        571 : }
     613                 :            : 
     614                 :       2952 : size_t MessageLoop::get_max_task_count() const { return kMaxTaskSize; }
     615                 :            : 
     616                 :        112 : size_t MessageLoop::get_max_timer_count() const { return kMaxTimerSize; }
     617                 :            : 
     618                 :       3479 : uint32_t MessageLoop::get_max_elapsed_time() const { return kMaxElapsedTime; }
     619                 :            : 
     620                 :       1249 : bool MessageLoop::is_in_same_thread() const {
     621                 :       1249 :   return impl_->thread_id.load(std::memory_order_acquire) == std::this_thread::get_id();
     622                 :            : }
     623                 :            : 
     624                 :         30 : std::shared_ptr<MessageLoop::AliveState> MessageLoop::get_alive_state() const { return impl_->alive_state; }
     625                 :            : 
     626                 :        357 : void MessageLoop::on_begin() {
     627         [ +  + ]:        357 :   if (impl_->begin_callback) {
     628                 :          2 :     impl_->begin_callback();
     629                 :            :   }
     630                 :        357 : }
     631                 :            : 
     632                 :        357 : void MessageLoop::on_end() {
     633         [ +  + ]:        357 :   if (impl_->end_callback) {
     634                 :          2 :     impl_->end_callback();
     635                 :            :   }
     636                 :        357 : }
     637                 :            : 
     638                 :       3244 : void MessageLoop::on_idle() {
     639         [ +  + ]:       3244 :   if (impl_->idle_callback) {
     640                 :          2 :     impl_->idle_callback();
     641                 :            :   }
     642                 :       3244 : }
     643                 :            : 
     644                 :       4005 : void MessageLoop::on_task_changed(Callback&& callback, uint32_t start_time) {
     645   [ +  +  +  -  :       4005 :   if (start_time > 0 && get_max_elapsed_time() > 0) {
                   +  + ]
     646                 :            :     uint32_t elapsed_time =
     647                 :        596 :         get_current_time<std::chrono::steady_clock, std::chrono::milliseconds, uint32_t>() - start_time;
     648                 :            : 
     649         [ +  + ]:        596 :     if VUNLIKELY (elapsed_time > get_max_elapsed_time()) {
     650                 :          1 :       on_task_timeout(std::move(callback), elapsed_time);
     651                 :            :     } else {
     652                 :        595 :       callback();
     653                 :            :     }
     654                 :            :   } else {
     655                 :       3409 :     callback();
     656                 :            :   }
     657                 :       4005 : }
     658                 :            : 
     659                 :            : // LCOV_EXCL_START GCOVR_EXCL_START
     660                 :            : void MessageLoop::on_task_timeout(Callback&& callback, uint32_t elapsed_time) {
     661                 :            :   (void)callback;
     662                 :            :   CLOG_W("MessageLoop: Task timed out after %ums (%s).", elapsed_time, impl_->name.c_str());
     663                 :            : }
     664                 :            : // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     665                 :            : 
     666                 :       3450 : uint64_t MessageLoop::get_current_nano_time() {
     667                 :       3450 :   return get_current_time<std::chrono::steady_clock, std::chrono::nanoseconds, uint64_t>();
     668                 :            : }
     669                 :            : 
     670                 :        113 : bool MessageLoop::add_timer(Timer* timer) {
     671         [ +  - ]:        113 :   std::lock_guard lock(impl_->mtx);
     672                 :            : 
     673         [ -  + ]:        113 :   if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     674                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     675                 :            :   }
     676                 :            : 
     677   [ +  -  +  + ]:        113 :   if VUNLIKELY (impl_->timer_set.size() >= get_max_timer_count()) {
     678   [ +  -  +  - ]:          2 :     CLOG_W("MessageLoop: Timer is full (%s).", impl_->name.c_str());
     679                 :          1 :     return false;
     680                 :            :   }
     681                 :            : 
     682         [ +  - ]:        112 :   return impl_->timer_set.emplace(timer).second;
     683                 :        113 : }
     684                 :            : 
     685                 :         99 : bool MessageLoop::remove_timer(Timer* timer) {
     686         [ +  - ]:         99 :   std::lock_guard lock(impl_->mtx);
     687                 :            : 
     688         [ +  - ]:        198 :   return impl_->timer_set.erase(timer) != 0;
     689                 :         99 : }
     690                 :            : 
     691                 :          5 : bool MessageLoop::drop_one_normal_task() {
     692         [ +  + ]:          7 :   for (auto iter = impl_->normal_queue->begin(); iter != impl_->normal_queue->end(); ++iter) {
     693         [ +  + ]:          5 :     if (std::get<1>(*iter)) {
     694         [ +  - ]:          3 :       impl_->normal_queue->erase(iter);
     695                 :            : 
     696                 :          3 :       return true;
     697                 :            :     }
     698                 :            :   }
     699                 :            : 
     700                 :          2 :   return false;
     701                 :            : }
     702                 :            : 
     703                 :          2 : bool MessageLoop::drop_one_lockfree_task(bool keep_reserved) {
     704                 :          2 :   Impl::LockfreeTaskTuple task;
     705                 :            : 
     706         [ -  + ]:          2 :   if (!impl_->lockfree_queue->try_pop<Impl::LockfreeQueue::kNoBehavior>(task)) {
     707                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     708                 :            :   }
     709                 :            : 
     710         [ -  + ]:          2 :   if (!keep_reserved) {
     711                 :            :     release_lockfree_task();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     712                 :            :   }
     713                 :            : 
     714                 :          2 :   return true;
     715                 :          2 : }
     716                 :            : 
     717                 :          3 : bool MessageLoop::drop_one_priority_task() {
     718         [ +  + ]:          3 :   if (impl_->priority_droppable_queue->empty()) {
     719                 :          1 :     return false;
     720                 :            :   }
     721                 :            : 
     722                 :          2 :   impl_->priority_droppable_queue->pop();
     723                 :            : 
     724                 :          2 :   return true;
     725                 :            : }
     726                 :            : 
     727                 :        444 : bool MessageLoop::reserve_lockfree_task() {
     728                 :        444 :   auto count = impl_->lockfree_task_count.load(std::memory_order_acquire);
     729         [ +  - ]:        444 :   const auto max_count = get_max_task_count();
     730                 :            : 
     731         [ +  + ]:        465 :   while (count < max_count) {
     732         [ +  + ]:        906 :     if (impl_->lockfree_task_count.compare_exchange_weak(count, count + 1U, std::memory_order_acq_rel,
     733                 :            :                                                          std::memory_order_acquire)) {
     734                 :        432 :       return true;
     735                 :            :     }
     736                 :            :   }
     737                 :            : 
     738                 :         12 :   return false;
     739                 :            : }
     740                 :            : 
     741                 :        429 : void MessageLoop::release_lockfree_task() { impl_->lockfree_task_count.fetch_sub(1U, std::memory_order_acq_rel); }
     742                 :            : 
     743                 :       3139 : bool MessageLoop::push_task(Callback&& callback, uint16_t priority, bool droppable, TaskOverflowPolicy overflow_policy,
     744                 :            :                             const TaskHandle* submit_handle) {
     745                 :       3778 :   auto is_cancelled = [submit_handle]() -> bool {
     746   [ +  +  +  + ]:       3690 :     return submit_handle != nullptr && submit_handle->state() == TaskExecutionState::kCancelled;
     747                 :       3139 :   };
     748                 :            : 
     749                 :         14 :   auto reject = [submit_handle]() -> bool {
     750   [ +  +  +  -  :          8 :     if (submit_handle != nullptr && !submit_handle->is_done()) {
                   +  + ]
     751                 :          3 :       TaskHandle::mark_task_rejected(*submit_handle);
     752                 :            :     }
     753                 :            : 
     754                 :          8 :     return false;
     755                 :       3139 :   };
     756                 :            : 
     757         [ +  + ]:       3139 :   if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     758         [ +  - ]:          4 :     return reject();
     759                 :            :   }
     760                 :            : 
     761                 :       3135 :   bool is_full = false;
     762                 :       3135 :   int retry_cnt = 0;
     763                 :            : 
     764         [ +  + ]:       3135 :   if (impl_->type == kNormalType) {
     765         [ +  + ]:       2710 :     do {
     766                 :            :       {
     767         [ +  - ]:       2717 :         std::lock_guard lock(impl_->mtx);
     768                 :            : 
     769         [ -  + ]:       2717 :         if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     770                 :            :           return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     771                 :            :         }
     772                 :            : 
     773         [ +  + ]:       2717 :         if VUNLIKELY (is_cancelled()) {
     774                 :          1 :           return false;
     775                 :            :         }
     776                 :            : 
     777         [ +  - ]:       2716 :         is_full = impl_->normal_queue->size() >= get_max_task_count();
     778                 :            : 
     779         [ +  + ]:       2716 :         if VLIKELY (!is_full) {
     780         [ +  - ]:       2671 :           push_normal_task(std::move(callback), droppable);
     781         [ +  + ]:         45 :         } else if (overflow_policy == TaskOverflowPolicy::kReject) {
     782         [ +  - ]:          1 :           return reject();
     783   [ +  +  +  -  :         44 :         } else if (impl_->strategy.load(std::memory_order_acquire) == kPopStrategy &&
                   +  + ]
     784                 :            :                    overflow_policy != TaskOverflowPolicy::kBlock) {
     785   [ +  -  +  + ]:          4 :           if (!drop_one_normal_task()) {
     786         [ +  - ]:          2 :             return reject();
     787                 :            :           }
     788                 :            : 
     789         [ +  - ]:          2 :           push_normal_task(std::move(callback), droppable);
     790                 :            : 
     791                 :          2 :           is_full = false;
     792                 :            : 
     793                 :          2 :           break;
     794                 :            :         }
     795      [ +  +  + ]:       2717 :       }
     796                 :            : 
     797         [ +  + ]:       2711 :       if VUNLIKELY (is_full) {
     798   [ +  -  +  +  :         40 :         if (impl_->strategy.load(std::memory_order_acquire) == kOptimizationStrategy &&
                   +  + ]
     799                 :            :             overflow_policy != TaskOverflowPolicy::kBlock) {
     800         [ +  + ]:         11 :           if (++retry_cnt > 10) {
     801                 :            :             {
     802         [ +  - ]:          1 :               std::lock_guard lock(impl_->mtx);
     803                 :            : 
     804         [ -  + ]:          1 :               if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     805                 :            :                 return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     806                 :            :               }
     807                 :            : 
     808         [ -  + ]:          1 :               if VUNLIKELY (is_cancelled()) {
     809                 :            :                 return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     810                 :            :               }
     811                 :            : 
     812   [ +  -  -  + ]:          1 :               if (!drop_one_normal_task()) {
     813                 :            :                 return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     814                 :            :               }
     815                 :            : 
     816         [ +  - ]:          1 :               push_normal_task(std::move(callback), droppable);
     817                 :            : 
     818                 :          1 :               is_full = false;
     819         [ +  - ]:          1 :             }
     820                 :            : 
     821   [ +  -  +  - ]:          2 :             CLOG_W("MessageLoop: Task is full, removed top data (%s).", impl_->name.c_str());
     822                 :          1 :             break;
     823                 :            :           }
     824                 :            :         }
     825                 :            : 
     826         [ -  + ]:         39 :         if VUNLIKELY (is_cancelled()) {
     827                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     828                 :            :         }
     829                 :            : 
     830         [ +  - ]:         39 :         std::this_thread::sleep_for(std::chrono::milliseconds(1));
     831                 :            :       }
     832                 :            :     } while (is_full);
     833                 :            : 
     834         [ +  - ]:       2674 :     wakeup();
     835                 :            : 
     836                 :       2674 :     return !is_full;
     837                 :            : 
     838         [ +  + ]:        457 :   } else if (impl_->type == kLockfreeType) {
     839                 :            :     struct ProducerGuard final {
     840                 :        434 :       explicit ProducerGuard(Impl& impl) noexcept : impl_ref(impl) {
     841                 :        434 :         impl_ref.lockfree_producer_count.fetch_add(1U, std::memory_order_acq_rel);
     842                 :        434 :       }
     843                 :            : 
     844                 :        434 :       ~ProducerGuard() {
     845         [ +  + ]:        868 :         if (impl_ref.lockfree_producer_count.fetch_sub(1U, std::memory_order_acq_rel) == 1U) {
     846                 :        273 :           std::lock_guard lock(impl_ref.mtx);
     847                 :        273 :           impl_ref.cv.notify_all();
     848                 :        273 :         }
     849                 :        434 :       }
     850                 :            : 
     851                 :            :       Impl& impl_ref;
     852                 :        434 :     } producer_guard(*impl_);
     853                 :            : 
     854                 :       1302 :     auto push_reserved_lockfree_task = [this, &is_cancelled, &reject, &callback]() -> bool {
     855         [ -  + ]:        434 :       if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     856                 :            :         release_lockfree_task();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     857                 :            :         return reject();          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     858                 :            :       }
     859                 :            : 
     860         [ -  + ]:        434 :       if VUNLIKELY (is_cancelled()) {
     861                 :            :         release_lockfree_task();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     862                 :            :         return false;             // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     863                 :            :       }
     864                 :            : 
     865         [ -  + ]:        434 :       if VUNLIKELY (!push_lockfree_task(std::move(callback))) {
     866                 :            :         release_lockfree_task();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     867                 :            :         return reject();          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     868                 :            :       }
     869                 :            : 
     870                 :        434 :       return true;
     871                 :        434 :     };
     872                 :            : 
     873         [ +  - ]:         10 :     do {
     874         [ -  + ]:        444 :       if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     875                 :            :         return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     876                 :            :       }
     877                 :            : 
     878         [ -  + ]:        444 :       if VUNLIKELY (is_cancelled()) {
     879                 :            :         return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     880                 :            :       }
     881                 :            : 
     882         [ +  - ]:        444 :       is_full = !reserve_lockfree_task();
     883                 :            : 
     884         [ +  + ]:        444 :       if VLIKELY (!is_full) {
     885   [ +  -  -  + ]:        432 :         if VUNLIKELY (!push_reserved_lockfree_task()) {
     886                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     887                 :            :         }
     888                 :            : 
     889                 :        432 :         break;
     890                 :            :       }
     891                 :            : 
     892         [ -  + ]:         12 :       if (overflow_policy == TaskOverflowPolicy::kReject) {
     893                 :            :         return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     894                 :            :       }
     895                 :            : 
     896   [ +  +  +  -  :         12 :       if (impl_->strategy.load(std::memory_order_acquire) == kPopStrategy &&
                   +  + ]
     897                 :            :           overflow_policy != TaskOverflowPolicy::kBlock) {
     898   [ +  -  -  + ]:          1 :         if (!drop_one_lockfree_task(true)) {
     899                 :            :           return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     900                 :            :         }
     901                 :            : 
     902   [ +  -  -  + ]:          1 :         if VUNLIKELY (!push_reserved_lockfree_task()) {
     903                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     904                 :            :         }
     905                 :            : 
     906                 :          1 :         is_full = false;
     907                 :            : 
     908                 :          1 :         break;
     909                 :            :       }
     910                 :            : 
     911         [ +  - ]:         11 :       if VUNLIKELY (is_full) {
     912   [ +  -  +  -  :         11 :         if (impl_->strategy.load(std::memory_order_acquire) == kOptimizationStrategy &&
                   +  - ]
     913                 :            :             overflow_policy != TaskOverflowPolicy::kBlock) {
     914         [ +  + ]:         11 :           if (++retry_cnt > 10) {
     915         [ -  + ]:          1 :             if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     916                 :            :               return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     917                 :            :             }
     918                 :            : 
     919         [ -  + ]:          1 :             if VUNLIKELY (is_cancelled()) {
     920                 :            :               return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     921                 :            :             }
     922                 :            : 
     923   [ +  -  -  + ]:          1 :             if (!drop_one_lockfree_task(true)) {
     924                 :            :               return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     925                 :            :             }
     926                 :            : 
     927   [ +  -  -  + ]:          1 :             if VUNLIKELY (!push_reserved_lockfree_task()) {
     928                 :            :               return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     929                 :            :             }
     930                 :            : 
     931                 :          1 :             is_full = false;
     932   [ +  -  +  - ]:          2 :             CLOG_W("MessageLoop: Task is full, removed top data (%s).", impl_->name.c_str());
     933                 :          1 :             break;
     934                 :            :           }
     935                 :            :         }
     936                 :            : 
     937         [ -  + ]:         10 :         if VUNLIKELY (is_cancelled()) {
     938                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     939                 :            :         }
     940                 :            : 
     941         [ +  - ]:         10 :         std::this_thread::sleep_for(std::chrono::milliseconds(1));
     942                 :            :       }
     943                 :            :     } while (is_full);
     944                 :            : 
     945         [ +  - ]:        434 :     wakeup();
     946                 :            : 
     947                 :        434 :     return !is_full;
     948         [ +  - ]:        457 :   } else if (impl_->type == kPriorityType) {
     949         [ +  + ]:         30 :     do {
     950                 :            :       {
     951         [ +  - ]:         33 :         std::lock_guard lock(impl_->mtx);
     952                 :            : 
     953         [ -  + ]:         33 :         if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     954                 :            :           return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     955                 :            :         }
     956                 :            : 
     957         [ -  + ]:         33 :         if VUNLIKELY (is_cancelled()) {
     958                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     959                 :            :         }
     960                 :            : 
     961                 :         33 :         is_full =
     962         [ +  - ]:         33 :             impl_->priority_droppable_queue->size() + impl_->priority_protected_queue->size() >= get_max_task_count();
     963                 :            : 
     964         [ +  + ]:         33 :         if VLIKELY (!is_full) {
     965         [ +  - ]:         20 :           push_priority_task(std::move(callback), priority, droppable);
     966         [ -  + ]:         13 :         } else if (overflow_policy == TaskOverflowPolicy::kReject) {
     967                 :            :           return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     968   [ +  +  +  -  :         13 :         } else if (impl_->strategy.load(std::memory_order_acquire) == kPopStrategy &&
                   +  + ]
     969                 :            :                    overflow_policy != TaskOverflowPolicy::kBlock) {
     970   [ +  -  +  + ]:          2 :           if (!drop_one_priority_task()) {
     971         [ +  - ]:          1 :             return reject();
     972                 :            :           }
     973                 :            : 
     974         [ +  - ]:          1 :           push_priority_task(std::move(callback), priority, droppable);
     975                 :            : 
     976                 :          1 :           is_full = false;
     977                 :            : 
     978                 :          1 :           break;
     979                 :            :         }
     980      [ +  +  + ]:         33 :       }
     981                 :            : 
     982         [ +  + ]:         31 :       if VUNLIKELY (is_full) {
     983   [ +  -  +  -  :         11 :         if (impl_->strategy.load(std::memory_order_acquire) == kOptimizationStrategy &&
                   +  - ]
     984                 :            :             overflow_policy != TaskOverflowPolicy::kBlock) {
     985         [ +  + ]:         11 :           if (++retry_cnt > 10) {
     986                 :            :             {
     987         [ +  - ]:          1 :               std::lock_guard lock(impl_->mtx);
     988                 :            : 
     989         [ -  + ]:          1 :               if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
     990                 :            :                 return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     991                 :            :               }
     992                 :            : 
     993         [ -  + ]:          1 :               if VUNLIKELY (is_cancelled()) {
     994                 :            :                 return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     995                 :            :               }
     996                 :            : 
     997   [ +  -  -  + ]:          1 :               if (!drop_one_priority_task()) {
     998                 :            :                 return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     999                 :            :               }
    1000                 :            : 
    1001         [ +  - ]:          1 :               push_priority_task(std::move(callback), priority, droppable);
    1002                 :          1 :               is_full = false;
    1003         [ +  - ]:          1 :             }
    1004                 :            : 
    1005   [ +  -  +  - ]:          2 :             CLOG_W("MessageLoop: Task is full, removed top data (%s).", impl_->name.c_str());
    1006                 :          1 :             break;
    1007                 :            :           }
    1008                 :            :         }
    1009                 :            : 
    1010         [ -  + ]:         10 :         if VUNLIKELY (is_cancelled()) {
    1011                 :            :           return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1012                 :            :         }
    1013                 :            : 
    1014         [ +  - ]:         10 :         std::this_thread::sleep_for(std::chrono::milliseconds(1));
    1015                 :            :       }
    1016                 :            :     } while (is_full);
    1017                 :            : 
    1018         [ +  - ]:         22 :     wakeup();
    1019                 :            : 
    1020                 :         22 :     return !is_full;
    1021                 :            :   }
    1022                 :            : 
    1023                 :            :   return reject();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1024                 :            : }
    1025                 :            : 
    1026                 :       3618 : void MessageLoop::push_normal_task(Callback&& callback, bool droppable) {
    1027                 :       3618 :   uint32_t start_time = 0;
    1028                 :            : 
    1029   [ +  -  +  + ]:       3618 :   if (get_max_elapsed_time() > 0) {
    1030                 :        597 :     start_time = get_current_time<std::chrono::steady_clock, std::chrono::milliseconds, uint32_t>();
    1031                 :            :   }
    1032                 :            : 
    1033         [ +  - ]:       3618 :   impl_->normal_queue->emplace_back(start_time, droppable, std::move(callback));
    1034                 :       3618 : }
    1035                 :            : 
    1036                 :        434 : bool MessageLoop::push_lockfree_task(Callback&& callback) {
    1037                 :        434 :   uint32_t start_time = 0;
    1038                 :            : 
    1039   [ +  -  -  + ]:        434 :   if (get_max_elapsed_time() > 0) {
    1040                 :          0 :     start_time =
    1041                 :            :         get_current_time<std::chrono::steady_clock, std::chrono::milliseconds,  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1042                 :            :                          uint32_t>();                                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1043                 :            :   }
    1044                 :            : 
    1045         [ +  - ]:        434 :   for (int retry = 0; retry < kMaxLockfreePushRetry; ++retry) {
    1046         [ +  - ]:        434 :     if VLIKELY (impl_->lockfree_queue->try_push<Impl::LockfreeQueue::kNoBehavior>(
    1047                 :            :                     std::forward_as_tuple(start_time, std::move(callback)))) {
    1048                 :        434 :       return true;
    1049                 :            :     }
    1050                 :            : 
    1051                 :            :     Utils::yield_cpu();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1052                 :            :   }
    1053                 :            : 
    1054                 :            :   // LCOV_EXCL_START GCOVR_EXCL_START
    1055                 :            :   CLOG_E("MessageLoop: Failed to push lockfree task after %d retries (%s).", kMaxLockfreePushRetry,
    1056                 :            :          impl_->name.c_str());
    1057                 :            :   return false;
    1058                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1059                 :            : }
    1060                 :            : 
    1061                 :         23 : void MessageLoop::push_priority_task(Callback&& callback, uint16_t priority, bool droppable) {
    1062                 :         23 :   uint32_t start_time = 0;
    1063                 :            : 
    1064   [ +  -  -  + ]:         23 :   if (get_max_elapsed_time() > 0) {
    1065                 :          0 :     start_time =
    1066                 :            :         get_current_time<std::chrono::steady_clock, std::chrono::milliseconds,  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1067                 :            :                          uint32_t>();                                           // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1068                 :            :   }
    1069                 :            : 
    1070         [ +  + ]:         23 :   auto& queue = droppable ? impl_->priority_droppable_queue : impl_->priority_protected_queue;
    1071         [ +  + ]:         23 :   const uint16_t effective_priority = priority == kNoPriority ? static_cast<uint16_t>(kNormalPriority) : priority;
    1072         [ +  - ]:         46 :   queue->emplace(std::numeric_limits<uint16_t>::max() - effective_priority, impl_->task_seq, start_time, droppable,
    1073                 :         23 :                  std::move(callback));
    1074                 :         23 :   ++impl_->task_seq;
    1075                 :         23 : }
    1076                 :            : 
    1077                 :        358 : void MessageLoop::do_consume() {
    1078         [ +  - ]:        358 :   std::unique_lock lock(impl_->mtx);
    1079                 :            : 
    1080                 :        358 :   impl_->is_running.store(true, std::memory_order_release);
    1081                 :        358 :   impl_->is_busy.store(true, std::memory_order_release);
    1082                 :        358 :   impl_->thread_id.store(std::this_thread::get_id(), std::memory_order_release);
    1083                 :            : 
    1084                 :            : #ifdef _WIN32
    1085                 :            :   impl_->thread_handle.store(::OpenThread(THREAD_ALL_ACCESS, FALSE, ::GetCurrentThreadId()), std::memory_order_release);
    1086                 :            : #endif
    1087                 :            : 
    1088         [ +  - ]:        358 :   lock.unlock();
    1089                 :            : 
    1090         [ +  - ]:        358 :   on_begin();
    1091                 :            : 
    1092         [ +  + ]:        358 :   if (impl_->type == kNormalType) {
    1093   [ +  -  +  + ]:       3160 :     while (process_normal_task(true)) {
    1094                 :            :     }
    1095         [ +  + ]:         18 :   } else if (impl_->type == kLockfreeType) {
    1096   [ +  -  +  + ]:         49 :     while (process_lockfree_task(true)) {
    1097                 :            :     }
    1098         [ +  - ]:          9 :   } else if (impl_->type == kPriorityType) {
    1099   [ +  -  +  + ]:         28 :     while (process_priority_task(true)) {
    1100                 :            :     }
    1101                 :            :   }
    1102                 :            : 
    1103         [ +  - ]:        358 :   on_end();
    1104                 :            : 
    1105         [ +  - ]:        358 :   lock.lock();
    1106                 :            : 
    1107                 :        358 :   impl_->thread_id.store(std::thread::id(), std::memory_order_release);
    1108                 :        358 :   impl_->is_running.store(false, std::memory_order_release);
    1109                 :        358 :   impl_->is_busy.store(false, std::memory_order_release);
    1110                 :            : 
    1111         [ +  - ]:        358 :   lock.unlock();
    1112                 :            : 
    1113                 :        358 :   impl_->cv.notify_all();
    1114                 :        358 : }
    1115                 :            : 
    1116                 :       3165 : bool MessageLoop::process_normal_task(bool block) {
    1117                 :       3165 :   impl_->is_busy.store(true, std::memory_order_release);
    1118                 :            : 
    1119                 :       3165 :   [[maybe_unused]] bool is_timeout = true;
    1120                 :       3165 :   int64_t sleep_time = -1;
    1121                 :            : 
    1122                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
    1123   [ +  -  +  - ]:       3165 :   Impl::NormalQueue temp_queue(&MemoryResource::global_instance());
    1124                 :            : #else
    1125                 :            :   Impl::NormalQueue temp_queue;
    1126                 :            : #endif
    1127                 :            : 
    1128         [ +  - ]:       3165 :   std::unique_lock lock(impl_->mtx);
    1129                 :            : 
    1130                 :       3165 :   impl_->task_seq = 0;
    1131         [ +  - ]:       3165 :   temp_queue.swap(impl_->normal_queue.value());
    1132                 :            : 
    1133         [ +  - ]:       3165 :   lock.unlock();
    1134                 :            : 
    1135   [ +  +  +  +  :       6724 :   while (!temp_queue.empty() && !impl_->force_quit_flag.load(std::memory_order_acquire)) {
                   +  + ]
    1136                 :       3559 :     auto&& [start_time, droppable, task] = std::move(const_cast<Impl::NormalTaskTuple&>(temp_queue.front()));
    1137                 :            :     (void)droppable;
    1138         [ +  - ]:       3559 :     on_task_changed(std::move(task), start_time);
    1139                 :       3559 :     temp_queue.pop_front();
    1140                 :            :   }
    1141                 :            : 
    1142         [ +  - ]:       3165 :   on_idle();
    1143                 :            : 
    1144         [ +  - ]:       3165 :   lock.lock();
    1145                 :            : 
    1146         [ +  + ]:       3165 :   if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
    1147         [ +  - ]:        340 :     lock.unlock();
    1148         [ +  - ]:        340 :     drop_pending_tasks();
    1149                 :        340 :     return false;
    1150                 :            :   }
    1151                 :            : 
    1152         [ +  + ]:       2825 :   if (!impl_->timer_set.empty()) {
    1153         [ +  - ]:       2061 :     process_timer_task(sleep_time);
    1154                 :            :   }
    1155                 :            : 
    1156                 :       2825 :   impl_->is_busy.store(false, std::memory_order_release);
    1157                 :       2825 :   impl_->cv.notify_all();
    1158                 :            : 
    1159         [ +  + ]:       2825 :   if (block) {
    1160                 :      13412 :     auto predicate = [this]() -> bool {
    1161         [ +  - ]:       7199 :       return impl_->quit_flag.load(std::memory_order_acquire) || impl_->is_busy.load(std::memory_order_acquire) ||
    1162   [ +  +  +  +  :       7199 :              !impl_->normal_queue->empty() || impl_->wakeup_pending.load(std::memory_order_acquire);
                   +  + ]
    1163                 :       2820 :     };
    1164                 :            : 
    1165         [ +  + ]:       2820 :     if (sleep_time < 0) {
    1166                 :        800 :       impl_->cv.wait(lock, std::move(predicate));
    1167                 :        800 :       is_timeout = false;
    1168         [ +  + ]:       2020 :     } else if (sleep_time > 0) {
    1169                 :       1166 :       is_timeout = !impl_->cv.wait_for(lock, std::chrono::nanoseconds(sleep_time), std::move(predicate));
    1170                 :            :     }
    1171                 :            : 
    1172                 :       2820 :     impl_->wakeup_pending.store(false, std::memory_order_release);
    1173                 :            :   }
    1174                 :            : 
    1175                 :       2825 :   return true;
    1176                 :       3165 : }
    1177                 :            : 
    1178                 :         49 : bool MessageLoop::process_lockfree_task(bool block) {
    1179                 :         49 :   impl_->is_busy.store(true, std::memory_order_release);
    1180                 :            : 
    1181                 :         49 :   [[maybe_unused]] bool is_timeout = true;
    1182                 :            : 
    1183         [ +  - ]:        478 :   while (!impl_->force_quit_flag.load(std::memory_order_acquire)) {
    1184                 :        478 :     Impl::LockfreeTaskTuple temp_task;
    1185                 :            : 
    1186         [ +  + ]:        478 :     if (!impl_->lockfree_queue->try_pop<Impl::LockfreeQueue::kNoBehavior>(temp_task)) {
    1187                 :         49 :       break;
    1188                 :            :     }
    1189                 :            : 
    1190         [ +  - ]:        429 :     release_lockfree_task();
    1191                 :            : 
    1192                 :        429 :     auto&& [start_time, task] = std::move(temp_task);
    1193                 :            : 
    1194         [ +  - ]:        429 :     on_task_changed(std::move(task), start_time);
    1195         [ +  + ]:        478 :   }
    1196                 :            : 
    1197         [ +  - ]:         49 :   on_idle();
    1198                 :            : 
    1199         [ +  - ]:         49 :   std::unique_lock lock(impl_->mtx);
    1200                 :            : 
    1201                 :         49 :   int64_t sleep_time = -1;
    1202                 :            : 
    1203         [ +  + ]:         49 :   if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
    1204         [ +  - ]:          9 :     lock.unlock();
    1205         [ +  - ]:          9 :     drop_pending_tasks();
    1206                 :          9 :     return false;
    1207                 :            :   }
    1208                 :            : 
    1209         [ -  + ]:         40 :   if (!impl_->timer_set.empty()) {
    1210                 :            :     process_timer_task(sleep_time);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1211                 :            :   }
    1212                 :            : 
    1213                 :         40 :   impl_->is_busy.store(false, std::memory_order_release);
    1214                 :         40 :   impl_->cv.notify_all();
    1215                 :            : 
    1216         [ +  - ]:         40 :   if (block) {
    1217                 :        181 :     auto predicate = [this]() -> bool {
    1218         [ +  - ]:        110 :       return impl_->quit_flag.load(std::memory_order_acquire) || impl_->is_busy.load(std::memory_order_acquire) ||
    1219   [ +  +  +  +  :        181 :              impl_->lockfree_task_count.load(std::memory_order_acquire) != 0U ||
                   +  + ]
    1220                 :         79 :              impl_->wakeup_pending.load(std::memory_order_acquire);
    1221                 :         40 :     };
    1222                 :            : 
    1223         [ +  - ]:         40 :     if (sleep_time < 0) {
    1224                 :         40 :       impl_->cv.wait(lock, std::move(predicate));
    1225                 :         40 :       is_timeout = false;
    1226                 :            :     } else if (sleep_time > 0) {                                                    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1227                 :            :       is_timeout = !impl_->cv.wait_for(lock, std::chrono::nanoseconds(sleep_time),  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1228                 :            :                                        std::move(predicate));                       // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1229                 :            :     }
    1230                 :            : 
    1231                 :         40 :     impl_->wakeup_pending.store(false, std::memory_order_release);
    1232                 :            :   }
    1233                 :            : 
    1234                 :         40 :   return true;
    1235                 :         49 : }
    1236                 :            : 
    1237                 :         30 : bool MessageLoop::process_priority_task(bool block) {
    1238                 :         30 :   impl_->is_busy.store(true, std::memory_order_release);
    1239                 :            : 
    1240                 :         30 :   [[maybe_unused]] bool is_timeout = true;
    1241                 :         30 :   int64_t sleep_time = -1;
    1242                 :            : 
    1243                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
    1244         [ +  - ]:         30 :   Impl::PriorityQueue temp_queue(&MemoryResource::global_instance());
    1245         [ +  - ]:         30 :   Impl::PriorityQueue temp_protected_queue(&MemoryResource::global_instance());
    1246                 :            : #else
    1247                 :            :   Impl::PriorityQueue temp_queue;
    1248                 :            :   Impl::PriorityQueue temp_protected_queue;
    1249                 :            : #endif
    1250                 :            : 
    1251         [ +  - ]:         30 :   std::unique_lock lock(impl_->mtx);
    1252                 :            : 
    1253                 :         30 :   impl_->task_seq = 0;
    1254         [ +  - ]:         30 :   temp_queue.swap(impl_->priority_droppable_queue.value());
    1255         [ +  - ]:         30 :   temp_protected_queue.swap(impl_->priority_protected_queue.value());
    1256                 :            : 
    1257         [ +  - ]:         30 :   lock.unlock();
    1258                 :            : 
    1259   [ +  +  +  +  :         64 :   while ((!temp_queue.empty() || !temp_protected_queue.empty()) &&
                   +  + ]
    1260         [ +  - ]:         17 :          !impl_->force_quit_flag.load(std::memory_order_acquire)) {
    1261                 :            :     const bool drain_protected =
    1262         [ +  + ]:         33 :         temp_queue.empty() ||
    1263   [ +  +  +  - ]:         16 :         (!temp_protected_queue.empty() && !Impl::priority_before(temp_queue.top(), temp_protected_queue.top()));
    1264         [ +  + ]:         17 :     auto& selected_queue = drain_protected ? temp_protected_queue : temp_queue;
    1265                 :         17 :     auto&& [priority, seq, start_time, droppable, task] =
    1266                 :         17 :         std::move(const_cast<Impl::PriorityTaskTuple&>(selected_queue.top()));
    1267                 :            :     (void)droppable;
    1268                 :            : 
    1269         [ +  - ]:         17 :     on_task_changed(std::move(task), start_time);
    1270                 :            : 
    1271         [ +  - ]:         17 :     selected_queue.pop();
    1272                 :            :   }
    1273                 :            : 
    1274         [ +  - ]:         30 :   on_idle();
    1275                 :            : 
    1276         [ +  - ]:         30 :   lock.lock();
    1277                 :            : 
    1278         [ +  + ]:         30 :   if VUNLIKELY (impl_->quit_flag.load(std::memory_order_acquire)) {
    1279         [ +  - ]:          9 :     lock.unlock();
    1280         [ +  - ]:          9 :     drop_pending_tasks();
    1281                 :          9 :     return false;
    1282                 :            :   }
    1283                 :            : 
    1284         [ +  + ]:         21 :   if (!impl_->timer_set.empty()) {
    1285         [ +  - ]:          4 :     process_timer_task(sleep_time);
    1286                 :            :   }
    1287                 :            : 
    1288                 :         21 :   impl_->is_busy.store(false, std::memory_order_release);
    1289                 :         21 :   impl_->cv.notify_all();
    1290                 :            : 
    1291         [ +  + ]:         21 :   if (block) {
    1292                 :        120 :     auto predicate = [this]() -> bool {
    1293         [ +  - ]:         59 :       return impl_->quit_flag.load(std::memory_order_acquire) || impl_->is_busy.load(std::memory_order_acquire) ||
    1294   [ +  +  +  +  :         77 :              !impl_->priority_droppable_queue->empty() || !impl_->priority_protected_queue->empty() ||
             +  -  +  + ]
    1295                 :         52 :              impl_->wakeup_pending.load(std::memory_order_acquire);
    1296                 :         19 :     };
    1297                 :            : 
    1298         [ +  + ]:         19 :     if (sleep_time < 0) {
    1299                 :         18 :       impl_->cv.wait(lock, std::move(predicate));
    1300                 :         18 :       is_timeout = false;
    1301         [ +  - ]:          1 :     } else if (sleep_time > 0) {
    1302                 :          1 :       is_timeout = !impl_->cv.wait_for(lock, std::chrono::nanoseconds(sleep_time), std::move(predicate));
    1303                 :            :     }
    1304                 :            : 
    1305                 :         19 :     impl_->wakeup_pending.store(false, std::memory_order_release);
    1306                 :            :   }
    1307                 :            : 
    1308                 :         21 :   return true;
    1309                 :         30 : }
    1310                 :            : 
    1311                 :       2065 : bool MessageLoop::process_timer_task(int64_t& next_sleep_time) {
    1312                 :       2065 :   int64_t invoke_count = 0;
    1313                 :       2065 :   int64_t remain_loop_count = 0;
    1314                 :       2065 :   int64_t interval_time = 0;
    1315                 :       2065 :   int64_t remain_time = 0;
    1316                 :       2065 :   uint64_t processed_invoke_count = 0;
    1317                 :       2065 :   bool has_erase = false;
    1318                 :       2065 :   bool has_processed = false;
    1319                 :            : 
    1320                 :       2065 :   next_sleep_time = -1;
    1321                 :            : 
    1322         [ +  + ]:       5354 :   for (auto iter = impl_->timer_set.begin(); iter != impl_->timer_set.end();) {
    1323                 :       3289 :     Timer* timer = *iter;
    1324                 :            : 
    1325   [ +  -  +  + ]:       3289 :     if (!timer->is_active()) {
    1326                 :         34 :       ++iter;
    1327                 :         34 :       continue;
    1328                 :            :     }
    1329                 :            : 
    1330   [ +  -  -  + ]:       3255 :     if VUNLIKELY (!timer->has_callback()) {
    1331                 :            :       timer->stop();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1332                 :            :       ++iter;         // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1333                 :            :       continue;       // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1334                 :            :     }
    1335                 :            : 
    1336                 :       3255 :     interval_time =
    1337   [ +  -  +  +  :       3255 :         timer->get_interval() == 0 ? Timer::kMinInterval : static_cast<uint64_t>(timer->get_interval()) * 1000'000U;
                   +  - ]
    1338                 :            : 
    1339         [ +  - ]:       3255 :     uint64_t start_time = timer->get_start_time();
    1340         [ +  - ]:       3255 :     uint64_t current_time = get_current_nano_time();
    1341                 :            : 
    1342         [ -  + ]:       3255 :     if VUNLIKELY (current_time < start_time) {
    1343                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1344                 :            :       remain_time = static_cast<int64_t>((start_time - current_time) + static_cast<uint64_t>(interval_time) -
    1345                 :            :                                          Timer::kMinInterval);
    1346                 :            : 
    1347                 :            :       if (remain_time < 0) {
    1348                 :            :         remain_time = 0;
    1349                 :            :       }
    1350                 :            : 
    1351                 :            :       if (next_sleep_time < 0 || next_sleep_time > remain_time) {
    1352                 :            :         next_sleep_time = remain_time;
    1353                 :            :       }
    1354                 :            : 
    1355                 :            :       ++iter;
    1356                 :            :       continue;
    1357                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1358                 :            :     }
    1359                 :            : 
    1360         [ +  - ]:       3255 :     processed_invoke_count = timer->get_invoke_count();
    1361                 :       3255 :     invoke_count = (current_time - start_time + Timer::kMinInterval) / interval_time;
    1362                 :            : 
    1363                 :       3255 :     remain_loop_count = invoke_count - processed_invoke_count;
    1364                 :            : 
    1365         [ +  + ]:       3255 :     if (remain_loop_count > 0) {
    1366                 :        945 :       has_erase = false;
    1367                 :        945 :       bool capacity_blocked = false;
    1368                 :            : 
    1369         [ +  - ]:        945 :       auto alive_flag = timer->get_alive_flag();
    1370                 :       5610 :       auto run_timer_callback = [this, timer, alive_flag]() {
    1371         [ -  + ]:        935 :         if VUNLIKELY (!alive_flag->load(std::memory_order_acquire)) {
    1372                 :          0 :           return;
    1373                 :            :         }
    1374                 :            : 
    1375                 :            :         {
    1376         [ +  - ]:        935 :           std::lock_guard timer_lock(impl_->mtx);
    1377                 :            : 
    1378   [ +  -  +  -  :        935 :           if VUNLIKELY (!alive_flag->load(std::memory_order_acquire) || impl_->timer_set.count(timer) == 0) {
             -  +  -  + ]
    1379                 :            :             return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1380                 :            :           }
    1381                 :            : 
    1382         [ +  - ]:        935 :           timer->begin_in_flight();
    1383         [ +  - ]:        935 :         }
    1384                 :            : 
    1385                 :        935 :         timer->run_callback();
    1386                 :        935 :         timer->end_in_flight();
    1387                 :        945 :       };
    1388                 :            : 
    1389   [ +  -  +  -  :        945 :       for (int64_t i = 0; timer->get_remain_loop_count() != 0 && i < remain_loop_count; ++i) {
             +  -  +  - ]
    1390         [ +  + ]:        945 :         if (impl_->type == kNormalType) {
    1391   [ +  -  -  + ]:        944 :           if VUNLIKELY (impl_->normal_queue->size() >= get_max_task_count()) {
    1392                 :            :             // LCOV_EXCL_START GCOVR_EXCL_START
    1393                 :            :             if (!drop_one_normal_task()) {
    1394                 :            :               CLOG_W("MessageLoop: Timer task is full and no task can be dropped (%s).", impl_->name.c_str());
    1395                 :            :               capacity_blocked = true;
    1396                 :            :               break;
    1397                 :            :             }
    1398                 :            : 
    1399                 :            :             CLOG_W("MessageLoop: Timer task is full, removed top data (%s).", impl_->name.c_str());
    1400                 :            : 
    1401                 :            :             std::this_thread::sleep_for(std::chrono::milliseconds(1));
    1402                 :            :             // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1403                 :            :           }
    1404                 :            : 
    1405   [ +  -  +  + ]:        944 :           if (timer->is_once_type()) {
    1406   [ +  -  +  - ]:          9 :             push_normal_task(timer->take_callback());
    1407                 :            :           } else {
    1408   [ +  -  +  - ]:        935 :             push_normal_task(run_timer_callback);
    1409                 :            :           }
    1410                 :            : 
    1411                 :        944 :           ++processed_invoke_count;
    1412                 :        944 :           has_processed = true;
    1413         [ -  + ]:          1 :         } else if (impl_->type == kLockfreeType) {
    1414                 :            :           // LCOV_EXCL_START GCOVR_EXCL_START
    1415                 :            :           if VUNLIKELY (!reserve_lockfree_task()) {
    1416                 :            :             if (!drop_one_lockfree_task(true)) {
    1417                 :            :               CLOG_W("MessageLoop: Timer task is full and no task can be dropped (%s).", impl_->name.c_str());
    1418                 :            :               capacity_blocked = true;
    1419                 :            :               break;
    1420                 :            :             }
    1421                 :            : 
    1422                 :            :             CLOG_W("MessageLoop: Timer task is full, removed top data (%s).", impl_->name.c_str());
    1423                 :            :           }
    1424                 :            : 
    1425                 :            :           bool pushed = false;
    1426                 :            : 
    1427                 :            :           if (timer->is_once_type()) {
    1428                 :            :             pushed = push_lockfree_task(timer->take_callback());
    1429                 :            :           } else {
    1430                 :            :             pushed = push_lockfree_task(run_timer_callback);
    1431                 :            :           }
    1432                 :            : 
    1433                 :            :           if VUNLIKELY (!pushed) {
    1434                 :            :             release_lockfree_task();
    1435                 :            :             capacity_blocked = true;
    1436                 :            :             break;
    1437                 :            :           }
    1438                 :            : 
    1439                 :            :           ++processed_invoke_count;
    1440                 :            :           has_processed = true;
    1441                 :            :           // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1442         [ +  - ]:          1 :         } else if (impl_->type == kPriorityType) {
    1443   [ +  -  -  + ]:          1 :           if VUNLIKELY (impl_->priority_droppable_queue->size() + impl_->priority_protected_queue->size() >=
    1444                 :            :                         get_max_task_count()) {
    1445                 :            :             // LCOV_EXCL_START GCOVR_EXCL_START
    1446                 :            :             if (!drop_one_priority_task()) {
    1447                 :            :               CLOG_W("MessageLoop: Timer task is full and no droppable task exists (%s).", impl_->name.c_str());
    1448                 :            :               capacity_blocked = true;
    1449                 :            :               break;
    1450                 :            :             }
    1451                 :            : 
    1452                 :            :             CLOG_W("MessageLoop: Timer task is full, removed top data (%s).", impl_->name.c_str());
    1453                 :            : 
    1454                 :            :             std::this_thread::sleep_for(std::chrono::milliseconds(1));
    1455                 :            :             // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1456                 :            :           }
    1457                 :            : 
    1458   [ +  -  +  - ]:          1 :           if (timer->is_once_type()) {
    1459   [ +  -  +  -  :          1 :             push_priority_task(timer->take_callback(), timer->get_priority());
                   +  - ]
    1460                 :            :           } else {
    1461                 :            :             push_priority_task(run_timer_callback, timer->get_priority());  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1462                 :            :           }
    1463                 :            : 
    1464                 :          1 :           ++processed_invoke_count;
    1465                 :          1 :           has_processed = true;
    1466                 :            :         }
    1467                 :            : 
    1468   [ +  -  +  + ]:        945 :         if (timer->is_once_type()) {
    1469         [ +  - ]:         10 :           iter = impl_->timer_set.erase(iter);
    1470                 :         10 :           timer->~Timer();
    1471         [ +  - ]:         10 :           MemoryPool::global_instance().deallocate(timer, sizeof(Timer), alignof(Timer));
    1472                 :         10 :           has_erase = true;
    1473                 :         10 :           break;
    1474                 :            :         }
    1475                 :            : 
    1476         [ +  - ]:        935 :         timer->sub_remain_loop_count();
    1477                 :            : 
    1478   [ +  -  +  - ]:        935 :         if (!timer->is_strict()) {
    1479                 :        935 :           break;
    1480                 :            :         }
    1481                 :            :       }
    1482                 :            : 
    1483         [ +  + ]:        945 :       if VUNLIKELY (has_erase) {
    1484                 :         10 :         continue;
    1485                 :            :       }
    1486                 :            : 
    1487   [ +  -  +  + ]:        935 :       if (timer->get_remain_loop_count() == 0) {
    1488         [ +  - ]:         10 :         timer->stop();
    1489                 :            :       } else {
    1490   [ -  +  +  - ]:        925 :         timer->set_invoke_count(capacity_blocked
    1491                 :            :                                     ? processed_invoke_count
    1492                 :            :                                     : static_cast<uint64_t>(invoke_count));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1493                 :            :       }
    1494                 :            : 
    1495                 :        935 :       next_sleep_time = 0;
    1496                 :            : 
    1497   [ +  +  +  + ]:        955 :     } else {
    1498                 :       2310 :       remain_time = interval_time - (current_time - start_time) % interval_time - Timer::kMinInterval;
    1499                 :            : 
    1500         [ -  + ]:       2310 :       if (remain_time < 0) {
    1501                 :            :         remain_time = 0;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1502                 :            :       }
    1503                 :            : 
    1504   [ +  +  +  + ]:       2310 :       if (next_sleep_time < 0 || next_sleep_time > remain_time) {
    1505                 :       2143 :         next_sleep_time = remain_time;
    1506                 :            :       }
    1507                 :            :     }
    1508                 :            : 
    1509                 :       3245 :     ++iter;
    1510                 :            :   }
    1511                 :            : 
    1512                 :       2065 :   return has_processed;
    1513                 :            : }
    1514                 :            : 
    1515                 :       1013 : void MessageLoop::drop_pending_tasks() {
    1516                 :            : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE
    1517   [ +  -  +  - ]:       1013 :   Impl::NormalQueue normal_queue(&MemoryResource::global_instance());
    1518         [ +  - ]:       1013 :   Impl::PriorityQueue priority_droppable_queue(&MemoryResource::global_instance());
    1519         [ +  - ]:       1013 :   Impl::PriorityQueue priority_protected_queue(&MemoryResource::global_instance());
    1520                 :            : #else
    1521                 :            :   Impl::NormalQueue normal_queue;
    1522                 :            :   Impl::PriorityQueue priority_droppable_queue;
    1523                 :            :   Impl::PriorityQueue priority_protected_queue;
    1524                 :            : #endif
    1525                 :       1013 :   std::vector<Impl::LockfreeTaskTuple> lockfree_tasks;
    1526                 :            : 
    1527                 :            :   {
    1528         [ +  - ]:       1013 :     std::lock_guard lock(impl_->mtx);
    1529                 :            : 
    1530         [ +  + ]:       1013 :     if (impl_->type == kNormalType) {
    1531         [ +  - ]:        977 :       normal_queue.swap(impl_->normal_queue.value());
    1532         [ +  + ]:         36 :     } else if (impl_->type == kLockfreeType) {
    1533                 :         18 :       Impl::LockfreeTaskTuple task;
    1534         [ -  + ]:         18 :       while (impl_->lockfree_queue->try_pop<Impl::LockfreeQueue::kNoBehavior>(task)) {
    1535                 :            :         release_lockfree_task();                       // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1536                 :            :         lockfree_tasks.emplace_back(std::move(task));  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1537                 :            :       }
    1538         [ +  - ]:         36 :     } else if (impl_->type == kPriorityType) {
    1539         [ +  - ]:         18 :       priority_droppable_queue.swap(impl_->priority_droppable_queue.value());
    1540         [ +  - ]:         18 :       priority_protected_queue.swap(impl_->priority_protected_queue.value());
    1541                 :            :     }
    1542                 :       1013 :   }
    1543                 :       1013 : }
    1544                 :            : 
    1545                 :            : }  // namespace vlink

Generated by: LCOV version 1.14