Repository navigation
Expand file tree
/
Copy pathcancellation.hpp
More file actions
147 lines (113 loc) · 5.2 KB
/
Copy pathcancellation.hpp
File metadata and controls
147 lines (113 loc) · 5.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
This file is part of libhttpserver
Copyright (C) 2011-2026 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the file
LICENSE in the distribution; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#ifndef SRC_HTTPSERVER_CONCURRENCY_CANCELLATION_HPP_
#define SRC_HTTPSERVER_CONCURRENCY_CANCELLATION_HPP_
// Cancellation primitives of the v3 concurrency core (architecture §3.1,
// PRD-V3N-REQ-025/031). stop_source is the single request point;
// stop_token is the copyable fan-out handle. request_stop() is
// idempotent, never blocks, and returns while the caller is still inside
// a handler: no waiter is ever resumed synchronously inside
// request_stop() — resumptions are posted to each waiter's executor.
//
// co_await token.cancelled() suspends the task until stop is requested;
// it then completes exactly once by throwing the cancelled_exception
// marker, which the task's promise converts into the typed terminal
// outcome http::outcome_code::cancelled (never a propagating error).
#include <atomic>
#include <coroutine>
#include <optional>
#include <stop_token>
#include <utility>
#include <httpserver/concurrency/task.hpp>
namespace httpserver {
class stop_token;
// The single stop-request point. Copying is deliberately removed: one
// exchange, one requester. Tokens fan out by copying from get_token().
class stop_source {
public:
stop_source() = default;
stop_source(stop_source&&) = default;
stop_source& operator=(stop_source&&) = default;
stop_source(const stop_source&) = delete;
stop_source& operator=(const stop_source&) = delete;
// Requests stop. Returns true if this call won the race (first
// request); false afterwards. Idempotent and non-blocking; returns
// while the caller is inside a handler (DR-V3-008: handler-safe stop
// initiation returns immediately).
bool request_stop() noexcept { return source_.request_stop(); }
bool stop_requested() const noexcept { return source_.stop_requested(); }
bool stop_possible() const noexcept { return source_.stop_possible(); }
stop_token get_token() const noexcept;
private:
std::stop_source source_;
};
// Copyable fan-out handle observing a stop_source.
class stop_token {
public:
stop_token() noexcept = default;
bool stop_requested() const noexcept { return token_.stop_requested(); }
bool stop_possible() const noexcept { return token_.stop_possible(); }
// Awaitable completing the awaiting task exactly once with the typed
// cancelled outcome. Completing a pre-cancelled token happens on the
// awaiting frame's own thread (symmetric transfer at registration
// time); completing after a later request_stop() is posted to the
// awaiting frame's executor — never run on the requesting thread.
class cancellation_awaiter final {
public:
explicit cancellation_awaiter(std::stop_token token) noexcept
: token_(std::move(token)) { }
cancellation_awaiter& bind_frame(detail::task_frame_base* frame) noexcept;
bool await_ready() const noexcept { return false; }
std::coroutine_handle<> await_suspend(std::coroutine_handle<> awaiting);
// Resumption only ever happens through a fired path.
void await_resume() const;
~cancellation_awaiter();
private:
struct fire {
cancellation_awaiter* self;
void operator()() const { self->on_stop(); }
};
bool claim() noexcept {
return !fired_.exchange(true, std::memory_order_acq_rel);
}
void on_stop() noexcept;
void post_resume() noexcept;
std::stop_token token_;
std::optional<std::stop_callback<fire>> callback_;
detail::task_frame_base* frame_ = nullptr;
std::coroutine_handle<> awaiting_;
std::atomic<bool> fired_{false};
std::atomic<bool> armed_{false};
// Single-resumer handoff between the registration re-check and the
// stop callback: exactly one exchange wins and resumes the waiter.
std::atomic<bool> resume_claimed_{false};
};
[[nodiscard]] cancellation_awaiter cancelled() const noexcept {
return cancellation_awaiter(token_);
}
private:
friend class stop_source;
explicit stop_token(std::stop_token token) noexcept
: token_(std::move(token)) { }
std::stop_token token_;
};
inline stop_token stop_source::get_token() const noexcept {
return stop_token(source_.get_token());
}
} // namespace httpserver
#endif // SRC_HTTPSERVER_CONCURRENCY_CANCELLATION_HPP_