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 : 540 : [[maybe_unused]] static void invoke_cancellation_callback(MoveFunction<void()>& callback) noexcept { 38 [ - + ]: 540 : if VUNLIKELY (!callback) { 39 : 0 : return; 40 : : } 41 : : 42 : : try { 43 [ + + ]: 540 : 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 : 41812 : CancellationRegistration::CancellationRegistration() noexcept = default; 61 : : 62 : 41927 : CancellationRegistration::~CancellationRegistration() { reset(); } 63 : : 64 : 50 : CancellationRegistration::CancellationRegistration(CancellationRegistration&& other) noexcept 65 : 50 : : state_(std::move(other.state_)), id_(other.id_) { 66 : 50 : other.id_ = 0; 67 : 50 : } 68 : : 69 : 20653 : CancellationRegistration& CancellationRegistration::operator=(CancellationRegistration&& other) noexcept { 70 [ + + ]: 20653 : if VUNLIKELY (this == &other) { 71 : 1 : return *this; 72 : : } 73 : : 74 : 20652 : reset(); 75 : : 76 : 20652 : state_ = std::move(other.state_); 77 : 20652 : id_ = other.id_; 78 : 20652 : other.id_ = 0; 79 : : 80 : 20652 : return *this; 81 : : } 82 : : 83 : 62595 : void CancellationRegistration::reset() noexcept { 84 [ + + ]: 62595 : if VUNLIKELY (id_ == 0) { 85 : 62530 : return; 86 : : } 87 : : 88 : 65 : auto state = state_.lock(); 89 : 65 : decltype(State::callbacks)::node_type callback; 90 : : 91 [ + - ]: 65 : if VLIKELY (state) { 92 : 65 : std::lock_guard lock(state->mtx); 93 : : 94 : 65 : callback = state->callbacks.extract(id_); 95 : 65 : } 96 : : 97 : 65 : id_ = 0; 98 : 65 : state_.reset(); 99 : 65 : } 100 : : 101 : 34 : bool CancellationRegistration::valid() const noexcept { 102 [ + + ]: 34 : if VUNLIKELY (id_ == 0) { 103 : 10 : return false; 104 : : } 105 : : 106 : 24 : auto state = state_.lock(); 107 : : 108 [ + - ]: 24 : if VLIKELY (state) { 109 : 24 : std::lock_guard lock(state->mtx); 110 : : 111 : 24 : return state->callbacks.find(id_) != state->callbacks.end(); 112 : 24 : } 113 : : 114 : 0 : return false; 115 : 24 : } 116 : : 117 : 65 : CancellationRegistration::CancellationRegistration(std::weak_ptr<State> state, size_t id) noexcept 118 : 65 : : state_(std::move(state)), id_(id) {} 119 : : 120 : : // CancellationToken 121 : 652 : CancellationToken::CancellationToken() noexcept = default; 122 : : 123 : 41273 : bool CancellationToken::valid() const noexcept { return state_ != nullptr; } 124 : : 125 : 36861 : bool CancellationToken::is_cancellation_requested() const noexcept { 126 [ + + ]: 36861 : if VUNLIKELY (!state_) { 127 : 2 : return false; 128 : : } 129 : : 130 : 36842 : std::lock_guard lock(state_->mtx); 131 : : 132 : 36939 : return state_->cancelled; 133 : 36939 : } 134 : : 135 : 565 : CancellationRegistration CancellationToken::register_callback(MoveFunction<void()>&& callback) const { 136 [ + + + + : 565 : if VUNLIKELY (!state_ || !callback) { + + ] 137 : 2 : return {}; 138 : : } 139 : : 140 : : { 141 [ + - ]: 564 : std::lock_guard lock(state_->mtx); 142 : : 143 [ + + ]: 568 : if VLIKELY (!state_->cancelled) { 144 : 65 : const size_t id = state_->next_id++; 145 : : 146 [ + - ]: 65 : state_->callbacks.emplace(id, std::move(callback)); 147 : : 148 : 65 : return CancellationRegistration{state_, id}; 149 : : } 150 [ + + ]: 568 : } 151 : : 152 : 500 : invoke_cancellation_callback(callback); 153 : : 154 : 501 : return {}; 155 : : } 156 : : 157 : 69 : void CancellationToken::throw_if_cancellation_requested() const { 158 [ + + ]: 69 : if VUNLIKELY (is_cancellation_requested()) { 159 : 66 : throw Exception::OperationCancelled{}; 160 : : } 161 : 3 : } 162 : : 163 : 20736 : CancellationToken::CancellationToken(std::shared_ptr<State> state) noexcept : state_(std::move(state)) {} 164 : : 165 : : // CancellationSource 166 : 20683 : CancellationSource::CancellationSource() : state_(MemoryResource::make_shared<State>()) {} 167 : : 168 : 20736 : CancellationToken CancellationSource::token() const noexcept { return CancellationToken{state_}; } 169 : : 170 : 20617 : bool CancellationSource::is_cancellation_requested() const noexcept { return token().is_cancellation_requested(); } 171 : : 172 : 47 : bool CancellationSource::request_cancel() const { 173 [ + + ]: 47 : if VUNLIKELY (!state_) { 174 : 1 : return false; 175 : : } 176 : : 177 : : #ifdef VLINK_ENABLE_BASE_MEMORY_RESOURCE 178 [ + - ]: 46 : std::pmr::vector<MoveFunction<void()>> callbacks(&MemoryResource::global_instance()); 179 : : #else 180 : : std::vector<MoveFunction<void()>> callbacks; 181 : : #endif 182 : : 183 : : { 184 [ + - ]: 46 : std::lock_guard lock(state_->mtx); 185 : : 186 [ + + ]: 46 : if VUNLIKELY (state_->cancelled) { 187 : 2 : return false; 188 : : } 189 : : 190 : 44 : state_->cancelled = true; 191 : : 192 [ + - ]: 44 : callbacks.reserve(state_->callbacks.size()); 193 : : 194 [ + + ]: 84 : for (auto& [id, callback] : state_->callbacks) { 195 : : (void)id; 196 : : 197 [ + - ]: 40 : callbacks.emplace_back(std::move(callback)); 198 : : } 199 : : 200 : 44 : state_->callbacks.clear(); 201 [ + + ]: 46 : } 202 : : 203 [ + + ]: 84 : for (auto& callback : callbacks) { 204 : 40 : invoke_cancellation_callback(callback); 205 : : } 206 : : 207 : 44 : return true; 208 : 46 : } 209 : : 210 : : } // namespace vlink