как использовать boost :: asio :: defer () в составлении функции?

На Boost 1.66, у Asio есть осуждается asio_handler_is_continuation функция крюка, способствующая использованию defer функция. Кажется, что defer функция ведет себя точно так же, как post когда asio_handler_is_continuation == true. Тем не менее, способ использования defer отличается от способа использования asio_handler_is_continuation, и я не уверен, как правильно использовать defer,

РЕДАКТИРОВАТЬЯ думаю, что образец ниже слишком многословен, чтобы ясно выразить то, что я имею в виду. Вот более короткий пример:

async_read_until(stream, read_buffer, "\r\n",
[](boost::system::error_code ec, std::size_t bytes_transferred)
{
if(!ec)
async_write(stream, write_buffer, some_handler);
})

Теперь когда async_read_until завершено, переданный лямбда-обработчик будет вызван с использованием некоторых средств, эквивалентных boost::asio::post, Но async_write внутри лямбда-обработчика есть продолжение последней асинхронной задачи, поэтому я хочу вызвать лямбда-обработчик, используя defer воспользоваться преимуществом оптимизации.

Есть ли способ использовать defer (вместо post) вызвать лямбда-обработчик в приведенном выше примере?

ОРИГИНАЛЬНАЯ ПОЧТА: Я пытаюсь написать простую инициирующую функцию async_echo похож на тот, что в зверь документ, кроме той части, которая вызывает boost::asio::async_write будет называться продолжением. Чтобы достичь этого, предварительная промежуточная операция boost::asio::async_read_until должен вызвать обработчик *this как продолжение.

Это та часть, на которую я ссылаюсь в примере async_echo документа beast:

template<class AsyncStream, class Handler>
void echo_op<AsyncStream, Handler>::
operator()(boost::beast::error_code ec, std::size_t bytes_transferred)
{
// Store a reference to our state. The address of the state won't
// change, and this solves the problem where dereferencing the
// data member is undefined after a move.
auto& p = *p_;

// Now perform the next step in the state machine
switch(ec ? 2 : p.step)
{
// initial entry
case 0:
// read up to the first newline
p.step = 1;
return boost::asio::async_read_until(p.stream, p.buffer, "\r", std::move(*this));

case 1:
// write everything back
p.step = 2;
// async_read_until could have read past the newline,
// use buffers_prefix to make sure we only send one line
return boost::asio::async_write(p.stream,
boost::beast::buffers_prefix(bytes_transferred, p.buffer.data()), std::move(*this));

case 2:
p.buffer.consume(bytes_transferred);
break;
}

// Invoke the final handler. The implementation of `handler_ptr`
// will deallocate the storage for the state before the handler
// is invoked. This is necessary to provide the
// destroy-before-invocation guarantee on handler memory
// customizations.
//
// If we wanted to pass any arguments to the handler which come
// from the `state`, they would have to be moved to the stack
// first or else undefined behavior results.
//
p_.invoke(ec);
return;
}

В дни до 1.66 я мог просто подключить функцию следующим образом:

template <Function, Handler>
friend bool asio_handler_is_continuation(echo_op<Function, Handler>* handler)
{
using boost::asio::asio_handler_is_continuation;
return handler.p_->step == 1 ||
asio_handler_is_continuation(std::addressof(handler.p_->handler()));
}

внутри декларации echo_op,

Начиная с Boost 1.66, приведенный выше код вряд ли будет иметь какое-либо влияние (без BOOST_ASIO_NO_DEPRECATION макро). Так что я должен использовать defer,

Но с тех пор boost::asio::async_read_until имеет гарантия что «вызов обработчика будет выполнен способом, эквивалентным использованию boost :: asio :: io_context :: post ().», *this не будет вызываться с помощью deferто есть как продолжение.

Есть ли обходной путь, который делает boost::asio::async_read_until вызвать обработчик, используя defer? И есть ли хорошие примеры, которые используют defer функционировать?

3

Решение

Это озадачило меня и в прошлом.

Executor::defer а также Executor::post оба выполняют одну и ту же операцию, за исключением этой заметки:

Примечание. Хотя требования, предъявляемые к отложенному переносу, идентичны требованиям post, использование post передает предпочтение тому, что вызывающая сторона не блокирует первый шаг прогресса f1, тогда как defer передает предпочтение, что вызывающая сторона блокирует первый шаг f1. Одним из применений defer является передача намерения вызывающей стороны, что f1 является продолжением текущего контекста вызова. Исполнитель может использовать эту информацию для оптимизации или иной настройки способа вызова f1. —Конечная записка

https://www.boost.org/doc/libs/1_67_0/doc/html/boost_asio/reference/Executor1.html

Таким образом, кажется, что ответственность за продолжение цепочки была сделана деталью реализации Executor модель.

Что, насколько я могу судить, означает, что все, что вам нужно сделать, это вызвать бесплатную функцию defer(executor, handler) и исполнитель будет «делать правильные вещи»

