Skip to content

Commit 73ad66e

Browse files
committed
Adds example that does not user awaitable ops.
1 parent 9cf00d6 commit 73ad66e

File tree

10 files changed

+416
-312
lines changed

10 files changed

+416
-312
lines changed

CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ if (MSVC)
8282
target_compile_definitions(cpp20_intro PRIVATE _WIN32_WINNT=0x0601)
8383
endif()
8484

85+
add_executable(cpp20_intro_awaitable_ops examples/cpp20_intro_awaitable_ops.cpp)
86+
target_link_libraries(cpp20_intro_awaitable_ops common)
87+
target_compile_features(cpp20_intro_awaitable_ops PUBLIC cxx_std_20)
88+
add_test(cpp20_intro_awaitable_ops cpp20_intro_awaitable_ops)
89+
if (MSVC)
90+
target_compile_options(cpp20_intro_awaitable_ops PRIVATE /bigobj)
91+
target_compile_definitions(cpp20_intro_awaitable_ops PRIVATE _WIN32_WINNT=0x0601)
92+
endif()
93+
8594
add_executable(cpp17_intro examples/cpp17_intro.cpp)
8695
target_compile_features(cpp17_intro PUBLIC cxx_std_17)
8796
add_test(cpp17_intro cpp17_intro)
@@ -199,6 +208,14 @@ if (MSVC)
199208
target_compile_definitions(test_conn_exec PRIVATE _WIN32_WINNT=0x0601)
200209
endif()
201210

211+
add_executable(test_conn_exec_retry tests/conn_exec_retry.cpp)
212+
target_compile_features(test_conn_exec_retry PUBLIC cxx_std_20)
213+
add_test(test_conn_exec_retry test_conn_exec_retry)
214+
if (MSVC)
215+
target_compile_options(test_conn_exec_retry PRIVATE /bigobj)
216+
target_compile_definitions(test_conn_exec_retry PRIVATE _WIN32_WINNT=0x0601)
217+
endif()
218+
202219
add_executable(test_conn_push tests/conn_push.cpp)
203220
target_compile_features(test_conn_push PUBLIC cxx_std_20)
204221
add_test(test_conn_push test_conn_push)

README.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ the cases will be concerned with only three library entities
1515

