LCOV - code coverage report
Current view: top level - src/base - schedule.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 149 150 99.3 %
Date: 2026-06-27 19:56:04 Functions: 22 22 100.0 %
Branches: 100 160 62.5 %

           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/schedule.h"
      25                 :            : 
      26                 :            : #include <chrono>
      27                 :            : #include <cstdint>
      28                 :            : #include <memory>
      29                 :            : #include <mutex>
      30                 :            : #include <optional>
      31                 :            : #include <utility>
      32                 :            : #include <vector>
      33                 :            : 
      34                 :            : #include "./base/logger.h"
      35                 :            : #include "./base/memory_resource.h"
      36                 :            : 
      37                 :            : namespace vlink {
      38                 :            : 
      39                 :            : // Schedule::Config
      40                 :         25 : Schedule::Config::Config() = default;
      41                 :            : 
      42                 :         18 : Schedule::Config::Config(uint32_t _delay_ms, uint16_t _priority, uint32_t _schedule_timeout_ms,
      43                 :         18 :                          uint32_t _execution_timeout_ms)
      44                 :         18 :     : delay_ms(_delay_ms),
      45                 :         18 :       priority(_priority),
      46                 :         18 :       schedule_timeout_ms(_schedule_timeout_ms),
      47                 :         18 :       execution_timeout_ms(_execution_timeout_ms) {}
      48                 :            : 
      49                 :            : // Schedule::Status
      50                 :         44 : Schedule::Status::Status() : impl_(MemoryResource::make_shared<Schedule::Status::Impl>()) {
      51                 :         44 :   impl_->is_valid.store(true, std::memory_order_relaxed);
      52                 :         44 : }
      53                 :            : 
      54                 :         73 : Schedule::Status::~Status() { commit(); }
      55                 :            : 
      56                 :         29 : Schedule::Status::Status(Status&& status) noexcept {
      57         [ -  + ]:         29 :   if VUNLIKELY (this == &status) {
      58                 :            :     return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      59                 :            :   }
      60                 :            : 
      61                 :         29 :   impl_ = std::move(status.impl_);
      62                 :         29 :   launcher_ = std::move(status.launcher_);
      63                 :         29 :   committed_ = status.committed_;
      64                 :            : }
      65                 :            : 
      66                 :          2 : Schedule::Status& Schedule::Status::operator=(Status&& status) noexcept {
      67         [ -  + ]:          2 :   if VUNLIKELY (this == &status) {
      68                 :            :     return *this;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      69                 :            :   }
      70                 :            : 
      71                 :          2 :   commit();
      72                 :            : 
      73                 :          2 :   impl_ = std::move(status.impl_);
      74                 :          2 :   launcher_ = std::move(status.launcher_);
      75                 :          2 :   committed_ = status.committed_;
      76                 :            : 
      77                 :          2 :   return *this;
      78                 :            : }
      79                 :            : 
      80                 :         83 : void Schedule::Status::commit() noexcept {
      81   [ +  +  +  +  :         83 :   if (!impl_ || committed_) {
                   +  + ]
      82                 :         39 :     return;
      83                 :            :   }
      84                 :            : 
      85                 :         44 :   committed_ = true;
      86                 :            : 
      87         [ +  + ]:         44 :   if VLIKELY (launcher_) {
      88                 :         28 :     launcher_();
      89                 :            :   }
      90                 :            : }
      91                 :            : 
      92                 :          4 : void Schedule::Status::set_valid(bool valid) {
      93         [ +  - ]:          4 :   if VLIKELY (impl_) {
      94                 :          4 :     impl_->is_valid.store(valid, std::memory_order_relaxed);
      95                 :            :   }
      96                 :          4 : }
      97                 :            : 
      98   [ +  -  +  + ]:         46 : bool Schedule::Status::is_valid() const { return impl_ && impl_->is_valid.load(std::memory_order_relaxed); }
      99                 :            : 
     100                 :          8 : bool Schedule::Status::dispatch() {
     101                 :          8 :   commit();
     102                 :            : 
     103                 :          8 :   return is_valid();
     104                 :            : }
     105                 :            : 
     106                 :          3 : Schedule::Status& Schedule::Status::on_execution_timeout(Callback&& callback) {
     107         [ +  - ]:          3 :   if VLIKELY (is_valid()) {
     108         [ +  - ]:          3 :     std::lock_guard lock(impl_->mtx);
     109                 :            : 
     110         [ -  + ]:          3 :     if VUNLIKELY (impl_->dispatched.load(std::memory_order_acquire)) {
     111                 :            :       VLOG_E("Schedule: on_execution_timeout registered after dispatch; ignored.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     112                 :            :       return *this;                                                                  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     113                 :            :     }
     114                 :            : 
     115         [ -  + ]:          3 :     if VUNLIKELY (impl_->execution_timeout_callback) {
     116                 :            :       VLOG_E("Schedule: Execution timeout callback is already set.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     117                 :            :       return *this;                                                    // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     118                 :            :     }
     119                 :            : 
     120                 :          3 :     impl_->execution_timeout_callback = std::move(callback);
     121         [ +  - ]:          3 :   }
     122                 :            : 
     123                 :          3 :   return *this;
     124                 :            : }
     125                 :            : 
     126                 :          3 : Schedule::Status& Schedule::Status::on_schedule_timeout(Callback&& callback) {
     127         [ +  - ]:          3 :   if VLIKELY (is_valid()) {
     128         [ +  - ]:          3 :     std::lock_guard lock(impl_->mtx);
     129                 :            : 
     130         [ -  + ]:          3 :     if VUNLIKELY (impl_->dispatched.load(std::memory_order_acquire)) {
     131                 :            :       VLOG_E("Schedule: on_schedule_timeout registered after dispatch; ignored.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     132                 :            :       return *this;                                                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     133                 :            :     }
     134                 :            : 
     135         [ -  + ]:          3 :     if VUNLIKELY (impl_->schedule_timeout_callback) {
     136                 :            :       VLOG_E("Schedule: Schedule timeout callback is already set.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     137                 :            :       return *this;                                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     138                 :            :     }
     139                 :            : 
     140                 :          3 :     impl_->schedule_timeout_callback = std::move(callback);
     141         [ +  - ]:          3 :   }
     142                 :            : 
     143                 :          3 :   return *this;
     144                 :            : }
     145                 :            : 
     146                 :          5 : Schedule::Status& Schedule::Status::on_catch(CatchCallback&& callback) {
     147         [ +  - ]:          5 :   if VLIKELY (is_valid()) {
     148         [ +  - ]:          5 :     std::lock_guard lock(impl_->mtx);
     149                 :            : 
     150         [ -  + ]:          5 :     if VUNLIKELY (impl_->dispatched.load(std::memory_order_acquire)) {
     151                 :            :       VLOG_E("Schedule: on_catch registered after dispatch; ignored.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     152                 :            :       return *this;                                                      // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     153                 :            :     }
     154                 :            : 
     155         [ -  + ]:          5 :     if VUNLIKELY (impl_->catch_callback) {
     156                 :            :       VLOG_E("Schedule: Catch callback is already set.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     157                 :            :       return *this;                                        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     158                 :            :     }
     159                 :            : 
     160                 :          5 :     impl_->catch_callback = std::move(callback);
     161         [ +  - ]:          5 :   }
     162                 :            : 
     163                 :          5 :   return *this;
     164                 :            : }
     165                 :            : 
     166                 :            : // Schedule::RetStatus
     167                 :          6 : Schedule::Status& Schedule::RetStatus::on_else(Callback&& callback) {
     168         [ +  - ]:          6 :   if VLIKELY (is_valid()) {
     169         [ +  - ]:          6 :     std::lock_guard lock(impl_->mtx);
     170                 :            : 
     171         [ -  + ]:          6 :     if VUNLIKELY (impl_->dispatched.load(std::memory_order_acquire)) {
     172                 :            :       VLOG_E("Schedule: on_else registered after dispatch; ignored.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     173                 :            :       return *this;                                                     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     174                 :            :     }
     175                 :            : 
     176         [ -  + ]:          6 :     if VUNLIKELY (impl_->else_callback) {
     177                 :            :       VLOG_E("Schedule: Else callback is already set.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     178                 :            :       return *this;                                       // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     179                 :            :     }
     180                 :            : 
     181                 :          6 :     impl_->else_callback = std::move(callback);
     182         [ +  - ]:          6 :   }
     183                 :            : 
     184                 :          6 :   return *this;
     185                 :            : }
     186                 :            : 
     187                 :         15 : Schedule::RetStatus& Schedule::RetStatus::on_then(RetCallback&& callback) {
     188         [ +  - ]:         15 :   if VLIKELY (is_valid()) {
     189         [ +  - ]:         15 :     std::lock_guard lock(impl_->mtx);
     190                 :            : 
     191         [ +  + ]:         15 :     if VUNLIKELY (impl_->dispatched.load(std::memory_order_acquire)) {
     192   [ +  -  +  - ]:          2 :       VLOG_E("Schedule: on_then registered after dispatch; ignored.");
     193                 :          1 :       return *this;
     194                 :            :     }
     195                 :            : 
     196         [ +  - ]:         14 :     impl_->then_callback_list.emplace_back(std::move(callback));
     197         [ +  + ]:         15 :   }
     198                 :            : 
     199                 :         14 :   return *this;
     200                 :            : }
     201                 :            : 
     202                 :            : // Schedule
     203                 :         28 : Schedule::Status Schedule::process(const Config& config, Callback&& callback, Callback& wrapper_callback) {
     204                 :         28 :   RetCallback adapted_callback = [callback = std::move(callback)]() mutable -> bool {
     205         [ +  - ]:         24 :     if VLIKELY (callback) {
     206                 :         24 :       callback();
     207                 :            :     }
     208                 :            : 
     209                 :         23 :     return true;
     210         [ +  - ]:         28 :   };
     211                 :            : 
     212         [ +  - ]:         56 :   return internal_process_with_ret(config, std::move(adapted_callback), wrapper_callback);
     213                 :         28 : }
     214                 :            : 
     215                 :         11 : Schedule::RetStatus Schedule::process_with_ret(const Config& config, RetCallback&& callback,
     216                 :            :                                                Callback& wrapper_callback) {
     217                 :         11 :   return internal_process_with_ret(config, std::move(callback), wrapper_callback);
     218                 :            : }
     219                 :            : 
     220                 :         39 : Schedule::RetStatus Schedule::internal_process_with_ret(const Config& config, RetCallback&& callback,
     221                 :            :                                                         Callback& wrapper_callback) {
     222         [ +  - ]:         39 :   Schedule::RetStatus status;
     223                 :            : 
     224                 :         39 :   auto submit_time = std::chrono::steady_clock::now();
     225                 :            : 
     226                 :         39 :   wrapper_callback = [callback = std::move(callback), config, submit_time, impl = status.impl_]() mutable {
     227                 :         35 :     Schedule::Callback schedule_timeout_cb;
     228                 :         35 :     Schedule::Callback execution_timeout_cb;
     229                 :         35 :     Schedule::CatchCallback catch_cb;
     230                 :         35 :     Schedule::Callback else_cb;
     231                 :         35 :     std::vector<Schedule::RetCallback> then_callbacks;
     232                 :            : 
     233                 :            :     {
     234         [ +  - ]:         35 :       std::lock_guard lock(impl->mtx);
     235                 :            : 
     236         [ -  + ]:         35 :       if VUNLIKELY (!impl->is_valid.load(std::memory_order_relaxed)) {
     237                 :            :         return;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     238                 :            :       }
     239                 :            : 
     240                 :         35 :       schedule_timeout_cb = std::move(impl->schedule_timeout_callback);
     241                 :         35 :       execution_timeout_cb = std::move(impl->execution_timeout_callback);
     242                 :         35 :       catch_cb = std::move(impl->catch_callback);
     243                 :         35 :       else_cb = std::move(impl->else_callback);
     244                 :         35 :       then_callbacks = std::move(impl->then_callback_list);
     245                 :            : 
     246                 :         35 :       impl->dispatched.store(true, std::memory_order_release);
     247         [ +  - ]:         35 :     }
     248                 :            : 
     249                 :         35 :     auto now = std::chrono::steady_clock::now();
     250                 :            : 
     251         [ +  + ]:         35 :     if (config.schedule_timeout_ms > 0) {
     252   [ +  -  +  - ]:          2 :       auto wait_ms = std::chrono::duration_cast<std::chrono::milliseconds>(now - submit_time).count();
     253                 :            : 
     254         [ +  + ]:          2 :       if (static_cast<uint32_t>(wait_ms) > config.schedule_timeout_ms + config.delay_ms) {
     255         [ +  - ]:          1 :         if (schedule_timeout_cb) {
     256         [ +  - ]:          1 :           schedule_timeout_cb();
     257                 :            :         }
     258                 :            : 
     259                 :          1 :         return;
     260                 :            :       }
     261                 :            :     }
     262                 :            : 
     263                 :         76 :     auto run_with_timeout = [&catch_cb, &config,
     264                 :         49 :                              &execution_timeout_cb](Schedule::RetCallback& exe_callback) -> std::optional<bool> {
     265         [ -  + ]:         42 :       if VUNLIKELY (!exe_callback) {
     266                 :            :         return std::nullopt;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     267                 :            :       }
     268                 :            : 
     269                 :         42 :       auto exec_start = std::chrono::steady_clock::now();
     270                 :            : 
     271                 :         42 :       bool result = false;
     272                 :            : 
     273                 :            :       try {
     274         [ +  + ]:         42 :         result = exe_callback();
     275         [ -  + ]:          3 :       } catch (std::exception& e) {
     276         [ +  - ]:          3 :         if (catch_cb) {
     277         [ +  - ]:          3 :           catch_cb(e);
     278                 :            : 
     279                 :          3 :           return std::nullopt;
     280                 :            :         }
     281         [ +  - ]:          3 :       }
     282                 :            : 
     283                 :         39 :       auto exec_end = std::chrono::steady_clock::now();
     284                 :            : 
     285         [ +  + ]:         39 :       if (config.execution_timeout_ms > 0) {
     286   [ +  -  +  - ]:          2 :         auto exec_ms = std::chrono::duration_cast<std::chrono::milliseconds>(exec_end - exec_start).count();
     287                 :            : 
     288         [ +  + ]:          2 :         if (static_cast<uint32_t>(exec_ms) > config.execution_timeout_ms) {
     289         [ +  - ]:          1 :           if (execution_timeout_cb) {
     290         [ +  - ]:          1 :             execution_timeout_cb();
     291                 :            :           }
     292                 :            : 
     293                 :          1 :           return std::nullopt;
     294                 :            :         }
     295                 :            :       }
     296                 :            : 
     297                 :         38 :       return result;
     298                 :         34 :     };
     299                 :            : 
     300         [ +  - ]:         34 :     auto main_ret = run_with_timeout(callback);
     301                 :            : 
     302         [ +  + ]:         34 :     if (!main_ret.has_value()) {
     303                 :          4 :       return;
     304                 :            :     }
     305                 :            : 
     306   [ +  -  +  + ]:         30 :     if (!main_ret.value()) {
     307         [ +  - ]:          2 :       if (else_cb) {
     308         [ +  - ]:          2 :         else_cb();
     309                 :            :       }
     310                 :            : 
     311                 :          2 :       return;
     312                 :            :     }
     313                 :            : 
     314         [ +  + ]:         35 :     for (auto& then_callback : then_callbacks) {
     315         [ +  - ]:          8 :       auto then_ret = run_with_timeout(then_callback);
     316                 :            : 
     317         [ -  + ]:          8 :       if (!then_ret.has_value()) {
     318                 :          1 :         return;
     319                 :            :       }
     320                 :            : 
     321   [ +  -  +  + ]:          8 :       if (!then_ret.value()) {
     322         [ -  + ]:          1 :         if (else_cb) {
     323         [ #  # ]:          0 :           else_cb();
     324                 :            :         }
     325                 :            : 
     326                 :          1 :         return;
     327                 :            :       }
     328                 :            :     }
     329   [ +  -  +  +  :        106 :   };
          +  +  +  +  +  
                +  +  + ]
     330                 :            : 
     331                 :         78 :   return status;
     332                 :            : }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     333                 :            : 
     334                 :            : }  // namespace vlink

Generated by: LCOV version 1.14