File indexing completed on 2026-08-17 09:00:18
0001
0002
0003
0004
0005
0006
0007 #include <boost/redis/detail/multiplexer.hpp>
0008 #include <boost/redis/ignore.hpp>
0009 #include <boost/redis/request.hpp>
0010
0011 #include <boost/asio/error.hpp>
0012 #include <boost/assert.hpp>
0013
0014 #include <cstddef>
0015 #include <memory>
0016
0017 namespace boost::redis::detail {
0018
0019 multiplexer::elem::elem(request const& req, any_adapter adapter)
0020 : req_{&req}
0021 , adapter_{std::move(adapter)}
0022 , remaining_responses_{req.get_expected_responses()}
0023 , status_{status::waiting}
0024 , ec_{}
0025 , read_size_{0}
0026 { }
0027
0028 auto multiplexer::elem::notify_error(system::error_code ec) noexcept -> void
0029 {
0030 if (!ec_) {
0031 ec_ = ec;
0032 }
0033
0034 notify_done();
0035 }
0036
0037 auto multiplexer::elem::commit_response(std::size_t read_size) -> void
0038 {
0039 read_size_ += read_size;
0040 --remaining_responses_;
0041 }
0042
0043 void multiplexer::elem::mark_abandoned()
0044 {
0045 req_ = nullptr;
0046 adapter_ = any_adapter();
0047 set_done_callback([] { });
0048 }
0049
0050 multiplexer::multiplexer()
0051 {
0052
0053
0054 read_buffer_.reserve(4096u);
0055 }
0056
0057 void multiplexer::cancel(std::shared_ptr<elem> const& ptr)
0058 {
0059 if (ptr->is_waiting()) {
0060
0061 reqs_.erase(std::remove(std::begin(reqs_), std::end(reqs_), ptr));
0062 } else {
0063
0064
0065 ptr->mark_abandoned();
0066 }
0067 }
0068
0069 bool multiplexer::commit_write(std::size_t bytes_written)
0070 {
0071 BOOST_ASSERT(!cancel_run_called_);
0072 BOOST_ASSERT(bytes_written + write_offset_ <= write_buffer_.size());
0073
0074 usage_.bytes_sent += bytes_written;
0075 write_offset_ += bytes_written;
0076
0077
0078 if (write_offset_ < write_buffer_.size())
0079 return false;
0080
0081
0082 write_buffer_.clear();
0083
0084
0085
0086 std::for_each(std::begin(reqs_), std::end(reqs_), [](auto const& ptr) {
0087 BOOST_ASSERT_MSG(ptr != nullptr, "Expects non-null pointer.");
0088 if (ptr->is_staged()) {
0089 ptr->mark_written();
0090 }
0091 });
0092
0093 release_push_requests();
0094
0095 return true;
0096 }
0097
0098 void multiplexer::add(std::shared_ptr<elem> const& info)
0099 {
0100 BOOST_ASSERT(!info->is_abandoned());
0101
0102 reqs_.push_back(info);
0103
0104 if (request_access::has_priority(info->get_request())) {
0105 auto rend = std::partition_point(std::rbegin(reqs_), std::rend(reqs_), [](auto const& e) {
0106 return e->is_waiting();
0107 });
0108
0109 std::rotate(std::rbegin(reqs_), std::rbegin(reqs_) + 1, rend);
0110 }
0111 }
0112
0113 consume_result multiplexer::consume_impl(system::error_code& ec)
0114 {
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126 auto const data = read_buffer_.get_commited();
0127 BOOST_ASSERT(!data.empty());
0128
0129 if (!on_push_)
0130 on_push_ = is_next_push(data);
0131
0132 if (on_push_) {
0133 if (!resp3::parse(parser_, data, receive_adapter_, ec))
0134 return consume_result::needs_more;
0135
0136 return consume_result::got_push;
0137 }
0138
0139 BOOST_ASSERT(!reqs_.empty());
0140 BOOST_ASSERT(reqs_.front() != nullptr);
0141 BOOST_ASSERT(reqs_.front()->get_remaining_responses() != 0);
0142 BOOST_ASSERT(!reqs_.front()->is_waiting());
0143
0144 if (!resp3::parse(parser_, data, reqs_.front()->get_adapter(), ec))
0145 return consume_result::needs_more;
0146
0147 if (ec) {
0148 reqs_.front()->notify_error(ec);
0149 reqs_.pop_front();
0150 return consume_result::got_response;
0151 }
0152
0153 reqs_.front()->commit_response(parser_.get_consumed());
0154 if (reqs_.front()->get_remaining_responses() == 0) {
0155
0156 reqs_.front()->notify_done();
0157 reqs_.pop_front();
0158 }
0159
0160 return consume_result::got_response;
0161 }
0162
0163 std::pair<consume_result, std::size_t> multiplexer::consume(system::error_code& ec)
0164 {
0165 BOOST_ASSERT(!cancel_run_called_);
0166
0167 auto const ret = consume_impl(ec);
0168 auto const consumed = parser_.get_consumed();
0169 if (ec) {
0170 return std::make_pair(ret, consumed);
0171 }
0172
0173 if (ret != consume_result::needs_more) {
0174 parser_.reset();
0175 auto const res = read_buffer_.consume(consumed);
0176 commit_usage(ret == consume_result::got_push, res);
0177 return std::make_pair(ret, res.consumed);
0178 }
0179
0180 return std::make_pair(consume_result::needs_more, consumed);
0181 }
0182
0183 auto multiplexer::prepare_read() noexcept -> system::error_code { return read_buffer_.prepare(); }
0184
0185 auto multiplexer::get_prepared_read_buffer() noexcept -> read_buffer::span_type
0186 {
0187 return read_buffer_.get_prepared();
0188 }
0189
0190 void multiplexer::commit_read(std::size_t bytes_read) { read_buffer_.commit(bytes_read); }
0191
0192 auto multiplexer::get_read_buffer_size() const noexcept -> std::size_t
0193 {
0194 return read_buffer_.get_commited().size();
0195 }
0196
0197 void multiplexer::reset()
0198 {
0199 read_buffer_.clear();
0200 write_buffer_.clear();
0201 write_offset_ = 0u;
0202 parser_.reset();
0203 on_push_ = false;
0204 cancel_run_called_ = false;
0205 }
0206
0207 std::size_t multiplexer::prepare_write()
0208 {
0209 BOOST_ASSERT(!cancel_run_called_);
0210
0211
0212
0213 auto const point = std::partition_point(
0214 std::cbegin(reqs_),
0215 std::cend(reqs_),
0216 [](auto const& ri) {
0217 return !ri->is_waiting();
0218 });
0219
0220 std::for_each(point, std::cend(reqs_), [this](const std::shared_ptr<elem>& ri) {
0221
0222 BOOST_ASSERT(!ri->is_abandoned());
0223 write_buffer_ += ri->get_request().payload();
0224 ri->mark_staged();
0225 usage_.commands_sent += ri->get_request().get_commands();
0226 });
0227
0228 write_offset_ = 0u;
0229
0230 auto const d = std::distance(point, std::cend(reqs_));
0231 return static_cast<std::size_t>(d);
0232 }
0233
0234 std::size_t multiplexer::cancel_waiting()
0235 {
0236 auto f = [](auto const& ptr) {
0237 BOOST_ASSERT(ptr != nullptr);
0238 return !ptr->is_waiting();
0239 };
0240
0241 auto point = std::stable_partition(std::begin(reqs_), std::end(reqs_), f);
0242
0243 auto const ret = std::distance(point, std::end(reqs_));
0244
0245 std::for_each(point, std::end(reqs_), [](auto const& ptr) {
0246 ptr->notify_error({asio::error::operation_aborted});
0247 });
0248
0249 reqs_.erase(point, std::end(reqs_));
0250 return ret;
0251 }
0252
0253 void multiplexer::cancel_on_conn_lost()
0254 {
0255
0256
0257 BOOST_ASSERT(!cancel_run_called_);
0258 cancel_run_called_ = true;
0259
0260
0261 auto cond = [](const std::shared_ptr<elem>& ptr) {
0262 BOOST_ASSERT(ptr != nullptr);
0263
0264
0265
0266 if (ptr->is_abandoned())
0267 return false;
0268
0269 if (ptr->is_waiting()) {
0270 return !ptr->get_request().get_config().cancel_on_connection_lost;
0271 } else {
0272 return !ptr->get_request().get_config().cancel_if_unresponded;
0273 }
0274 };
0275
0276 auto point = std::stable_partition(std::begin(reqs_), std::end(reqs_), cond);
0277
0278 std::for_each(point, std::end(reqs_), [](auto const& ptr) {
0279 ptr->notify_error({asio::error::operation_aborted});
0280 });
0281
0282 reqs_.erase(point, std::end(reqs_));
0283
0284 std::for_each(std::begin(reqs_), std::end(reqs_), [](auto const& ptr) {
0285 return ptr->mark_waiting();
0286 });
0287 }
0288
0289 void multiplexer::commit_usage(bool is_push, read_buffer::consume_result res)
0290 {
0291 if (is_push) {
0292 usage_.pushes_received += 1;
0293 usage_.push_bytes_received += res.consumed;
0294 on_push_ = false;
0295 } else {
0296 usage_.responses_received += 1;
0297 usage_.response_bytes_received += res.consumed;
0298 }
0299
0300 usage_.bytes_rotated += res.rotated;
0301 }
0302
0303 bool multiplexer::is_next_push(std::string_view data) const noexcept
0304 {
0305
0306
0307
0308
0309
0310
0311
0312 BOOST_ASSERT(!data.empty());
0313 if (resp3::to_type(data.front()) == resp3::type::push)
0314 return true;
0315
0316
0317
0318
0319
0320
0321
0322
0323 if (reqs_.empty())
0324 return true;
0325
0326
0327
0328 if (reqs_.front()->get_remaining_responses() == 0)
0329 return true;
0330
0331
0332
0333
0334
0335
0336 return reqs_.front()->is_waiting();
0337 }
0338
0339 void multiplexer::release_push_requests()
0340 {
0341 auto point = std::stable_partition(
0342 std::begin(reqs_),
0343 std::end(reqs_),
0344 [](const std::shared_ptr<elem>& ptr) {
0345 return !(ptr->is_written() && ptr->get_remaining_responses() == 0u);
0346 });
0347
0348 std::for_each(point, std::end(reqs_), [](auto const& ptr) {
0349 ptr->notify_done();
0350 });
0351
0352 reqs_.erase(point, std::end(reqs_));
0353 }
0354
0355 void multiplexer::set_receive_adapter(any_adapter adapter)
0356 {
0357 receive_adapter_ = std::move(adapter);
0358 }
0359
0360 void multiplexer::set_config(config const& cfg)
0361 {
0362 read_buffer_.set_config({cfg.read_buffer_append_size, cfg.max_read_size});
0363 }
0364
0365 auto make_elem(request const& req, any_adapter adapter) -> std::shared_ptr<multiplexer::elem>
0366 {
0367 return std::make_shared<multiplexer::elem>(req, std::move(adapter));
0368 }
0369
0370 }