1414#ifndef LIBRADOS_ASIO_H
1515#define LIBRADOS_ASIO_H
1616
17+ #include < boost/asio/associated_cancellation_slot.hpp>
18+ #include < boost/asio/cancellation_type.hpp>
19+
1720#include " include/rados/librados.hpp"
1821#include " common/async/completion.h"
1922#include " librados/AioCompletionImpl.h"
@@ -74,6 +77,7 @@ struct Invoker<void> {
7477template <typename Result>
7578struct AsyncOp : Invoker<Result> {
7679 unique_aio_completion_ptr aio_completion;
80+ boost::asio::cancellation_slot slot;
7781
7882 using Signature = typename Invoker<Result>::Signature;
7983 using Completion = ceph::async::Completion<Signature, AsyncOp<Result>>;
@@ -83,6 +87,7 @@ struct AsyncOp : Invoker<Result> {
8387 auto p = std::unique_ptr<Completion>{static_cast <Completion*>(arg)};
8488 // move result out of Completion memory being freed
8589 auto op = std::move (p->user_data );
90+ op.slot .clear (); // clear our cancellation handler
8691 // access AioCompletionImpl directly to avoid locking
8792 const librados::AioCompletionImpl* pc = op.aio_completion ->pc ;
8893 const int ret = pc->rval ;
@@ -94,11 +99,46 @@ struct AsyncOp : Invoker<Result> {
9499 op.dispatch (std::move (p), ec, ver);
95100 }
96101
102+ struct op_cancellation {
103+ AioCompletion* completion = nullptr ;
104+ bool is_read = false ;
105+
106+ void operator ()(boost::asio::cancellation_type type) {
107+ if (completion == nullptr ) {
108+ return ; // no AioCompletion attached
109+ } else if (type == boost::asio::cancellation_type::none) {
110+ return ; // no cancellation requested
111+ } else if (is_read) {
112+ // read operations produce no side effects, so can satisfy the
113+ // requirements of 'total' cancellation. the weaker requirements
114+ // of 'partial' and 'terminal' are also satisfied
115+ completion->cancel ();
116+ } else if (type == boost::asio::cancellation_type::terminal) {
117+ // write operations only support 'terminal' cancellation because we
118+ // can't guarantee that no osd has succeeded (or will succeed) in
119+ // applying the write
120+ completion->cancel ();
121+ }
122+ }
123+ };
124+
97125 template <typename Executor1, typename CompletionHandler>
98- static auto create (const Executor1& ex1, CompletionHandler&& handler) {
126+ static auto create (const Executor1& ex1, bool is_read,
127+ CompletionHandler&& handler) {
128+ op_cancellation* cancel_handler = nullptr ;
129+ auto slot = boost::asio::get_associated_cancellation_slot (handler);
130+ if (slot.is_connected ()) {
131+ cancel_handler = &slot.template emplace <op_cancellation>();
132+ }
133+
99134 auto p = Completion::create (ex1, std::move (handler));
100135 p->user_data .aio_completion .reset (
101136 Rados::aio_create_completion (p.get (), aio_dispatch));
137+ if (cancel_handler) {
138+ cancel_handler->completion = p->user_data .aio_completion .get ();
139+ cancel_handler->is_read = is_read;
140+ p->user_data .slot = std::move (slot);
141+ }
102142 return p;
103143 }
104144};
@@ -108,6 +148,9 @@ struct AsyncOp : Invoker<Result> {
108148
109149// / Calls IoCtx::aio_read() and arranges for the AioCompletion to call a
110150// / given handler with signature (error_code, version_t, bufferlist).
151+ // /
152+ // / The given IoCtx reference is not required to remain valid, but some IoCtx
153+ // / instance must preserve its underlying implementation until completion.
111154template <typename ExecutionContext, typename CompletionToken>
112155auto async_read (ExecutionContext& ctx, IoCtx& io, const std::string& oid,
113156 size_t len, uint64_t off, CompletionToken&& token)
@@ -117,7 +160,8 @@ auto async_read(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
117160 return boost::asio::async_initiate<CompletionToken, Signature>(
118161 [] (auto handler, auto ex, IoCtx& io, const std::string& oid,
119162 size_t len, uint64_t off) {
120- auto p = Op::create (ex, std::move (handler));
163+ constexpr bool is_read = true ;
164+ auto p = Op::create (ex, is_read, std::move (handler));
121165 auto & op = p->user_data ;
122166
123167 int ret = io.aio_read (oid, op.aio_completion .get (), &op.result , len, off);
@@ -132,6 +176,9 @@ auto async_read(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
132176
133177// / Calls IoCtx::aio_write() and arranges for the AioCompletion to call a
134178// / given handler with signature (error_code, version_t).
179+ // /
180+ // / The given IoCtx reference is not required to remain valid, but some IoCtx
181+ // / instance must preserve its underlying implementation until completion.
135182template <typename ExecutionContext, typename CompletionToken>
136183auto async_write (ExecutionContext& ctx, IoCtx& io, const std::string& oid,
137184 const bufferlist &bl, size_t len, uint64_t off,
@@ -142,7 +189,8 @@ auto async_write(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
142189 return boost::asio::async_initiate<CompletionToken, Signature>(
143190 [] (auto handler, auto ex, IoCtx& io, const std::string& oid,
144191 const bufferlist &bl, size_t len, uint64_t off) {
145- auto p = Op::create (ex, std::move (handler));
192+ constexpr bool is_read = false ;
193+ auto p = Op::create (ex, is_read, std::move (handler));
146194 auto & op = p->user_data ;
147195
148196 int ret = io.aio_write (oid, op.aio_completion .get (), bl, len, off);
@@ -157,6 +205,9 @@ auto async_write(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
157205
158206// / Calls IoCtx::aio_operate() and arranges for the AioCompletion to call a
159207// / given handler with signature (error_code, version_t, bufferlist).
208+ // /
209+ // / The given IoCtx reference is not required to remain valid, but some IoCtx
210+ // / instance must preserve its underlying implementation until completion.
160211template <typename ExecutionContext, typename CompletionToken>
161212auto async_operate (ExecutionContext& ctx, IoCtx& io, const std::string& oid,
162213 ObjectReadOperation *read_op, int flags,
@@ -167,7 +218,8 @@ auto async_operate(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
167218 return boost::asio::async_initiate<CompletionToken, Signature>(
168219 [] (auto handler, auto ex, IoCtx& io, const std::string& oid,
169220 ObjectReadOperation *read_op, int flags) {
170- auto p = Op::create (ex, std::move (handler));
221+ constexpr bool is_read = true ;
222+ auto p = Op::create (ex, is_read, std::move (handler));
171223 auto & op = p->user_data ;
172224
173225 int ret = io.aio_operate (oid, op.aio_completion .get (), read_op,
@@ -183,6 +235,9 @@ auto async_operate(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
183235
184236// / Calls IoCtx::aio_operate() and arranges for the AioCompletion to call a
185237// / given handler with signature (error_code, version_t).
238+ // /
239+ // / The given IoCtx reference is not required to remain valid, but some IoCtx
240+ // / instance must preserve its underlying implementation until completion.
186241template <typename ExecutionContext, typename CompletionToken>
187242auto async_operate (ExecutionContext& ctx, IoCtx& io, const std::string& oid,
188243 ObjectWriteOperation *write_op, int flags,
@@ -194,7 +249,8 @@ auto async_operate(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
194249 [] (auto handler, auto ex, IoCtx& io, const std::string& oid,
195250 ObjectWriteOperation *write_op, int flags,
196251 const jspan_context* trace_ctx) {
197- auto p = Op::create (ex, std::move (handler));
252+ constexpr bool is_read = false ;
253+ auto p = Op::create (ex, is_read, std::move (handler));
198254 auto & op = p->user_data ;
199255
200256 int ret = io.aio_operate (oid, op.aio_completion .get (), write_op, flags, trace_ctx);
@@ -209,6 +265,9 @@ auto async_operate(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
209265
210266// / Calls IoCtx::aio_notify() and arranges for the AioCompletion to call a
211267// / given handler with signature (error_code, version_t, bufferlist).
268+ // /
269+ // / The given IoCtx reference is not required to remain valid, but some IoCtx
270+ // / instance must preserve its underlying implementation until completion.
212271template <typename ExecutionContext, typename CompletionToken>
213272auto async_notify (ExecutionContext& ctx, IoCtx& io, const std::string& oid,
214273 bufferlist& bl, uint64_t timeout_ms, CompletionToken &&token)
@@ -218,7 +277,8 @@ auto async_notify(ExecutionContext& ctx, IoCtx& io, const std::string& oid,
218277 return boost::asio::async_initiate<CompletionToken, Signature>(
219278 [] (auto handler, auto ex, IoCtx& io, const std::string& oid,
220279 bufferlist& bl, uint64_t timeout_ms) {
221- auto p = Op::create (ex, std::move (handler));
280+ constexpr bool is_read = false ;
281+ auto p = Op::create (ex, is_read, std::move (handler));
222282 auto & op = p->user_data ;
223283
224284 int ret = io.aio_notify (oid, op.aio_completion .get (),
0 commit comments