1616
For example, the coroutine below uses a short-lived connection to read Redis
1717
[hashes](https://redis.io/docs/data-types/hashes/)
18-
in a `std::map` (see cpp20_intro.cpp, cpp17_intro.cpp and containers.cpp)
18+
in a `std::map`
1919

2020
```cpp
2121
auto async_main() -> net::awaitable<void>
@@ -41,37 +41,40 @@ auto async_main() -> net::awaitable<void>
4141
}
4242
```
4343

44+
For different versions of this example using different styles see
45+
46+
* cpp20_intro.cpp: Does not use awaitable operators
47+
* cpp20_intro_awaitable_ops.cpp: The version above.
48+
* cpp17_intro.cpp: Requires C++17 only.
49+
* cpp20_intro_tls.cpp: Communicates over TLS.
50+
4451
The execution of `connection::async_exec` above is composed with
4552
`connection::async_run` with the aid of the Asio awaitable `operator ||`
4653
that ensures that one operation is cancelled as soon as the other
4754
completes, these functions play the following roles
4855

4956
* `connection::async_exec`: Execute commands by queuing the request
50-
for writing and wait for the response sent back by Redis. It can be
51-
called from multiple places in your code concurrently.
57+
for writing. It will wait for the response sent back by Redis and
58+
can be called from multiple places in your code concurrently.
5259
* `connection::async_run`: Coordinate low-level read and write
5360
operations. More specifically, it will hand IO control to
5461
`async_exec` when a response arrives, to
55-
`aedis::connection::async_receive` when a server-push is received
62+
`async_receive` when a server-push is received
5663
and will trigger writes of pending requests when a reconnection
5764
occurs.
5865

5966
The role played by `async_run` can be better understood in the context
6067
of long-lived connections, which we will cover in the next section.
61-
Before that however, the reader might want to skim over the examples
68+
Before that however, the reader might want to skim over some further examples
6269

63-
* cpp17_intro.cpp: The Aedis hello-world program. Sends one command and quits the connection.
64-
* cpp17_intro_sync.cpp: Shows how to use the connection class synchronously.
65-
* cpp17_low_level_sync.cpp: Sends a ping synchronously using the low-level API.
66-
* cpp20_intro.cpp: Like cpp17_intro.cpp but uses awaitable operators.
67-
* cpp20_intro_tls.cpp: Same as intro.cpp but over TLS.
6870
* cpp20_containers.cpp: Shows how to send and receive STL containers and how to use transactions.
6971
* cpp20_serialization.cpp: Shows how to serialize types using Boost.Json.
7072
* cpp20_resolve_with_sentinel.cpp: Shows how to resolve a master address using sentinels.
7173
* cpp20_subscriber.cpp: Shows how to implement pubsub with reconnection re-subscription.
7274
* cpp20_echo_server.cpp: A simple TCP echo server.
7375
* cpp20_chat_room.cpp: A command line chat built on Redis pubsub.
7476
* cpp20_low_level_async.cpp: Sends a ping asynchronously using the low-level API.
77+
* cpp17_low_level_sync.cpp: Sends a ping synchronously using the low-level API.
7578

7679
To avoid repetition code that is common to some examples has been
7780
grouped in common.hpp. The main function used in some async examples
@@ -159,7 +162,7 @@ auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
159162
```
160163

161164
The definition of `receiver` and `healthy_checker` above can be found
162-
in subscriber.cpp. Adding a loop around `async_run` produces a simple
165+
in cpp20_subscriber.cpp. Adding a loop around `async_run` produces a simple
163166
way to support reconnection _while there are pending operations on the connection_,
164167
for example, to reconnect to the same address
165168

@@ -187,7 +190,7 @@ auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
187190
}
188191
```
189192
190-
For failover with sentinels see `resolve_with_sentinel.cpp`. At
193+
For failover with sentinels see `cpp20_resolve_with_sentinel.cpp`. At
191194
this point the reasons for why `async_run` was introduced in Aedis
192195
might have become apparent to the reader
193196
@@ -225,7 +228,7 @@ co_await (conn.async_exec(...) || time.async_wait(...))
225228
* The cancellation will be ignored if the request has already
226229
been written to the socket.
227230
* NOTE: It is usually a better idea to have a healthy checker than adding
228-
per request timeout, see subscriber.cpp for an example.
231+
per request timeout, see cpp20_subscriber.cpp for an example.
229232

230233
```cpp
231234
co_await (conn.async_run(...) || time.async_wait(...))
@@ -302,7 +305,7 @@ std::map<std::string, mystruct> map {...};
302305
req.push_range("HSET", "key", map);
303306
```
304307
305-
Example serialization.cpp shows how store json strings in Redis.
308+
Example cpp20_serialization.cpp shows how store json strings in Redis.
306309
307310
<a name="responses"></a>
308311
@@ -1006,7 +1009,7 @@ Acknowledgement to people that helped shape Aedis
10061009

10071010
* `connection::async_receive_event` is now being used to communicate
10081011
internal events to the user, such as resolve, connect, push etc. For
1009-
examples see subscriber.cpp and `connection::event`.
1012+
examples see cpp20_subscriber.cpp and `connection::event`.
10101013

10111014
* The `aedis` directory has been moved to `include` to look more
10121015
similar to Boost libraries. Users should now replace `-I/aedis-path`

examples/cpp17_intro.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ auto main() -> int
4545
auto on_run = [](auto ec)
4646
{
4747
if (ec)
48-
return log(ec, "on_exec: ");
48+
return log(ec, "on_run: ");
4949
};
5050

5151
// async_exec callback.

examples/cpp20_intro.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,19 @@
66

77
#include <boost/asio.hpp>
88
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
9-
#include <boost/asio/experimental/awaitable_operators.hpp>
109
#include <aedis.hpp>
1110
#include "common/common.hpp"
1211

1312
namespace net = boost::asio;
1413
namespace resp3 = aedis::resp3;
15-
using namespace net::experimental::awaitable_operators;
1614
using aedis::adapt;
1715

16+
auto run(std::shared_ptr<connection> conn) -> net::awaitable<void>
17+
{
18+
co_await connect(conn, "127.0.0.1", "6379");
19+
co_await conn->async_run();
20+
}
21+
1822
// Called from the main function (see main.cpp)
1923
auto async_main() -> net::awaitable<void>
2024
{
@@ -25,9 +29,10 @@ auto async_main() -> net::awaitable<void>
2529

2630
std::tuple<aedis::ignore, std::string, aedis::ignore> resp;
2731

28-
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
29-
co_await connect(conn, "127.0.0.1", "6379");
30-
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
32+
auto ex = co_await net::this_coro::executor;
33+
auto conn = std::make_shared<connection>(ex);
34+
net::co_spawn(ex, run(conn), net::detached);
35+
co_await conn->async_exec(req, adapt(resp));
3136

3237
std::cout << "PING: " << std::get<1>(resp) << std::endl;
3338
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva ([email protected])
2+
*
3+
* Distributed under the Boost Software License, Version 1.0. (See
4+
* accompanying file LICENSE.txt)
5+
*/
6+
7+
#include <boost/asio.hpp>
8+
#if defined(BOOST_ASIO_HAS_CO_AWAIT)
9+
#include <boost/asio/experimental/awaitable_operators.hpp>
10+
#include <aedis.hpp>
11+
#include "common/common.hpp"
12+
13+
namespace net = boost::asio;
14+
namespace resp3 = aedis::resp3;
15+
using namespace net::experimental::awaitable_operators;
16+
using aedis::adapt;
17+
18+
// Called from the main function (see main.cpp)
19+
auto async_main() -> net::awaitable<void>
20+
{
21+
resp3::request req;
22+
req.push("HELLO", 3);
23+
req.push("PING", "Hello world");
24+
req.push("QUIT");
25+
26+
std::tuple<aedis::ignore, std::string, aedis::ignore> resp;
27+
28+
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
29+
co_await connect(conn, "127.0.0.1", "6379");
30+
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
31+
32+
std::cout << "PING: " << std::get<1>(resp) << std::endl;
33+
}
34+
35+
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)

include/aedis/resp3/impl/request.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace aedis::resp3::detail {
1111

12-
auto has_push_response(std::string_view cmd) -> bool
12+
auto has_response(std::string_view cmd) -> bool
1313
{
1414
if (cmd == "SUBSCRIBE") return true;
1515
if (cmd == "PSUBSCRIBE") return true;

include/aedis/resp3/request.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ void to_bulk(Request& to, T n)
6262

6363
namespace detail {
6464

65-
auto has_push_response(std::string_view cmd) -> bool;
65+
auto has_response(std::string_view cmd) -> bool;
6666

6767
template <class T>
6868
struct add_bulk_impl {
@@ -386,7 +386,7 @@ class request {
386386
private:
387387
void check_cmd(std::string_view cmd)
388388
{
389-
if (!detail::has_push_response(cmd))
389+
if (!detail::has_response(cmd))
390390
++commands_;
391391

392392
if (cmd == "HELLO")

tests/conn_exec.cpp

Lines changed: 0 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -122,123 +122,3 @@ BOOST_AUTO_TEST_CASE(cancel_request_if_not_connected)
122122

123123
ioc.run();
124124
}
125-
126-
// TODO: This test is broken.
127-
BOOST_AUTO_TEST_CASE(request_retry_false)
128-
{
129-
resp3::request req0;
130-
req0.get_config().coalesce = false;
131-
req0.get_config().cancel_on_connection_lost = true;
132-
req0.push("HELLO", 3);
133-
134-
resp3::request req1;
135-
req1.get_config().coalesce = true;
136-
req1.get_config().cancel_on_connection_lost = true;
137-
req1.push("BLPOP", "any", 0);
138-
139-
resp3::request req2;
140-
req2.get_config().coalesce = true;
141-
req2.get_config().cancel_on_connection_lost = false;
142-
req2.get_config().cancel_if_unresponded = true;
143-
req2.push("PING");
144-
145-
net::io_context ioc;
146-
connection conn{ioc};
147-
148-
net::steady_timer st{ioc};
149-
st.expires_after(std::chrono::seconds{1});
150-
st.async_wait([&](auto){
151-
// Cancels the request before receiving the response. This
152-
// should cause the second request to complete with error
153-
// although it has cancel_on_connection_lost = false.
154-
conn.cancel(aedis::operation::run);
155-
});
156-
157-
auto const endpoints = resolve();
158-
net::connect(conn.next_layer(), endpoints);
159-
160-
conn.async_exec(req0, adapt(), [](auto ec, auto){
161-
BOOST_TEST(!ec);
162-
});
163-
164-
conn.async_exec(req1, adapt(), [](auto ec, auto){
165-
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
166-
});
167-
168-
conn.async_exec(req2, adapt(), [](auto ec, auto){
169-
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
170-
});
171-
172-
conn.async_run([](auto ec){
173-
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
174-
});
175-
176-
ioc.run();
177-
}
178-
179-
BOOST_AUTO_TEST_CASE(request_retry_true)
180-
{
181-
resp3::request req0;
182-
req0.get_config().coalesce = false;
183-
req0.get_config().cancel_on_connection_lost = true;
184-
req0.push("HELLO", 3);
185-
186-
resp3::request req1;
187-
req1.get_config().coalesce = true;
188-
req1.get_config().cancel_on_connection_lost = true;
189-
req1.push("BLPOP", "any", 0);
190-
191-
resp3::request req2;
192-
req2.get_config().coalesce = true;
193-
req2.get_config().cancel_on_connection_lost = false;
194-
req2.get_config().cancel_if_unresponded = false;
195-
req2.push("PING");
196-
197-
resp3::request req3;
198-
req3.get_config().coalesce = true;
199-
req3.get_config().cancel_on_connection_lost = true;
200-
req3.get_config().cancel_if_unresponded = true;
201-
req3.push("QUIT");
202-
203-
net::io_context ioc;
204-
connection conn{ioc};
205-
206-
net::steady_timer st{ioc};
207-
st.expires_after(std::chrono::seconds{1});
208-
st.async_wait([&](auto){
209-
// Cancels the request before receiving the response. This
210-
// should cause the second request to complete with error
211-
// although it has cancel_on_connection_lost = false.
212-
conn.cancel(aedis::operation::run);
213-
});
214-
215-
auto const endpoints = resolve();
216-
net::connect(conn.next_layer(), endpoints);
217-
218-
conn.async_exec(req0, adapt(), [](auto ec, auto){
219-
BOOST_TEST(!ec);
220-
});
221-
222-
conn.async_exec(req1, adapt(), [](auto ec, auto){
223-
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
224-
});
225-
226-
conn.async_exec(req2, adapt(), [&](auto ec, auto){
227-
BOOST_TEST(!ec);
228-
conn.async_exec(req3, adapt(), [&](auto ec, auto){
229-
BOOST_TEST(!ec);
230-
});
231-
});
232-
233-
conn.async_run([&](auto ec){
234-
BOOST_CHECK_EQUAL(ec, boost::system::errc::errc_t::operation_canceled);
235-
conn.reset_stream();
236-
net::connect(conn.next_layer(), endpoints);
237-
conn.async_run([&](auto ec){
238-
std::cout << ec.message() << std::endl;
239-
BOOST_TEST(!ec);
240-
});
241-
});
242-
243-
ioc.run();
244-
}

0 commit comments

Comments
 (0)