Skip to content

Commit 4f9dcc7

Browse files
committed
Fixes async_exec terminal cancellation.
1 parent ad5dd8c commit 4f9dcc7

File tree

3 files changed

+22
-16
lines changed

3 files changed

+22
-16
lines changed

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,16 @@ that ensures that one operation is cancelled as soon as the other
4747
completes, these functions play the following roles
4848

4949
* `connection::async_exec`: Execute commands by writing the request payload to the underlying stream and reading the response sent back by Redis. It can be called from multiple places in your code concurrently.
50-
* `connection::async_run`: Coordinate the low-level IO (read and write) operations and remains suspended until the connection is lost.
51-
52-
When a connection is lost, the `async_exec` calls won't automatically
53-
fail, instead, they will remain suspended until they are either all
54-
canceled with a call to `connection::cancel(operation::exec)` or a new
55-
connection is established and `async_run` is called again, in which
56-
case they will be resent automatically. Users can customise this
57-
behaviour by carefully choosing the values of
50+
* `connection::async_run`: Coordinate the low-level IO (read and write) operations. It remains suspended until the connection is lost.
51+
52+
The `async_exec` calls won't automatically fail when the connection is
53+
lost, instead, they will remain suspended until either
54+
`connection::cancel(operation::exec)` is called to cancel them all or
55+
a new connection is established and `async_run` is called again.
56+
Users can customise the desired behaviour by carefully choosing
5857
`aedis::resp3::request::config`. The role played by `async_run`
59-
becomes clearer with long-lived connections, which we will cover
60-
in the next section.
58+
becomes clearer with long-lived connections, which we will cover in
59+
the next section.
6160

6261
<a name="connection"></a>
6362
## Connection

include/aedis/detail/connection_ops.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,18 @@ struct exec_op {
139139

140140
if (is_cancelled(self)) {
141141
if (info->is_written()) {
142-
self.get_cancellation_state().clear();
143-
goto EXEC_OP_WAIT; // Too late, can't cancel.
142+
if (self.get_cancellation_state().cancelled() == boost::asio::cancellation_type_t::terminal) {
143+
// Cancellation requires closing the connection
144+
// otherwise it stays in inconsistent state.
145+
conn->cancel(operation::run);
146+
return self.complete(ec, 0);
147+
} else {
148+
// Can't implement other cancelation types, ignoring.
149+
self.get_cancellation_state().clear();
150+
goto EXEC_OP_WAIT;
151+
}
144152
} else {
153+
// Cancelation can be honored.
145154
conn->remove_request(info);
146155
self.complete(ec, 0);
147156
return;

tests/conn_exec_cancel.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ auto ignore_implicit_cancel_of_req_written() -> net::awaitable<void>
110110
// Will be cancelled before it is written.
111111
resp3::request req2;
112112
req2.get_config().coalesce = false;
113-
//req2.get_config().cancel_on_connection_lost = true;
113+
req2.get_config().cancel_on_connection_lost = true;
114114
req2.push("PING");
115115

116116
net::steady_timer st{ex};
@@ -123,11 +123,9 @@ auto ignore_implicit_cancel_of_req_written() -> net::awaitable<void>
123123
st.async_wait(redir(ec3))
124124
);
125125

126-
BOOST_TEST(!ec1);
126+
BOOST_CHECK_EQUAL(ec1, net::error::basic_errors::operation_aborted);
127127
BOOST_CHECK_EQUAL(ec2, net::error::basic_errors::operation_aborted);
128128
BOOST_TEST(!ec3);
129-
130-
conn->cancel(operation::run);
131129
}
132130

133131
auto cancel_of_req_written_on_run_canceled() -> net::awaitable<void>

0 commit comments

Comments
 (0)