Wrapped async_write calls in a strand since these calls might not be safe to call outside of the context runners if tls is used
This commit is contained in:
parent
f28b183f0a
commit
eca0e1d622
2 changed files with 72 additions and 32 deletions
|
|
@ -31,6 +31,7 @@ namespace SimpleWeb {
|
||||||
using io_context = asio::io_context;
|
using io_context = asio::io_context;
|
||||||
using resolver_results = asio::ip::tcp::resolver::results_type;
|
using resolver_results = asio::ip::tcp::resolver::results_type;
|
||||||
using async_connect_endpoint = asio::ip::tcp::endpoint;
|
using async_connect_endpoint = asio::ip::tcp::endpoint;
|
||||||
|
using strand = asio::strand<asio::any_io_executor>;
|
||||||
|
|
||||||
template <typename handler_type>
|
template <typename handler_type>
|
||||||
inline void post(io_context &context, handler_type &&handler) {
|
inline void post(io_context &context, handler_type &&handler) {
|
||||||
|
|
@ -43,25 +44,38 @@ namespace SimpleWeb {
|
||||||
return asio::ip::make_address(str);
|
return asio::ip::make_address(str);
|
||||||
}
|
}
|
||||||
template <typename socket_type, typename duration_type>
|
template <typename socket_type, typename duration_type>
|
||||||
std::unique_ptr<asio::steady_timer> make_steady_timer(socket_type &socket, std::chrono::duration<duration_type> duration) {
|
inline std::unique_ptr<asio::steady_timer> make_steady_timer(socket_type &socket, std::chrono::duration<duration_type> duration) {
|
||||||
return std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket.get_executor(), duration));
|
return std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket.get_executor(), duration));
|
||||||
}
|
}
|
||||||
template <typename handler_type>
|
template <typename handler_type>
|
||||||
void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
|
inline void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
|
||||||
resolver.async_resolve(host_port.first, host_port.second, std::forward<handler_type>(handler));
|
resolver.async_resolve(host_port.first, host_port.second, std::forward<handler_type>(handler));
|
||||||
}
|
}
|
||||||
inline asio::executor_work_guard<io_context::executor_type> make_work_guard(io_context &context) {
|
inline asio::executor_work_guard<io_context::executor_type> make_work_guard(io_context &context) {
|
||||||
return asio::make_work_guard(context);
|
return asio::make_work_guard(context);
|
||||||
}
|
}
|
||||||
|
template <typename socket_type>
|
||||||
|
inline asio::basic_socket<boost::asio::ip::tcp>::executor_type get_executor(socket_type &socket) {
|
||||||
|
return socket.get_executor();
|
||||||
|
}
|
||||||
|
template <typename execution_context, typename handler_type>
|
||||||
|
inline asio::executor_binder<typename asio::decay<handler_type>::type, typename execution_context::executor_type> bind_executor(strand &strand, handler_type &&handler) {
|
||||||
|
return asio::bind_executor(strand, std::forward<handler_type>(handler));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
using io_context = asio::io_service;
|
using io_context = asio::io_service;
|
||||||
using resolver_results = asio::ip::tcp::resolver::iterator;
|
using resolver_results = asio::ip::tcp::resolver::iterator;
|
||||||
using async_connect_endpoint = asio::ip::tcp::resolver::iterator;
|
using async_connect_endpoint = asio::ip::tcp::resolver::iterator;
|
||||||
|
using strand = asio::io_service::strand;
|
||||||
|
|
||||||
template <typename handler_type>
|
template <typename handler_type>
|
||||||
inline void post(io_context &context, handler_type &&handler) {
|
inline void post(io_context &context, handler_type &&handler) {
|
||||||
context.post(std::forward<handler_type>(handler));
|
context.post(std::forward<handler_type>(handler));
|
||||||
}
|
}
|
||||||
|
template <typename handler_type>
|
||||||
|
inline void post(strand &strand, handler_type &&handler) {
|
||||||
|
strand.post(std::forward<handler_type>(handler));
|
||||||
|
}
|
||||||
inline void restart(io_context &context) noexcept {
|
inline void restart(io_context &context) noexcept {
|
||||||
context.reset();
|
context.reset();
|
||||||
}
|
}
|
||||||
|
|
@ -69,16 +83,24 @@ namespace SimpleWeb {
|
||||||
return asio::ip::address::from_string(str);
|
return asio::ip::address::from_string(str);
|
||||||
}
|
}
|
||||||
template <typename socket_type, typename duration_type>
|
template <typename socket_type, typename duration_type>
|
||||||
std::unique_ptr<asio::steady_timer> make_steady_timer(socket_type &socket, std::chrono::duration<duration_type> duration) {
|
inline std::unique_ptr<asio::steady_timer> make_steady_timer(socket_type &socket, std::chrono::duration<duration_type> duration) {
|
||||||
return std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket.get_io_service(), duration));
|
return std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket.get_io_service(), duration));
|
||||||
}
|
}
|
||||||
template <typename handler_type>
|
template <typename handler_type>
|
||||||
void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
|
inline void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
|
||||||
resolver.async_resolve(asio::ip::tcp::resolver::query(host_port.first, host_port.second), std::forward<handler_type>(handler));
|
resolver.async_resolve(asio::ip::tcp::resolver::query(host_port.first, host_port.second), std::forward<handler_type>(handler));
|
||||||
}
|
}
|
||||||
inline io_context::work make_work_guard(io_context &context) {
|
inline io_context::work make_work_guard(io_context &context) {
|
||||||
return io_context::work(context);
|
return io_context::work(context);
|
||||||
}
|
}
|
||||||
|
template <typename socket_type>
|
||||||
|
inline io_context &get_executor(socket_type &socket) {
|
||||||
|
return socket.get_io_service();
|
||||||
|
}
|
||||||
|
template <typename handler_type>
|
||||||
|
inline asio::detail::wrapped_handler<strand, handler_type, asio::detail::is_continuation_if_running> bind_executor(strand &strand, handler_type &&handler) {
|
||||||
|
return strand.wrap(std::forward<handler_type>(handler));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
} // namespace SimpleWeb
|
} // namespace SimpleWeb
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,49 +76,61 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_from_queue() REQUIRES(send_queue_mutex) {
|
void send_from_queue() REQUIRES(send_queue_mutex) {
|
||||||
|
auto buffer = send_queue.begin()->first->data();
|
||||||
auto self = this->shared_from_this();
|
auto self = this->shared_from_this();
|
||||||
asio::async_write(*self->session->connection->socket, *send_queue.begin()->first, [self](const error_code &ec, std::size_t /*bytes_transferred*/) {
|
post(session->connection->read_write_strand, [self, buffer] {
|
||||||
auto lock = self->session->connection->handler_runner->continue_lock();
|
auto lock = self->session->connection->handler_runner->continue_lock();
|
||||||
if(!lock)
|
if(!lock)
|
||||||
return;
|
return;
|
||||||
{
|
asio::async_write(*self->session->connection->socket, buffer, [self](const error_code &ec, std::size_t /*bytes_transferred*/) {
|
||||||
LockGuard lock(self->send_queue_mutex);
|
auto lock = self->session->connection->handler_runner->continue_lock();
|
||||||
if(!ec) {
|
if(!lock)
|
||||||
auto it = self->send_queue.begin();
|
return;
|
||||||
auto callback = std::move(it->second);
|
{
|
||||||
self->send_queue.erase(it);
|
LockGuard lock(self->send_queue_mutex);
|
||||||
if(self->send_queue.size() > 0)
|
if(!ec) {
|
||||||
self->send_from_queue();
|
auto it = self->send_queue.begin();
|
||||||
|
auto callback = std::move(it->second);
|
||||||
|
self->send_queue.erase(it);
|
||||||
|
if(self->send_queue.size() > 0)
|
||||||
|
self->send_from_queue();
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
if(callback)
|
if(callback)
|
||||||
callback(ec);
|
callback(ec);
|
||||||
}
|
|
||||||
else {
|
|
||||||
// All handlers in the queue is called with ec:
|
|
||||||
std::vector<std::function<void(const error_code &)>> callbacks;
|
|
||||||
for(auto &pair : self->send_queue) {
|
|
||||||
if(pair.second)
|
|
||||||
callbacks.emplace_back(std::move(pair.second));
|
|
||||||
}
|
}
|
||||||
self->send_queue.clear();
|
else {
|
||||||
|
// All handlers in the queue is called with ec:
|
||||||
|
std::vector<std::function<void(const error_code &)>> callbacks;
|
||||||
|
for(auto &pair : self->send_queue) {
|
||||||
|
if(pair.second)
|
||||||
|
callbacks.emplace_back(std::move(pair.second));
|
||||||
|
}
|
||||||
|
self->send_queue.clear();
|
||||||
|
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
for(auto &callback : callbacks)
|
for(auto &callback : callbacks)
|
||||||
callback(ec);
|
callback(ec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_on_delete(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
|
void send_on_delete(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
|
||||||
|
auto buffer = streambuf->data();
|
||||||
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
|
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
|
||||||
asio::async_write(*session->connection->socket, *streambuf, [self, callback](const error_code &ec, std::size_t /*bytes_transferred*/) {
|
post(session->connection->read_write_strand, [self, buffer, callback] {
|
||||||
auto lock = self->session->connection->handler_runner->continue_lock();
|
auto lock = self->session->connection->handler_runner->continue_lock();
|
||||||
if(!lock)
|
if(!lock)
|
||||||
return;
|
return;
|
||||||
if(callback)
|
asio::async_write(*self->session->connection->socket, buffer, [self, callback](const error_code &ec, std::size_t /*bytes_transferred*/) {
|
||||||
callback(ec);
|
auto lock = self->session->connection->handler_runner->continue_lock();
|
||||||
|
if(!lock)
|
||||||
|
return;
|
||||||
|
if(callback)
|
||||||
|
callback(ec);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,12 +298,18 @@ namespace SimpleWeb {
|
||||||
class Connection : public std::enable_shared_from_this<Connection> {
|
class Connection : public std::enable_shared_from_this<Connection> {
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Connection(std::shared_ptr<ScopeRunner> handler_runner_, Args &&...args) noexcept : handler_runner(std::move(handler_runner_)), socket(new socket_type(std::forward<Args>(args)...)) {}
|
Connection(std::shared_ptr<ScopeRunner> handler_runner_, Args &&...args) noexcept : handler_runner(std::move(handler_runner_)), socket(new socket_type(std::forward<Args>(args)...)), read_write_strand(get_executor(socket->lowest_layer())) {}
|
||||||
|
|
||||||
std::shared_ptr<ScopeRunner> handler_runner;
|
std::shared_ptr<ScopeRunner> handler_runner;
|
||||||
|
|
||||||
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Needed for TLS communication where async_write could be called outside of the io_context runners.
|
||||||
|
* For more information see https://stackoverflow.com/a/12801042.
|
||||||
|
*/
|
||||||
|
strand read_write_strand;
|
||||||
|
|
||||||
std::unique_ptr<asio::steady_timer> timer;
|
std::unique_ptr<asio::steady_timer> timer;
|
||||||
|
|
||||||
void close() noexcept {
|
void close() noexcept {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue