可以使用一些自定义分配器来避免这种分配吗?
Can this allocation be avoided with some custom allocator?
我正在尝试学习如何将 boost::asio 与自定义分配器一起使用。在下面的例子中,我安排了一个计时器在一个链上被触发,并且处理程序是使用自定义分配器分配的。为此,我使用了这个 example:。但即使我指定了一个自定义分配器,也会调用带有堆栈跟踪的 new:
operator new(unsigned long) 0x00007fff2040882a
std::__1::__libcpp_allocate(unsigned long, unsigned long) new:253
std::__1::allocator<std::__1::__shared_ptr_emplace<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::allocator<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > > > >::allocate(unsigned long) memory:1664
std::__1::enable_if<!(is_array<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value), std::__1::shared_ptr<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > > >::type std::__1::make_shared<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >&&) memory:4037
void boost::asio::execution::detail::any_executor_base::construct_object<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >&, std::__1::integral_constant<bool, false>) any_executor.hpp:1155
boost::asio::execution::detail::any_executor_base::any_executor_base<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::integral_constant<bool, false>) any_executor.hpp:502
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::any_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::enable_if<conditional<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > >::value)) && (!(is_base_of<boost::asio::execution::detail::any_executor_base, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value)), boost::asio::execution::detail::is_valid_target_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void (boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> >)>, std::__1::integral_constant<bool, false> >::type::value, void>::type*) any_executor.hpp:1406
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::any_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::enable_if<conditional<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > >::value)) && (!(is_base_of<boost::asio::execution::detail::any_executor_base, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value)), boost::asio::execution::detail::is_valid_target_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void (boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> >)>, std::__1::integral_constant<bool, false> >::type::value, void>::type*) any_executor.hpp:1409
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn_impl<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> > >(void const*, void const*, std::__1::enable_if<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void>::value)) && (boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >::is_preferable), void>::type*) any_executor.hpp:1102
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> > >(void const*, void const*) any_executor.hpp:1115
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::prefer<boost::asio::execution::detail::blocking::possibly_t<0> >(boost::asio::execution::detail::blocking::possibly_t<0> const&, std::__1::enable_if<find_convertible_preferable_property<boost::asio::execution::detail::blocking::possibly_t<0> >::value, void>::type*) const any_executor.hpp:1682
boost::asio::any_io_executor boost::asio::any_io_executor::prefer<boost::asio::execution::detail::blocking::possibly_t<0> >(boost::asio::execution::detail::blocking::possibly_t<0> const&, boost::asio::constraint<traits::prefer_member<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > const&, boost::asio::execution::detail::blocking::possibly_t<0> const&>::is_valid, int>::type) const any_io_executor.hpp:228
std::__1::enable_if<(call_traits<asio_prefer_fn::impl, boost::asio::any_io_executor&, void (boost::asio::execution::detail::blocking::possibly_t<0> const&)>::overload) == ((asio_prefer_fn::overload_type)3), asio_prefer_fn::call_traits<asio_prefer_fn::impl, boost::asio::any_io_executor&, void (boost::asio::execution::detail::blocking::possibly_t<0> const&), void, void, void, void, void, void, void>::result_type>::type asio_prefer_fn::impl::operator()<boost::asio::any_io_executor&, boost::asio::execution::detail::blocking::possibly_t<0> const&>(boost::asio::any_io_executor&, boost::asio::execution::detail::blocking::possibly_t<0> const&) const prefer.hpp:496
void boost::asio::detail::handler_work_base<boost::asio::any_io_executor, void, boost::asio::io_context, boost::asio::executor, void>::dispatch<boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> > >(boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>&, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >&) handler_work.hpp:430
void boost::asio::detail::handler_work<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::asio::any_io_executor, void>::complete<boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code> >(boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>&, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >&) handler_work.hpp:505
boost::asio::detail::wait_handler<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::asio::any_io_executor>::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) wait_handler.hpp:76
boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) scheduler_operation.hpp:40
boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) scheduler.ipp:486
boost::asio::detail::scheduler::run(boost::system::error_code&) scheduler.ipp:204
boost::asio::io_context::run() io_context.ipp:63
main::$_1::operator()() const strand_demo.cpp:190
decltype(std::__1::forward<main::$_1>(fp)()) std::__1::__invoke<main::$_1>(main::$_1&&) type_traits:3747
void std::__1::__async_func<main::$_1>::__execute<>(std::__1::__tuple_indices<>) future:2186
std::__1::__async_func<main::$_1>::operator()() future:2179
std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::__execute() future:997
decltype(*(std::__1::forward<std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*>(fp0)).*fp()) std::__1::__invoke<void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*, void>(void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*&&)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*&&) type_traits:3688
void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*>&, std::__1::__tuple_indices<2ul>) thread:280
void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*> >(void*) thread:291
_pthread_start 0x00007fff204438fc
thread_start 0x00007fff2043f443
代码:
#include <chrono>
#include <iostream>
#include <utility>
#include "boost/asio.hpp"
#include <boost/pool/pool.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind/bind.hpp>
namespace details {
template <size_t SIZE, size_t... REST>
class Bucket;
template <size_t SIZE>
class Bucket<SIZE> {
public:
Bucket() = default;
protected:
void* do_alloc(const std::size_t numElements) {
assert(numElements == 1);
return allocator_.malloc();
}
void do_dealloc(void* const ptr, const std::size_t numElements) {
assert(numElements == 1);
allocator_.free(ptr, numElements);
}
private:
boost::pool<boost::default_user_allocator_new_delete> allocator_{SIZE};
};
template <size_t SIZE, size_t... REST>
class Bucket
: public Bucket<SIZE>
, public Bucket<REST>... {};
} // namespace details
template <size_t SIZE, size_t... REST>
class BucketPool : public details::Bucket<SIZE, REST...> {
public:
template <size_t S>
void* alloc(const std::size_t numElements) {
return details::Bucket<S>::do_alloc(numElements);
}
template <size_t S>
void dealloc(void* const ptr, const std::size_t numElements) {
assert(numElements == 1);
details::Bucket<S>::do_dealloc(ptr, numElements);
}
};
using strand_t = boost::asio::strand<boost::asio::io_context::executor_type>;
template <typename T, typename PoolType>
class ObjectAllocator {
public:
using value_type = T;
explicit ObjectAllocator(PoolType& bucketPool) : bucketPool_(bucketPool) {}
template <typename U, typename K>
explicit ObjectAllocator(const ObjectAllocator<U, K>& other)
: bucketPool_(other.bucketPool_) {}
bool operator==(const ObjectAllocator& lhs) const noexcept {
return bucketPool_ == lhs.bucketPool_;
}
bool operator!=(const ObjectAllocator& lhs) const noexcept {
return bucketPool_ != lhs.bucketPool_;
}
T* allocate(const std::size_t numElements) const {
return static_cast<T*>(bucketPool_.template alloc<sizeof(T)>(numElements));
}
void deallocate(T* const ptr, const std::size_t numElements) const {
bucketPool_.template dealloc<sizeof(T)>(ptr, numElements);
}
private:
template <typename, typename>
friend class ObjectAllocator;
PoolType& bucketPool_;
};
template <typename HandlerT>
class AsioTimer {
class HandlerWrapper : public boost::enable_shared_from_this<HandlerWrapper> {
public:
HandlerWrapper(strand_t strand, HandlerT handler)
: timer_(strand), handler_(handler), milliseconds_(0) {}
void startTimer(const std::chrono::milliseconds& everyMilliseconds) {
milliseconds_ = everyMilliseconds;
timer_.expires_from_now(everyMilliseconds);
startAsyncWait();
}
private:
void startAsyncWait() {
timer_.async_wait(MakeCustomAllocationHandler(
memory_,
boost::bind(&HandlerWrapper::handleTimerCallback, this->shared_from_this(),
boost::asio::placeholders::error)));
}
void handleTimerCallback(const boost::system::error_code& e) {
if (e != boost::asio::error::operation_aborted) {
handler_();
}
timer_.expires_at(timer_.expires_at() + milliseconds_);
startAsyncWait();
}
BucketPool<128> memory_;
boost::asio::steady_timer timer_;
HandlerT handler_;
std::chrono::milliseconds milliseconds_;
};
public:
AsioTimer(strand_t strand, HandlerT handler)
: handlerWrapper_(boost::make_shared<HandlerWrapper>(strand, handler)) {}
void startTimer(const std::chrono::milliseconds& everyMilliseconds) {
handlerWrapper_->startTimer(everyMilliseconds);
}
private:
boost::shared_ptr<HandlerWrapper> handlerWrapper_;
};
template <typename HandlerT, typename PoolT>
class CustomAllocationHandler {
public:
using allocator_type = ObjectAllocator<HandlerT, PoolT>;
CustomAllocationHandler(PoolT& memory, HandlerT handler)
: memory_(memory), handler_(std::move(handler)) {}
allocator_type get_allocator() const noexcept {
return allocator_type(memory_);
}
template <typename... Args>
void operator()(Args&&... args) {
handler_(std::forward<Args>(args)...);
}
private:
PoolT& memory_;
HandlerT handler_;
};
template <typename HandlerT, typename PoolT>
CustomAllocationHandler<HandlerT, PoolT> MakeCustomAllocationHandler(PoolT& memory,
HandlerT handler) {
return CustomAllocationHandler<HandlerT, PoolT>(memory, std::move(handler));
}
int main() {
boost::asio::io_context ioContext;
strand_t myStrand(make_strand(ioContext));
AsioTimer timer(myStrand, [] { std::cout << "timer called" << std::endl; });
timer.startTimer(std::chrono::milliseconds(20));
auto fut = std::async([&ioContext] {
ioContext.run();
});
std::this_thread::sleep_for(std::chrono::seconds(1));
ioContext.stop();
fut.get();
}
有什么方法可以避免在调用 ioContext::run()
时调用 new
?
您的代码正在使用类型擦除的执行程序。它有效地运行在
asio::basic_stream_socket<asio::ip::tcp, asio::any_io_executor>
,这会导致开销(分配和引用计数)。
要删除涉及的分配,请静态键入您的执行程序。请记住,它会使您的代码不那么灵活,因此如果您想将它与不同的执行程序一起使用,您可能需要在实际执行程序类型上进行模板化(例如 thread_pool
vs io_context
vs strand<>
)。
例如
using executor_t = boost::asio::io_context::executor_type;
using socket_t = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;
请注意,这在实践中通常会大大提高 Asio 网络的性能!
为了好玩,我让它在回显之前反转了文本。输出“dlrow olleH”
#include <array>
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>
using boost::asio::ip::tcp;
// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_memory {
public:
handler_memory() : in_use_(false)
{
}
handler_memory(const handler_memory&) = delete;
handler_memory& operator=(const handler_memory&) = delete;
void* allocate(std::size_t size)
{
if (!in_use_ && size < sizeof(storage_)) {
in_use_ = true;
return &storage_;
} else {
return ::operator new(size);
}
}
void deallocate(void* pointer)
{
if (pointer == &storage_) {
in_use_ = false;
} else {
::operator delete(pointer);
}
}
private:
// Storage space used for handler-based custom memory allocation.
typename std::aligned_storage<1024>::type storage_;
// Whether the handler-based custom allocation storage has been used.
bool in_use_;
};
// The allocator to be associated with the handler objects. This allocator only
// needs to satisfy the C++11 minimal allocator requirements.
template <typename T> class handler_allocator {
public:
using value_type = T;
explicit handler_allocator(handler_memory& mem) : memory_(mem)
{
}
template <typename U>
handler_allocator(const handler_allocator<U>& other) noexcept
: memory_(other.memory_)
{
}
bool operator==(const handler_allocator& other) const noexcept
{
return &memory_ == &other.memory_;
}
bool operator!=(const handler_allocator& other) const noexcept
{
return &memory_ != &other.memory_;
}
T* allocate(std::size_t n) const
{
return static_cast<T*>(memory_.allocate(sizeof(T) * n));
}
void deallocate(T* p, std::size_t /*n*/) const
{
return memory_.deallocate(p);
}
private:
template <typename> friend class handler_allocator;
// The underlying memory.
handler_memory& memory_;
};
// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. The allocator_type type and get_allocator()
// member function are used by the asynchronous operations to obtain the
// allocator. Calls to operator() are forwarded to the encapsulated handler.
template <typename Handler> class custom_alloc_handler {
public:
using allocator_type = handler_allocator<Handler>;
custom_alloc_handler(handler_memory& m, Handler h) : memory_(m), handler_(h)
{
}
allocator_type get_allocator() const noexcept
{
return allocator_type(memory_);
}
template <typename... Args> void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
private:
handler_memory& memory_;
Handler handler_;
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler>
make_custom_alloc_handler(handler_memory& m, Handler h)
{
return custom_alloc_handler<Handler>(m, h);
}
using executor_t = boost::asio::io_context::executor_type;
using socket_t = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;
class session : public std::enable_shared_from_this<session> {
public:
session(socket_t socket) : socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_),
make_custom_alloc_handler(
handler_memory_,
[this, self](boost::system::error_code ec, std::size_t length) {
if (!ec) {
do_write(length);
}
}));
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
if (length > 0) { // just for fun
std::reverse(data_.begin(), data_.begin() + length - 1);
}
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
make_custom_alloc_handler(handler_memory_,
[this, self](boost::system::error_code ec,
std::size_t /*length*/) {
if (!ec) {
do_read();
}
}));
}
// The socket used to communicate with the client.
socket_t socket_;
// Buffer used to store data received from the client.
std::array<char, 1024> data_;
// The memory to use for handler-based custom memory allocation.
handler_memory handler_memory_;
};
class server {
public:
server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, socket_t socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
acceptor_t acceptor_;
};
int main(int argc, char* argv[])
{
try {
if (argc != 2) {
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
server s(io_context, std::atoi(argv[1]));
using namespace std::chrono_literals;
io_context.run_for(3s);
return 0;
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return 2;
}
}
奖金
使用 C++17 CTAD,只需很少的更改即可在执行程序上对整个服务器进行模板化。
另请注意,它按值传递执行程序,更多地将执行上下文与服务对象分离。
#include <array>
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>
using boost::asio::ip::tcp;
// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_memory {
public:
handler_memory() : in_use_(false)
{
}
handler_memory(const handler_memory&) = delete;
handler_memory& operator=(const handler_memory&) = delete;
void* allocate(std::size_t size)
{
if (!in_use_ && size < sizeof(storage_)) {
in_use_ = true;
return &storage_;
} else {
return ::operator new(size);
}
}
void deallocate(void* pointer)
{
if (pointer == &storage_) {
in_use_ = false;
} else {
::operator delete(pointer);
}
}
private:
// Storage space used for handler-based custom memory allocation.
typename std::aligned_storage<1024>::type storage_;
// Whether the handler-based custom allocation storage has been used.
bool in_use_;
};
// The allocator to be associated with the handler objects. This allocator only
// needs to satisfy the C++11 minimal allocator requirements.
template <typename T> class handler_allocator {
public:
using value_type = T;
explicit handler_allocator(handler_memory& mem) : memory_(mem)
{
}
template <typename U>
handler_allocator(const handler_allocator<U>& other) noexcept
: memory_(other.memory_)
{
}
bool operator==(const handler_allocator& other) const noexcept
{
return &memory_ == &other.memory_;
}
bool operator!=(const handler_allocator& other) const noexcept
{
return &memory_ != &other.memory_;
}
T* allocate(std::size_t n) const
{
return static_cast<T*>(memory_.allocate(sizeof(T) * n));
}
void deallocate(T* p, std::size_t /*n*/) const
{
return memory_.deallocate(p);
}
private:
template <typename> friend class handler_allocator;
// The underlying memory.
handler_memory& memory_;
};
// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. The allocator_type type and get_allocator()
// member function are used by the asynchronous operations to obtain the
// allocator. Calls to operator() are forwarded to the encapsulated handler.
template <typename Handler> class custom_alloc_handler {
public:
using allocator_type = handler_allocator<Handler>;
custom_alloc_handler(handler_memory& m, Handler h) : memory_(m), handler_(h)
{
}
allocator_type get_allocator() const noexcept
{
return allocator_type(memory_);
}
template <typename... Args> void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
private:
handler_memory& memory_;
Handler handler_;
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler>
make_custom_alloc_handler(handler_memory& m, Handler h)
{
return custom_alloc_handler<Handler>(m, h);
}
template <typename Executor>
class server {
using executor_t = Executor;
using socket_t = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;
class session : public std::enable_shared_from_this<session> {
public:
session(socket_t socket) : socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(this->shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_),
make_custom_alloc_handler(
handler_memory_,
[this, self](boost::system::error_code ec,
std::size_t length) {
if (!ec) {
do_write(length);
}
}));
}
void do_write(std::size_t length)
{
auto self(this->shared_from_this());
if (length > 0) { // just for fun
std::reverse(data_.begin(), data_.begin() + length - 1);
}
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
make_custom_alloc_handler(
handler_memory_,
[this, self](boost::system::error_code ec,
std::size_t /*length*/) {
if (!ec) {
do_read();
}
}));
}
// The socket used to communicate with the client.
socket_t socket_;
// Buffer used to store data received from the client.
std::array<char, 1024> data_;
// The memory to use for handler-based custom memory allocation.
handler_memory handler_memory_;
};
public:
server(Executor executor, short port)
: acceptor_(executor, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, socket_t socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
acceptor_t acceptor_;
};
int main(int argc, char* argv[])
{
try {
if (argc != 2) {
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
server s(io_context.get_executor(), std::atoi(argv[1]));
using namespace std::chrono_literals;
io_context.run_for(3s);
return 0;
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return 2;
}
}
我正在尝试学习如何将 boost::asio 与自定义分配器一起使用。在下面的例子中,我安排了一个计时器在一个链上被触发,并且处理程序是使用自定义分配器分配的。为此,我使用了这个 example:。但即使我指定了一个自定义分配器,也会调用带有堆栈跟踪的 new:
operator new(unsigned long) 0x00007fff2040882a
std::__1::__libcpp_allocate(unsigned long, unsigned long) new:253
std::__1::allocator<std::__1::__shared_ptr_emplace<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::allocator<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > > > >::allocate(unsigned long) memory:1664
std::__1::enable_if<!(is_array<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value), std::__1::shared_ptr<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > > >::type std::__1::make_shared<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >&&) memory:4037
void boost::asio::execution::detail::any_executor_base::construct_object<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >&, std::__1::integral_constant<bool, false>) any_executor.hpp:1155
boost::asio::execution::detail::any_executor_base::any_executor_base<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::integral_constant<bool, false>) any_executor.hpp:502
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::any_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::enable_if<conditional<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > >::value)) && (!(is_base_of<boost::asio::execution::detail::any_executor_base, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value)), boost::asio::execution::detail::is_valid_target_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void (boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> >)>, std::__1::integral_constant<bool, false> >::type::value, void>::type*) any_executor.hpp:1406
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::any_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >(boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, std::__1::enable_if<conditional<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > >::value)) && (!(is_base_of<boost::asio::execution::detail::any_executor_base, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> > >::value)), boost::asio::execution::detail::is_valid_target_executor<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void (boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> >)>, std::__1::integral_constant<bool, false> >::type::value, void>::type*) any_executor.hpp:1409
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn_impl<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> > >(void const*, void const*, std::__1::enable_if<(!(is_same<boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, void>::value)) && (boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >::is_preferable), void>::type*) any_executor.hpp:1102
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::detail::any_executor_base::prefer_fn<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >, boost::asio::strand<boost::asio::io_context::basic_executor_type<std::__1::allocator<void>, 4u> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> > >(void const*, void const*) any_executor.hpp:1115
boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > >::prefer<boost::asio::execution::detail::blocking::possibly_t<0> >(boost::asio::execution::detail::blocking::possibly_t<0> const&, std::__1::enable_if<find_convertible_preferable_property<boost::asio::execution::detail::blocking::possibly_t<0> >::value, void>::type*) const any_executor.hpp:1682
boost::asio::any_io_executor boost::asio::any_io_executor::prefer<boost::asio::execution::detail::blocking::possibly_t<0> >(boost::asio::execution::detail::blocking::possibly_t<0> const&, boost::asio::constraint<traits::prefer_member<boost::asio::execution::any_executor<boost::asio::execution::context_as_t<boost::asio::execution_context&>, boost::asio::execution::detail::blocking::never_t<0>, boost::asio::execution::prefer_only<boost::asio::execution::detail::blocking::possibly_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::tracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::outstanding_work::untracked_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::fork_t<0> >, boost::asio::execution::prefer_only<boost::asio::execution::detail::relationship::continuation_t<0> > > const&, boost::asio::execution::detail::blocking::possibly_t<0> const&>::is_valid, int>::type) const any_io_executor.hpp:228
std::__1::enable_if<(call_traits<asio_prefer_fn::impl, boost::asio::any_io_executor&, void (boost::asio::execution::detail::blocking::possibly_t<0> const&)>::overload) == ((asio_prefer_fn::overload_type)3), asio_prefer_fn::call_traits<asio_prefer_fn::impl, boost::asio::any_io_executor&, void (boost::asio::execution::detail::blocking::possibly_t<0> const&), void, void, void, void, void, void, void>::result_type>::type asio_prefer_fn::impl::operator()<boost::asio::any_io_executor&, boost::asio::execution::detail::blocking::possibly_t<0> const&>(boost::asio::any_io_executor&, boost::asio::execution::detail::blocking::possibly_t<0> const&) const prefer.hpp:496
void boost::asio::detail::handler_work_base<boost::asio::any_io_executor, void, boost::asio::io_context, boost::asio::executor, void>::dispatch<boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> > >(boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>&, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >&) handler_work.hpp:430
void boost::asio::detail::handler_work<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::asio::any_io_executor, void>::complete<boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code> >(boost::asio::detail::binder1<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::system::error_code>&, CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >&) handler_work.hpp:505
boost::asio::detail::wait_handler<CustomAllocationHandler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, AsioTimer<main::$_0>::HandlerWrapper, boost::system::error_code const&>, boost::_bi::list2<boost::_bi::value<boost::shared_ptr<AsioTimer<main::$_0>::HandlerWrapper> >, boost::arg<1> (*)()> >, BucketPool<128ul> >, boost::asio::any_io_executor>::do_complete(void*, boost::asio::detail::scheduler_operation*, boost::system::error_code const&, unsigned long) wait_handler.hpp:76
boost::asio::detail::scheduler_operation::complete(void*, boost::system::error_code const&, unsigned long) scheduler_operation.hpp:40
boost::asio::detail::scheduler::do_run_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lock&, boost::asio::detail::scheduler_thread_info&, boost::system::error_code const&) scheduler.ipp:486
boost::asio::detail::scheduler::run(boost::system::error_code&) scheduler.ipp:204
boost::asio::io_context::run() io_context.ipp:63
main::$_1::operator()() const strand_demo.cpp:190
decltype(std::__1::forward<main::$_1>(fp)()) std::__1::__invoke<main::$_1>(main::$_1&&) type_traits:3747
void std::__1::__async_func<main::$_1>::__execute<>(std::__1::__tuple_indices<>) future:2186
std::__1::__async_func<main::$_1>::operator()() future:2179
std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::__execute() future:997
decltype(*(std::__1::forward<std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*>(fp0)).*fp()) std::__1::__invoke<void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*, void>(void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*&&)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*&&) type_traits:3688
void std::__1::__thread_execute<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*, 2ul>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*>&, std::__1::__tuple_indices<2ul>) thread:280
void* std::__1::__thread_proxy<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct> >, void (std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >::*)(), std::__1::__async_assoc_state<void, std::__1::__async_func<main::$_1> >*> >(void*) thread:291
_pthread_start 0x00007fff204438fc
thread_start 0x00007fff2043f443
代码:
#include <chrono>
#include <iostream>
#include <utility>
#include "boost/asio.hpp"
#include <boost/pool/pool.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind/bind.hpp>
namespace details {
template <size_t SIZE, size_t... REST>
class Bucket;
template <size_t SIZE>
class Bucket<SIZE> {
public:
Bucket() = default;
protected:
void* do_alloc(const std::size_t numElements) {
assert(numElements == 1);
return allocator_.malloc();
}
void do_dealloc(void* const ptr, const std::size_t numElements) {
assert(numElements == 1);
allocator_.free(ptr, numElements);
}
private:
boost::pool<boost::default_user_allocator_new_delete> allocator_{SIZE};
};
template <size_t SIZE, size_t... REST>
class Bucket
: public Bucket<SIZE>
, public Bucket<REST>... {};
} // namespace details
template <size_t SIZE, size_t... REST>
class BucketPool : public details::Bucket<SIZE, REST...> {
public:
template <size_t S>
void* alloc(const std::size_t numElements) {
return details::Bucket<S>::do_alloc(numElements);
}
template <size_t S>
void dealloc(void* const ptr, const std::size_t numElements) {
assert(numElements == 1);
details::Bucket<S>::do_dealloc(ptr, numElements);
}
};
using strand_t = boost::asio::strand<boost::asio::io_context::executor_type>;
template <typename T, typename PoolType>
class ObjectAllocator {
public:
using value_type = T;
explicit ObjectAllocator(PoolType& bucketPool) : bucketPool_(bucketPool) {}
template <typename U, typename K>
explicit ObjectAllocator(const ObjectAllocator<U, K>& other)
: bucketPool_(other.bucketPool_) {}
bool operator==(const ObjectAllocator& lhs) const noexcept {
return bucketPool_ == lhs.bucketPool_;
}
bool operator!=(const ObjectAllocator& lhs) const noexcept {
return bucketPool_ != lhs.bucketPool_;
}
T* allocate(const std::size_t numElements) const {
return static_cast<T*>(bucketPool_.template alloc<sizeof(T)>(numElements));
}
void deallocate(T* const ptr, const std::size_t numElements) const {
bucketPool_.template dealloc<sizeof(T)>(ptr, numElements);
}
private:
template <typename, typename>
friend class ObjectAllocator;
PoolType& bucketPool_;
};
template <typename HandlerT>
class AsioTimer {
class HandlerWrapper : public boost::enable_shared_from_this<HandlerWrapper> {
public:
HandlerWrapper(strand_t strand, HandlerT handler)
: timer_(strand), handler_(handler), milliseconds_(0) {}
void startTimer(const std::chrono::milliseconds& everyMilliseconds) {
milliseconds_ = everyMilliseconds;
timer_.expires_from_now(everyMilliseconds);
startAsyncWait();
}
private:
void startAsyncWait() {
timer_.async_wait(MakeCustomAllocationHandler(
memory_,
boost::bind(&HandlerWrapper::handleTimerCallback, this->shared_from_this(),
boost::asio::placeholders::error)));
}
void handleTimerCallback(const boost::system::error_code& e) {
if (e != boost::asio::error::operation_aborted) {
handler_();
}
timer_.expires_at(timer_.expires_at() + milliseconds_);
startAsyncWait();
}
BucketPool<128> memory_;
boost::asio::steady_timer timer_;
HandlerT handler_;
std::chrono::milliseconds milliseconds_;
};
public:
AsioTimer(strand_t strand, HandlerT handler)
: handlerWrapper_(boost::make_shared<HandlerWrapper>(strand, handler)) {}
void startTimer(const std::chrono::milliseconds& everyMilliseconds) {
handlerWrapper_->startTimer(everyMilliseconds);
}
private:
boost::shared_ptr<HandlerWrapper> handlerWrapper_;
};
template <typename HandlerT, typename PoolT>
class CustomAllocationHandler {
public:
using allocator_type = ObjectAllocator<HandlerT, PoolT>;
CustomAllocationHandler(PoolT& memory, HandlerT handler)
: memory_(memory), handler_(std::move(handler)) {}
allocator_type get_allocator() const noexcept {
return allocator_type(memory_);
}
template <typename... Args>
void operator()(Args&&... args) {
handler_(std::forward<Args>(args)...);
}
private:
PoolT& memory_;
HandlerT handler_;
};
template <typename HandlerT, typename PoolT>
CustomAllocationHandler<HandlerT, PoolT> MakeCustomAllocationHandler(PoolT& memory,
HandlerT handler) {
return CustomAllocationHandler<HandlerT, PoolT>(memory, std::move(handler));
}
int main() {
boost::asio::io_context ioContext;
strand_t myStrand(make_strand(ioContext));
AsioTimer timer(myStrand, [] { std::cout << "timer called" << std::endl; });
timer.startTimer(std::chrono::milliseconds(20));
auto fut = std::async([&ioContext] {
ioContext.run();
});
std::this_thread::sleep_for(std::chrono::seconds(1));
ioContext.stop();
fut.get();
}
有什么方法可以避免在调用 ioContext::run()
时调用 new
?
您的代码正在使用类型擦除的执行程序。它有效地运行在
asio::basic_stream_socket<asio::ip::tcp, asio::any_io_executor>
,这会导致开销(分配和引用计数)。
要删除涉及的分配,请静态键入您的执行程序。请记住,它会使您的代码不那么灵活,因此如果您想将它与不同的执行程序一起使用,您可能需要在实际执行程序类型上进行模板化(例如 thread_pool
vs io_context
vs strand<>
)。
例如
using executor_t = boost::asio::io_context::executor_type;
using socket_t = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;
请注意,这在实践中通常会大大提高 Asio 网络的性能!
为了好玩,我让它在回显之前反转了文本。输出“dlrow olleH”
#include <array>
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>
using boost::asio::ip::tcp;
// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_memory {
public:
handler_memory() : in_use_(false)
{
}
handler_memory(const handler_memory&) = delete;
handler_memory& operator=(const handler_memory&) = delete;
void* allocate(std::size_t size)
{
if (!in_use_ && size < sizeof(storage_)) {
in_use_ = true;
return &storage_;
} else {
return ::operator new(size);
}
}
void deallocate(void* pointer)
{
if (pointer == &storage_) {
in_use_ = false;
} else {
::operator delete(pointer);
}
}
private:
// Storage space used for handler-based custom memory allocation.
typename std::aligned_storage<1024>::type storage_;
// Whether the handler-based custom allocation storage has been used.
bool in_use_;
};
// The allocator to be associated with the handler objects. This allocator only
// needs to satisfy the C++11 minimal allocator requirements.
template <typename T> class handler_allocator {
public:
using value_type = T;
explicit handler_allocator(handler_memory& mem) : memory_(mem)
{
}
template <typename U>
handler_allocator(const handler_allocator<U>& other) noexcept
: memory_(other.memory_)
{
}
bool operator==(const handler_allocator& other) const noexcept
{
return &memory_ == &other.memory_;
}
bool operator!=(const handler_allocator& other) const noexcept
{
return &memory_ != &other.memory_;
}
T* allocate(std::size_t n) const
{
return static_cast<T*>(memory_.allocate(sizeof(T) * n));
}
void deallocate(T* p, std::size_t /*n*/) const
{
return memory_.deallocate(p);
}
private:
template <typename> friend class handler_allocator;
// The underlying memory.
handler_memory& memory_;
};
// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. The allocator_type type and get_allocator()
// member function are used by the asynchronous operations to obtain the
// allocator. Calls to operator() are forwarded to the encapsulated handler.
template <typename Handler> class custom_alloc_handler {
public:
using allocator_type = handler_allocator<Handler>;
custom_alloc_handler(handler_memory& m, Handler h) : memory_(m), handler_(h)
{
}
allocator_type get_allocator() const noexcept
{
return allocator_type(memory_);
}
template <typename... Args> void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
private:
handler_memory& memory_;
Handler handler_;
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler>
make_custom_alloc_handler(handler_memory& m, Handler h)
{
return custom_alloc_handler<Handler>(m, h);
}
using executor_t = boost::asio::io_context::executor_type;
using socket_t = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;
class session : public std::enable_shared_from_this<session> {
public:
session(socket_t socket) : socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_),
make_custom_alloc_handler(
handler_memory_,
[this, self](boost::system::error_code ec, std::size_t length) {
if (!ec) {
do_write(length);
}
}));
}
void do_write(std::size_t length)
{
auto self(shared_from_this());
if (length > 0) { // just for fun
std::reverse(data_.begin(), data_.begin() + length - 1);
}
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
make_custom_alloc_handler(handler_memory_,
[this, self](boost::system::error_code ec,
std::size_t /*length*/) {
if (!ec) {
do_read();
}
}));
}
// The socket used to communicate with the client.
socket_t socket_;
// Buffer used to store data received from the client.
std::array<char, 1024> data_;
// The memory to use for handler-based custom memory allocation.
handler_memory handler_memory_;
};
class server {
public:
server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, socket_t socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
acceptor_t acceptor_;
};
int main(int argc, char* argv[])
{
try {
if (argc != 2) {
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
server s(io_context, std::atoi(argv[1]));
using namespace std::chrono_literals;
io_context.run_for(3s);
return 0;
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return 2;
}
}
奖金
使用 C++17 CTAD,只需很少的更改即可在执行程序上对整个服务器进行模板化。
另请注意,它按值传递执行程序,更多地将执行上下文与服务对象分离。
#include <array>
#include <boost/asio.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <type_traits>
#include <utility>
using boost::asio::ip::tcp;
// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_memory {
public:
handler_memory() : in_use_(false)
{
}
handler_memory(const handler_memory&) = delete;
handler_memory& operator=(const handler_memory&) = delete;
void* allocate(std::size_t size)
{
if (!in_use_ && size < sizeof(storage_)) {
in_use_ = true;
return &storage_;
} else {
return ::operator new(size);
}
}
void deallocate(void* pointer)
{
if (pointer == &storage_) {
in_use_ = false;
} else {
::operator delete(pointer);
}
}
private:
// Storage space used for handler-based custom memory allocation.
typename std::aligned_storage<1024>::type storage_;
// Whether the handler-based custom allocation storage has been used.
bool in_use_;
};
// The allocator to be associated with the handler objects. This allocator only
// needs to satisfy the C++11 minimal allocator requirements.
template <typename T> class handler_allocator {
public:
using value_type = T;
explicit handler_allocator(handler_memory& mem) : memory_(mem)
{
}
template <typename U>
handler_allocator(const handler_allocator<U>& other) noexcept
: memory_(other.memory_)
{
}
bool operator==(const handler_allocator& other) const noexcept
{
return &memory_ == &other.memory_;
}
bool operator!=(const handler_allocator& other) const noexcept
{
return &memory_ != &other.memory_;
}
T* allocate(std::size_t n) const
{
return static_cast<T*>(memory_.allocate(sizeof(T) * n));
}
void deallocate(T* p, std::size_t /*n*/) const
{
return memory_.deallocate(p);
}
private:
template <typename> friend class handler_allocator;
// The underlying memory.
handler_memory& memory_;
};
// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. The allocator_type type and get_allocator()
// member function are used by the asynchronous operations to obtain the
// allocator. Calls to operator() are forwarded to the encapsulated handler.
template <typename Handler> class custom_alloc_handler {
public:
using allocator_type = handler_allocator<Handler>;
custom_alloc_handler(handler_memory& m, Handler h) : memory_(m), handler_(h)
{
}
allocator_type get_allocator() const noexcept
{
return allocator_type(memory_);
}
template <typename... Args> void operator()(Args&&... args)
{
handler_(std::forward<Args>(args)...);
}
private:
handler_memory& memory_;
Handler handler_;
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler>
make_custom_alloc_handler(handler_memory& m, Handler h)
{
return custom_alloc_handler<Handler>(m, h);
}
template <typename Executor>
class server {
using executor_t = Executor;
using socket_t = boost::asio::basic_stream_socket<tcp, executor_t>;
using acceptor_t = boost::asio::basic_socket_acceptor<tcp, executor_t>;
class session : public std::enable_shared_from_this<session> {
public:
session(socket_t socket) : socket_(std::move(socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(this->shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_),
make_custom_alloc_handler(
handler_memory_,
[this, self](boost::system::error_code ec,
std::size_t length) {
if (!ec) {
do_write(length);
}
}));
}
void do_write(std::size_t length)
{
auto self(this->shared_from_this());
if (length > 0) { // just for fun
std::reverse(data_.begin(), data_.begin() + length - 1);
}
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
make_custom_alloc_handler(
handler_memory_,
[this, self](boost::system::error_code ec,
std::size_t /*length*/) {
if (!ec) {
do_read();
}
}));
}
// The socket used to communicate with the client.
socket_t socket_;
// Buffer used to store data received from the client.
std::array<char, 1024> data_;
// The memory to use for handler-based custom memory allocation.
handler_memory handler_memory_;
};
public:
server(Executor executor, short port)
: acceptor_(executor, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, socket_t socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
acceptor_t acceptor_;
};
int main(int argc, char* argv[])
{
try {
if (argc != 2) {
std::cerr << "Usage: server <port>\n";
return 1;
}
boost::asio::io_context io_context;
server s(io_context.get_executor(), std::atoi(argv[1]));
using namespace std::chrono_literals;
io_context.run_for(3s);
return 0;
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return 2;
}
}