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/cancellation.h" 25 : : 26 : : #include <memory> 27 : : #include <mutex> 28 : : #include <unordered_map> 29 : : #include <utility> 30 : : #include <vector> 31 : : 32 : : #include "./base/logger.h" 33 : : #include "./base/memory_resource.h" 34 : : 35 : : namespace vlink { 36 : : 37 : 491 : [[maybe_unused]] static void invoke_cancellation_callback(MoveFunction<void()>& callback) noexcept { 38 [ - + ]: 491 : if VUNLIKELY (!callback) { 39 : 0 : return; 40 : : } 41 : : 42 : : try { 43 [ + + ]: 491 : callback(); 44 [ + - ]: 1 : } catch (const std::exception& e) { 45 : 2 : CLOG_E("Cancellation callback threw an exception: %s.", e.what()); 46 : 1 : } catch (...) { 47 : : CLOG_E("Cancellation callback threw a non-std exception."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 48 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE 49 : : } 50 : : 51 : : // CancellationRegistration::State 52 : : struct CancellationRegistration::State final { 53 : : mutable std::mutex mtx; 54 : : bool cancelled{false}; 55 : : size_t next_id{1}; 56 : : std::unordered_map<size_t, MoveFunction<void()>> callbacks; 57 : : }; 58 : : 59 : : // CancellationRegistration 60 : 637 : CancellationRegistration::CancellationRegistration() noexcept = default; 61 : : 62 : 795 : CancellationRegistration::~CancellationRegistration() { reset(); } 63 : : 64 : 47 : CancellationRegistration::CancellationRegistration(CancellationRegistration&& other) noexcept 65 : 47 : : state_(std::move(other.state_)), id_(other.id_) { 66 : 47 : other.id_ = 0; 67 : 47 : } 68 : : 69 : 63 : CancellationRegistration& CancellationRegistration::operator=(CancellationRegistration&& other) noexcept { 70 [ + + ]: 63 : if VUNLIKELY (this == &other) { 71 : 1 : return *this; 72 : : } 73 : : 74 : 62 : reset(); 75 : : 76 : 62 : state_ = std::move(other.state_); 77 : 62 : id_ = other.id_; 78 : 62 : other.id_ = 0; 79 : : 80 : 62 : return *this; 81 : : } 82 : : 83 : 871 : void CancellationRegistration::reset() noexcept { 84 [ + + ]: 871 : if VUNLIKELY (id_ == 0) { 85 : 757 : return; 86 : : } 87 : : 88 : 114 : auto state = state_.lock(); 89 : : 90 [ + - ]: 114 : if VLIKELY (state) { 91 : 114 : std::lock_guard lock(state->mtx); 92 : : 93 : 114 : state->callbacks.erase(id_); 94 : 114 : } 95 : : 96 : 114 : id_ = 0; 97 : 114 : state_.reset(); 98 : 114 : } 99 : : 100 : 33 : bool CancellationRegistration::valid() const noexcept { 101 [ + + ]: 33 : if VUNLIKELY (id_ == 0) { 102 : 9 : return false; 103 : : } 104 : : 105 : 24 : auto state = state_.lock(); 106 : : 107 [ + - ]: 24 : if VLIKELY (state) { 108 : 24 : std::lock_guard lock(state->mtx); 109 : : 110 : 24 : return state->callbacks.find(id_) != state->callbacks.end(); 111 : 24 : } 112 : : 113 : 0 : return false; 114 : 24 : } 115 : : 116 : 114 : CancellationRegistration::CancellationRegistration(std::weak_ptr<State> state, size_t id) noexcept 117 : 114 : : state_(std::move(state)), id_(id) {} 118 : : 119 : : // CancellationToken 120 : 60 : CancellationToken::CancellationToken() noexcept = default; 121 : : 122 : 64 : bool CancellationToken::valid() const noexcept { return state_ != nullptr; } 123 : : 124 : 5105 : bool CancellationToken::is_cancellation_requested() const noexcept { 125 [ + + ]: 5105 : if VUNLIKELY (!state_) { 126 : 2 : return false; 127 : : } 128 : : 129 : 5067 : std::lock_guard lock(state_->mtx); 130 : : 131 : 5133 : return state_->cancelled; 132 : 5133 : } 133 : : 134 : 559 : CancellationRegistration CancellationToken::register_callback(MoveFunction<void()>&& callback) const { 135 [ + + + + : 559 : if VUNLIKELY (!state_ || !callback) { + + ] 136 : 2 : return {}; 137 : : } 138 : : 139 : : { 140 [ + - ]: 558 : std::lock_guard lock(state_->mtx); 141 : : 142 [ + + ]: 566 : if VLIKELY (!state_->cancelled) { 143 : 114 : const size_t id = state_->next_id++; 144 : : 145 [ + - ]: 114 : state_->callbacks.emplace(id, std::move(callback)); 146 : : 147 : 114 : return CancellationRegistration{state_, id}; 148 : : } 149 [ + + ]: 566 : } 150 : : 151 : 452 : invoke_cancellation_callback(callback); 152 : : 153 : 451 : return {}; 154 : : } 155 : : 156 : 69 : void CancellationToken::throw_if_cancellation_requested() const { 157 [ + + ]: 69 : if VUNLIKELY (is_cancellation_requested()) { 158 : 66 : throw Exception::OperationCancelled{}; 159 : : } 160 : 3 : } 161 : : 162 : 206 : CancellationToken::CancellationToken(std::shared_ptr<State> state) noexcept : state_(std::move(state)) {} 163 : : 164 : : // CancellationSource 165 : 91 : CancellationSource::CancellationSource() : state_(MemoryResource::make_shared<State>()) {} 166 : : 167 : 206 : CancellationToken CancellationSource::token() const noexcept { return CancellationToken{state_}; } 168 : : 169 : 89 : bool CancellationSource::is_cancellation_requested() const noexcept { return token().is_cancellation_requested(); } 170 : : 171 : 44 : bool CancellationSource::request_cancel() const { 172 : : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE 173 [ + - ]: 44 : std::pmr::vector<MoveFunction<void()>> callbacks(&MemoryResource::global_instance()); 174 : : #else 175 : : std::vector<MoveFunction<void()>> callbacks; 176 : : #endif 177 : : 178 : : { 179 [ + - ]: 44 : std::lock_guard lock(state_->mtx); 180 : : 181 [ + + ]: 44 : if VUNLIKELY (state_->cancelled) { 182 : 2 : return false; 183 : : } 184 : : 185 : 42 : state_->cancelled = true; 186 : : 187 [ + - ]: 42 : callbacks.reserve(state_->callbacks.size()); 188 : : 189 [ + + ]: 82 : for (auto& [id, callback] : state_->callbacks) { 190 : : (void)id; 191 : : 192 [ + - ]: 40 : callbacks.emplace_back(std::move(callback)); 193 : : } 194 : : 195 : 42 : state_->callbacks.clear(); 196 [ + + ]: 44 : } 197 : : 198 [ + + ]: 82 : for (auto& callback : callbacks) { 199 : 40 : invoke_cancellation_callback(callback); 200 : : } 201 : : 202 : 42 : return true; 203 : 44 : } 204 : : 205 : : } // namespace vlink