Интересно, почему я не могу просто захватить ссылку asio :: handler_type для сопрограммы

Я играю с asio, используя сопрограмму, и хотел бы проверить, как может быть вызвана асинхронная функция. У меня есть следующий код:

void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
using handler_type = boost::asio::handler_type<decltype(yield), void()>::type;

handler_type handler(std::forward<handler_type>(yield));
boost::asio::async_result<decltype(handler)> result(handler);

auto timer(std::make_shared<boost::asio::deadline_timer>(io_service, boost::posix_time::seconds(1)));
// the program crashes if I use &handler here
timer->async_wait([&handler](const boost::system::error_code) {
std::cout << "enter" << std::endl;
handler();
std::cout << "done" << std::endl;
});
result.get();

std::cout << "function finished" << std::endl;

return;
}

int main()
{
boost::asio::io_service io_service;

boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield) {
std::cout << "hello" << std::endl;
async_foo(io_service, yield);
std::cout << "world" << std::endl;
});
io_service.run();
return 0;
}

Странно, что если я положу &Обработчик в списке перехвата потока выполнения будет испорчен, и затем он попадет в ошибку сегментации. Но он работает без каких-либо проблем, если я использую вместо этого «обработчик» (тогда, конечно, мне нужна копия в лямбда-выражении).

Обыскал и не смог найти ничего связанного. Заранее благодарю за любую помощь.

0

Решение

Как Таннер очень хорошо объясняет Вот:

  • В то время как spawn()
    добавляет работу к io_service (обработчик, который запустится и перейдет к
    сопрограмма), сама сопрограмма не работает. Чтобы предотвратить
    io_service цикл событий от завершения, пока сопрограмма выдающаяся,
    может быть необходимо добавить работу к io_service прежде чем уступить.

Если вы добавите дополнительную линию трассировки после run():

io_service.run();
std::cout << "BYE" << std::endl;

Затем, запустите несколько раз, пока вам не повезет, что вы не получите SEGV на ранней стадии, и вы увидите результат, подобный следующему:

hello
enter
done
BYE

Действительно, io_service возвращает, разрушая ожидающие операции / обработчики, что также разрушает стековый контекст coro¹. Разматывание этого стека уничтожает (локальные) переменные, которые живут в этом стеке:

  • обработчик
  • результат
  • таймер

И с тех пор timer не фиксируется в обработчике завершения для async_wait, таймер просто отменяется, но все еще пытается вызвать обработчик завершения, который теперь ссылается на уже не существующую переменную стека handler,

копирование handler по-видимому² поддерживает коро в живых.


Чтобы ответить на основной вопрос «и хотел бы проверить, как асинхронная функция может быть вызвана» Я бы предложил более простую идиому:

void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(1));
boost::system::error_code ec;

timer.async_wait(yield[ec]);

Видеть это Жить на Колиру


¹ -fsanitize=address подтверждает это
² я знаю, что он содержит weak_ptr в контексте сопрограммы, так что, возможно, Асио внутренне запирает это в shared_ptrЯ не слишком уверен в деталях реализации

0

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

Других решений пока нет …

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