Skip to content

Commit 56c0b28

Browse files
committed
Fixes issue 50 and 44.
1 parent c88fcfb commit 56c0b28

File tree

11 files changed

+212
-209
lines changed

11 files changed

+212
-209
lines changed

CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,15 @@ if (MSVC)
299299
target_compile_definitions(test_request PRIVATE _WIN32_WINNT=0x0601)
300300
endif()
301301

302+
add_executable(test_issue_50 tests/issue_50.cpp)
303+
target_compile_features(test_issue_50 PUBLIC cxx_std_20)
304+
target_link_libraries(test_issue_50 common)
305+
add_test(test_issue_50 test_issue_50)
306+
if (MSVC)
307+
target_compile_options(test_issue_50 PRIVATE /bigobj)
308+
target_compile_definitions(test_issue_50 PRIVATE _WIN32_WINNT=0x0601)
309+
endif()
310+
302311
# Install
303312
#=======================================================================
304313

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ connection to read Redis
4848
```cpp
4949
auto co_main() -> net::awaitable<void>
5050
{
51-
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
51+
connection conn{co_await net::this_coro::executor};
5252

5353
// From examples/common.hpp to avoid vebosity
5454
co_await connect(conn, "127.0.0.1", "6379");
@@ -65,7 +65,7 @@ auto co_main() -> net::awaitable<void>
6565
std::tuple<ignore, std::map<std::string, std::string>, ignore> resp;
6666

6767
// Executes the request. See below why we are using operator ||.
68-
co_await (conn->async_run() || conn->async_exec(req, adapt(resp)));
68+
co_await (conn.async_run() || conn.async_exec(req, adapt(resp)));
6969
// Use the map from std::get<1>(resp) ...
7070
}
7171
```

include/aedis/adapt.hpp

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,22 +40,13 @@ namespace detail
4040

4141
class ignore_adapter {
4242
public:
43-
explicit ignore_adapter(std::size_t max_read_size) : max_read_size_{max_read_size} {}
44-
4543
void
4644
operator()(
4745
std::size_t, resp3::node<std::string_view> const&, boost::system::error_code&) { }
4846

4947
[[nodiscard]]
5048
auto get_supported_response_size() const noexcept
5149
{ return static_cast<std::size_t>(-1);}
52-
53-
[[nodiscard]]
54-
auto get_max_read_size(std::size_t) const noexcept
55-
{ return max_read_size_;}
56-
57-
private:
58-
std::size_t max_read_size_;
5950
};
6051