Обновить:

Нашел некоторую документацию, которая показывает, как связать обработчики через конечного исполнителя:

источник документации: https://github.com/chriskohlhoff/asio-tr2/blob/master/doc/executors.qbk

пример: https://github.com/chriskohlhoff/executors/blob/v0.2-branch/src/examples/executor/async_op_2.cpp

см. строки 38+ в async_op_2.cpp

4

Другие решения

Поиграв немного, получается, что asio_handler_is_continuation не считается устаревшим; и нет возможности заменить его defer В настоящее время.

Перенаправить любой post звонки в deferЯ предоставил следующий пользовательский исполнитель:

template<typename UnderlyingExecutor, typename std::enable_if<boost::asio::is_executor<UnderlyingExecutor>::value, int>::type = 0>
class continuation_executor
{
private:
UnderlyingExecutor _ex;

public:

continuation_executor(UnderlyingExecutor ex)
:_ex(ex){}

template<class Function, class Allocator>
void post(Function f, Allocator a)
{
std::cout<<"Redirected to defer()"<<std::endl;
_ex.defer(BOOST_ASIO_MOVE_CAST(Function)(f),a);
}

template<class Function, class Allocator>
void defer(Function f, Allocator a)
{
std::cout<<"defer() called"<<std::endl;
_ex.defer(BOOST_ASIO_MOVE_CAST(Function)(f),a);
}

template<class Function, class Allocator>
void dispatch(Function f, Allocator a)
{
std::cout<<"dispatch() called"<<std::endl;
_ex.dispatch(BOOST_ASIO_MOVE_CAST(Function)(f),a);
}

auto context() -> decltype(_ex.context())
{
return _ex.context();
}

void on_work_started()
{
_ex.on_work_started();
}
void on_work_finished()
{
_ex.on_work_finished();
}
};

Это действительно тривиальный исполнитель, полностью полагаясь на основного исполнителя, с continuation_executor::post который перенаправляет в основного исполнителя defer,

Но когда я передаю обработчик async_read_some с чем-то вроде bind_executor(conti_exec, handler)Я получаю следующий вывод:

dispatch() called

Таким образом, переданный обработчик не запланирован через post(); это запланировано некоторыми другими средствами. Конкретно, встроенная асинхронная функция, такая как asio::async_read_some планировать внутренний объект операции через scheduler::post_immediate_completion, затем io_context::run выполняет операцию.

По завершении асинхронной операции, complete метод объекта операции вызывается для выполнения предоставленного пользователем обработчика. Тот complete метод, по крайней мере, в текущей реализации, использует связанный исполнитель dispatch метод для запуска обработчика. Там нет места для выше крюка. Так что это совершенно мрачно; попытка использовать defer вместо asio_handler_is_continuation не повезло.

То, что я сказал на мой вопрос: «Начиная с Boost 1.66, приведенный выше код вряд ли будет иметь какой-либо эффект (без макроса BOOST_ASIO_NO_DEPRECATION).», Это совершенно неверно. asio_handler_is_continuation все еще в силе, и это не устарел с 1.67.

Это доказательство того, что asio_handler_is_continuation все еще в силе:

  // Start an asynchronous send. The data being sent must be valid for the
// lifetime of the asynchronous operation.
template <typename ConstBufferSequence, typename Handler>
void async_send(base_implementation_type& impl,
const ConstBufferSequence& buffers,
socket_base::message_flags flags, Handler& handler)
{
bool is_continuation =
boost_asio_handler_cont_helpers::is_continuation(handler);

// Allocate and construct an operation to wrap the handler.
typedef reactive_socket_send_op<ConstBufferSequence, Handler> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
op::ptr::allocate(handler), 0 };
p.p = new (p.v) op(impl.socket_, impl.state_, buffers, flags, handler);

BOOST_ASIO_HANDLER_CREATION((reactor_.context(), *p.p, "socket",
&impl, impl.socket_, "async_send"));

start_op(impl, reactor::write_op, p.p, is_continuation, true,
((impl.state_ & socket_ops::stream_oriented)
&& buffer_sequence_adapter<boost::asio::const_buffer,
ConstBufferSequence>::all_empty(buffers)));
p.v = p.p = 0;
}

Обратите внимание, что он использует boost_asio_handler_cont_helpers выяснить, является ли обработчик продолжением. boost_asio_handler_cont_helpers внутренне вызывает asio_handler_is_continuation,

async_send используется async_write_some внутренне. Я не проверял все встроенные асинхронные задачи, которые предоставляет библиотека asio, но я вполне уверен, что другие асинхронные задачи выполняют свой обработчик таким же образом.

Итак, если вы хотите, чтобы встроенные асинхронные задачи выполняли ваш обработчик как продолжение, вам придется положиться на asio_handler_is_continuation, defer не собирается полностью замени это! defer может использоваться только тогда, когда вы планируете свой обработчик непосредственно из вашего кода.

1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector