cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
cancellation.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
7
8#include <atomic>
9#include <chrono>
10#include <condition_variable>
11#include <cstddef>
12#include <functional>
13#include <memory>
14#include <mutex>
15#include <utility>
16#include <vector>
17
18namespace mcp {
19
21
22namespace detail {
23
24class CancellationRegistration;
25
26CancellationRegistration register_cancellation_callback(
27 CancellationToken token, std::function<void()> callback);
28
29} // namespace detail
30
38 std::atomic_bool cancelled{false};
39 std::mutex mutex;
40 std::condition_variable cv;
41 std::size_t next_callback_id = 1;
42 std::vector<std::pair<std::size_t, std::function<void()>>> callbacks;
43};
44
45namespace detail {
46
48 public:
49 CancellationRegistration() = default;
51 CancellationRegistration& operator=(const CancellationRegistration&) = delete;
52
54 : state_(std::move(other.state_)), id_(other.id_) {
55 other.id_ = 0;
56 }
57
58 CancellationRegistration& operator=(
59 CancellationRegistration&& other) noexcept {
60 if (this != &other) {
61 unregister();
62 state_ = std::move(other.state_);
63 id_ = other.id_;
64 other.id_ = 0;
65 }
66 return *this;
67 }
68
69 ~CancellationRegistration() { unregister(); }
70
71 bool active() const noexcept { return state_ != nullptr && id_ != 0; }
72
73 private:
74 friend CancellationRegistration register_cancellation_callback(
75 CancellationToken token, std::function<void()> callback);
76
77 CancellationRegistration(std::shared_ptr<CancellationState> state,
78 std::size_t id)
79 : state_(std::move(state)), id_(id) {}
80
81 void unregister() noexcept {
82 if (!state_ || id_ == 0) {
83 return;
84 }
85 std::lock_guard<std::mutex> lock(state_->mutex);
86 for (auto iter = state_->callbacks.begin(); iter != state_->callbacks.end();
87 ++iter) {
88 if (iter->first == id_) {
89 state_->callbacks.erase(iter);
90 break;
91 }
92 }
93 id_ = 0;
94 state_.reset();
95 }
96
97 std::shared_ptr<CancellationState> state_;
98 std::size_t id_ = 0;
99};
100
101} // namespace detail
102
105 public:
110 static CancellationToken none() { return CancellationToken(nullptr); }
111
114
117 bool cancelled() const noexcept {
118 return state_ != nullptr &&
119 state_->cancelled.load(std::memory_order_acquire);
120 }
121
123 bool cancellable() const noexcept { return state_ != nullptr; }
124
127 bool wait_for_cancel() const {
128 if (!state_) return false;
129 // Fast path: already cancelled
130 if (state_->cancelled.load(std::memory_order_acquire)) return true;
131 std::unique_lock<std::mutex> lock(state_->mutex);
132 state_->cv.wait(lock, [this]() {
133 return state_->cancelled.load(std::memory_order_acquire);
134 });
135 return true;
136 }
137
140 bool wait_for_cancel(std::chrono::milliseconds timeout) const {
141 if (!state_) return false;
142 if (state_->cancelled.load(std::memory_order_acquire)) return true;
143 std::unique_lock<std::mutex> lock(state_->mutex);
144 if (state_->cv.wait_for(lock, timeout, [this]() {
145 return state_->cancelled.load(std::memory_order_acquire);
146 })) {
147 return true;
148 }
149 return false;
150 }
151
154 bool wait_for_cancel(std::chrono::steady_clock::time_point deadline) const {
155 if (!state_) return false;
156 if (state_->cancelled.load(std::memory_order_acquire)) return true;
157 std::unique_lock<std::mutex> lock(state_->mutex);
158 if (state_->cv.wait_until(lock, deadline, [this]() {
159 return state_->cancelled.load(std::memory_order_acquire);
160 })) {
161 return true;
162 }
163 return false;
164 }
165
166 private:
167 friend class CancellationSource;
169 detail::register_cancellation_callback(CancellationToken token,
170 std::function<void()> callback);
171
172 explicit CancellationToken(std::shared_ptr<CancellationState> state)
173 : state_(std::move(state)) {}
174
175 std::shared_ptr<CancellationState> state_;
176};
177
178namespace detail {
179
180inline CancellationRegistration register_cancellation_callback(
181 CancellationToken token, std::function<void()> callback) {
182 if (!callback || !token.state_) {
183 return CancellationRegistration{};
184 }
185
186 bool run_now = false;
187 std::size_t id = 0;
188 {
189 std::lock_guard<std::mutex> lock(token.state_->mutex);
190 if (token.state_->cancelled.load(std::memory_order_acquire)) {
191 run_now = true;
192 } else {
193 id = token.state_->next_callback_id++;
194 token.state_->callbacks.emplace_back(id, std::move(callback));
195 }
196 }
197
198 if (run_now) {
199 try {
200 callback();
201 } catch (...) {
202 }
203 return CancellationRegistration{};
204 }
205
206 return CancellationRegistration{std::move(token.state_), id};
207}
208
209} // namespace detail
210
213 public:
214 CancellationSource() : state_(std::make_shared<CancellationState>()) {}
215
217 CancellationToken token() const noexcept { return CancellationToken(state_); }
218
222 void cancel() const noexcept {
223 if (state_ != nullptr) {
224 const bool already_cancelled =
225 state_->cancelled.exchange(true, std::memory_order_acq_rel);
226 std::vector<std::function<void()>> callbacks;
227 if (!already_cancelled) {
228 std::lock_guard<std::mutex> lock(state_->mutex);
229 for (auto& callback : state_->callbacks) {
230 callbacks.push_back(std::move(callback.second));
231 }
232 state_->callbacks.clear();
233 }
234 state_->cv.notify_all();
235 for (auto& callback : callbacks) {
236 try {
237 callback();
238 } catch (...) {
239 }
240 }
241 }
242 }
243
245 bool cancelled() const noexcept {
246 return state_ != nullptr &&
247 state_->cancelled.load(std::memory_order_acquire);
248 }
249
250 private:
251 std::shared_ptr<CancellationState> state_;
252};
253
254} // namespace mcp
Owner side of a cooperative cancellation token.
Definition cancellation.hpp:212
CancellationToken token() const noexcept
Returns a token sharing this source's cancellation state.
Definition cancellation.hpp:217
bool cancelled() const noexcept
Returns true when cancellation has been requested.
Definition cancellation.hpp:245
void cancel() const noexcept
Requests cancellation for all tokens created from this source.
Definition cancellation.hpp:222
Copyable token observed by cancellation-aware SDK operations.
Definition cancellation.hpp:104
static CancellationToken none()
Constructs a detached token that is never cancelled.
Definition cancellation.hpp:110
bool wait_for_cancel(std::chrono::steady_clock::time_point deadline) const
Block until cancellation is requested or deadline is reached.
Definition cancellation.hpp:154
bool wait_for_cancel() const
Block until cancellation is requested.
Definition cancellation.hpp:127
CancellationToken()
Constructs a detached token (same as none()).
Definition cancellation.hpp:113
bool wait_for_cancel(std::chrono::milliseconds timeout) const
Block until cancellation is requested or timeout expires.
Definition cancellation.hpp:140
bool cancellable() const noexcept
Returns true when this token is attached to a cancellation source.
Definition cancellation.hpp:123
bool cancelled() const noexcept
Returns true after the associated source has requested cancellation.
Definition cancellation.hpp:117
Definition cancellation.hpp:47
mcp::CancellationToken CancellationToken
Copyable cooperative cancellation token for server handlers.
Definition context.hpp:22
Shared state backing a cancellation token/source pair.
Definition cancellation.hpp:37