LCOV - code coverage report
Current view: top level - src/base - message_loop.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 698 713 97.9 %
Date: 2026-07-26 14:05:51 Functions: 93 95 97.9 %
Branches: 665 996 66.8 %

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

Generated by: LCOV version 1.14