6152
template <class Tuple>
@@ -67,11 +58,9 @@ class static_adapter {
6758
using adapters_array_type = std::array<variant_type, size>;
6859

6960
adapters_array_type adapters_;
70-
std::size_t max_read_size_;
7161

7262
public:
73-
explicit static_adapter(Tuple& r, std::size_t max_read_size)
74-
: max_read_size_{max_read_size}
63+
explicit static_adapter(Tuple& r)
7564
{
7665
adapter::detail::assigner<size - 1>::assign(adapters_, r);
7766
}
@@ -80,10 +69,6 @@ class static_adapter {
8069
auto get_supported_response_size() const noexcept
8170
{ return size;}
8271

83-
[[nodiscard]]
84-
auto get_max_read_size(std::size_t) const noexcept
85-
{ return max_read_size_;}
86-
8772
void
8873
operator()(
8974
std::size_t i,
@@ -102,23 +87,17 @@ class vector_adapter {
10287
private:
10388
using adapter_type = typename adapter::detail::response_traits<Vector>::adapter_type;
10489
adapter_type adapter_;
105-
std::size_t max_read_size_;
10690

10791
public:
108-
explicit vector_adapter(Vector& v, std::size_t max_read_size)
92+
explicit vector_adapter(Vector& v)
10993
: adapter_{adapter::adapt2(v)}
110-
, max_read_size_{max_read_size}
11194
{ }
11295

11396
[[nodiscard]]
11497
auto
11598
get_supported_response_size() const noexcept
11699
{ return static_cast<std::size_t>(-1);}
117100

118-
[[nodiscard]]
119-
auto get_max_read_size(std::size_t) const noexcept
120-
{ return max_read_size_;}
121-
122101
void
123102
operator()(
124103
std::size_t,
@@ -137,26 +116,26 @@ struct response_traits<void> {
137116
using response_type = void;
138117
using adapter_type = detail::ignore_adapter;
139118

140-
static auto adapt(std::size_t max_read_size) noexcept
141-
{ return detail::ignore_adapter{max_read_size}; }
119+
static auto adapt() noexcept
120+
{ return detail::ignore_adapter{}; }
142121
};
143122

144123
template <class String, class Allocator>
145124
struct response_traits<std::vector<resp3::node<String>, Allocator>> {
146125
using response_type = std::vector<resp3::node<String>, Allocator>;
147126
using adapter_type = vector_adapter<response_type>;
148127

149-
static auto adapt(response_type& v, std::size_t max_read_size) noexcept
150-
{ return adapter_type{v, max_read_size}; }
128+
static auto adapt(response_type& v) noexcept
129+
{ return adapter_type{v}; }
151130
};
152131

153132
template <class ...Ts>
154133
struct response_traits<std::tuple<Ts...>> {
155134
using response_type = std::tuple<Ts...>;
156135
using adapter_type = static_adapter<response_type>;
157136

158-
static auto adapt(response_type& r, std::size_t max_read_size) noexcept
159-
{ return adapter_type{r, max_read_size}; }
137+
static auto adapt(response_type& r) noexcept
138+
{ return adapter_type{r}; }
160139
};
161140

162141
template <class Adapter>
@@ -171,10 +150,6 @@ class wrapper {
171150
auto get_supported_response_size() const noexcept
172151
{ return adapter_.get_supported_response_size();}
173152

174-
[[nodiscard]]
175-
auto get_max_read_size(std::size_t) const noexcept
176-
{ return adapter_.get_max_read_size(0); }
177-
178153
private:
179154
Adapter adapter_;
180155
};
@@ -192,13 +167,10 @@ auto make_adapter_wrapper(Adapter adapter)
192167
*
193168
* This function can be used to create adapters that ignores
194169
* responses.
195-
*
196-
* @param max_read_size Specifies the maximum size of the read
197-
* buffer.
198170
*/
199-
inline auto adapt(std::size_t max_read_size = (std::numeric_limits<std::size_t>::max)()) noexcept
171+
inline auto adapt() noexcept
200172
{
201-
return detail::response_traits<void>::adapt(max_read_size);
173+
return detail::response_traits<void>::adapt();
202174
}
203175

204176
/** @brief Adapts a type to be used as a response.
@@ -213,13 +185,11 @@ inline auto adapt(std::size_t max_read_size = (std::numeric_limits<std::size_t>:
213185
* and `std::string`.
214186
*
215187
* @param t Tuple containing the responses.
216-
* @param max_read_size Specifies the maximum size of the read
217-
* buffer.
218188
*/
219189
template<class T>
220-
auto adapt(T& t, std::size_t max_read_size = (std::numeric_limits<std::size_t>::max)()) noexcept
190+
auto adapt(T& t) noexcept
221191
{
222-
return detail::response_traits<T>::adapt(t, max_read_size);
192+
return detail::response_traits<T>::adapt(t);
223193
}
224194

225195
} // aedis

include/aedis/connection.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,15 +187,21 @@ class basic_connection :
187187
auto cancel(operation op) -> std::size_t
188188
{ return base_type::cancel(op); }
189189

190+
/// Sets the maximum size of the read buffer.
191+
void set_max_buffer_read_size(std::size_t max_read_size) noexcept
192+
{ base_type::set_max_buffer_read_size(max_read_size); }
193+
190194
private:
191195
using this_type = basic_connection<next_layer_type>;
192196

193197
template <class, class> friend class detail::connection_base;
194198
template <class, class> friend struct detail::exec_read_op;
195199
template <class, class> friend struct detail::exec_op;
200+
template <class, class> friend struct detail::receive_op;
196201
template <class> friend struct detail::reader_op;
197202
template <class> friend struct detail::writer_op;
198203
template <class> friend struct detail::run_op;
204+
template <class> friend struct detail::wait_receive_op;
199205

200206
void close() { stream_.close(); }
201207
auto is_open() const noexcept { return stream_.is_open(); }

include/aedis/detail/connection_base.hpp

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class connection_base {
4747
connection_base(executor_type ex, std::pmr::memory_resource* resource)
4848
: writer_timer_{ex}
4949
, read_timer_{ex}
50-
, guarded_op_{ex}
50+
, channel_{ex}
5151
, read_buffer_{resource}
5252
, write_buffer_{resource}
5353
, reqs_{resource}
@@ -76,7 +76,7 @@ class connection_base {
7676
}
7777
case operation::receive:
7878
{
79-
guarded_op_.cancel();
79+
channel_.cancel();
8080
return 1U;
8181
}
8282
default: BOOST_ASSERT(false); return 0;
@@ -136,7 +136,7 @@ class connection_base {
136136
template <class Adapter, class CompletionToken>
137137
auto async_exec(resp3::request const& req, Adapter adapter, CompletionToken token)
138138
{
139-
BOOST_ASSERT_MSG(req.size() <= adapter.get_supported_response_size(), "Request and adapter have incompatible sizes.");
139+
BOOST_ASSERT_MSG(req.size() <= adapter.get_supported_response_size(), "Request and response have incompatible sizes.");
140140

141141
return boost::asio::async_compose
142142
< CompletionToken
@@ -149,9 +149,10 @@ class connection_base {
149149
{
150150
auto f = detail::make_adapter_wrapper(adapter);
151151

152-
return guarded_op_.async_wait(
153-
resp3::async_read(derived().next_layer(), make_dynamic_buffer(adapter.get_max_read_size(0)), f, boost::asio::deferred),
154-
std::move(token));
152+
return boost::asio::async_compose
153+
< CompletionToken
154+
, void(boost::system::error_code, std::size_t)
155+
>(detail::receive_op<Derived, decltype(f)>{&derived(), f}, token, channel_);
155156
}
156157

157158
template <class CompletionToken>
@@ -163,10 +164,14 @@ class connection_base {
163164
>(detail::run_op<Derived>{&derived()}, token, writer_timer_);
164165
}
165166

167+
void set_max_buffer_read_size(std::size_t max_read_size) noexcept
168+
{max_read_size_ = max_read_size;}
169+
166170
private:
167171
using clock_type = std::chrono::steady_clock;
168172
using clock_traits_type = boost::asio::wait_traits<clock_type>;
169173
using timer_type = boost::asio::basic_waitable_timer<clock_type, clock_traits_type, executor_type>;
174+
using channel_type = boost::asio::experimental::channel<executor_type, void(boost::system::error_code, std::size_t)>;
170175

171176
auto derived() -> Derived& { return static_cast<Derived&>(*this); }
172177

@@ -272,7 +277,17 @@ class connection_base {
272277
template <class> friend struct detail::run_op;
273278
template <class, class> friend struct detail::exec_op;
274279
template <class, class> friend struct detail::exec_read_op;
275-
template <class> friend struct detail::send_receive_op;
280+
template <class, class> friend struct detail::receive_op;
281+
template <class> friend struct detail::wait_receive_op;
282+
283+
template <class CompletionToken>
284+
auto async_wait_receive(CompletionToken token)
285+
{
286+
return boost::asio::async_compose
287+
< CompletionToken
288+
, void(boost::system::error_code)
289+
>(wait_receive_op<Derived>{&derived()}, token, channel_);
290+
}
276291

277292
void cancel_push_requests()
278293
{
@@ -299,12 +314,12 @@ class connection_base {
299314
std::rotate(std::rbegin(reqs_), std::rbegin(reqs_) + 1, rend);
300315
}
301316

302-
if (derived().is_open() && cmds_ == 0 && write_buffer_.empty())
317+
if (derived().is_open() && !is_waiting_response() && write_buffer_.empty())
303318
writer_timer_.cancel();
304319
}
305320

306-
auto make_dynamic_buffer(std::size_t max_read_size = 512)
307-
{ return boost::asio::dynamic_buffer(read_buffer_, max_read_size); }
321+
auto make_dynamic_buffer()
322+
{ return boost::asio::dynamic_buffer(read_buffer_, max_read_size_); }
308323

309324
template <class CompletionToken>
310325
auto reader(CompletionToken&& token)
@@ -336,7 +351,6 @@ class connection_base {
336351
void stage_request(req_info& ri)
337352
{
338353
write_buffer_ += ri.get_request().payload();
339-
cmds_ += ri.get_request().size();
340354
ri.mark_staged();
341355
}
342356

@@ -358,17 +372,22 @@ class connection_base {
358372
}
359373
}
360374

375+
bool is_waiting_response() const noexcept
376+
{
377+
return !std::empty(reqs_) && reqs_.front()->is_written();
378+
}
379+
361380
// Notice we use a timer to simulate a condition-variable. It is
362381
// also more suitable than a channel and the notify operation does
363382
// not suspend.
364383
timer_type writer_timer_;
365384
timer_type read_timer_;
366-
detail::guarded_operation<executor_type> guarded_op_;
385+
channel_type channel_;
367386

368387
std::pmr::string read_buffer_;
369388
std::pmr::string write_buffer_;
370-
std::size_t cmds_ = 0;
371389
reqs_type reqs_;
390+
std::size_t max_read_size_ = (std::numeric_limits<std::size_t>::max)();
372391
};
373392

374393
} // aedis

0 commit comments

Comments
 (0)