Vamos supor que eu tenha uma corrotina em execução em uma cadeia. Gostaria que a conclusão fosse enviada para o executor pai da cadeia (ou seja, a thread_pool
), mas também deixando a cadeia. Qual é a melhor maneira de obter esse resultado sem incorrer em sobrecarga indevida?
Aqui está um exemplo de código que ilustra o problema. Parece-me que isso f2
gera sobrecarga e quebra a localidade do raciocínio do código. Mas f3
não consigo fazer o que quero no caso específico em que o executor vinculado é o pai da cadeia. Vincular f3
a qualquer outro executor não relacionado teria sido suficiente, mas não ao pai da cadeia.
boost::asio::awaitable<void> f2(auto& pool, auto& strand)
{
assert(strand.running_in_this_thread());
co_await boost::asio::post(bind_executor(pool, boost::asio::deferred));
}
boost::asio::awaitable<void> f3([[maybe_unused]] auto& pool, auto& strand)
{
assert(strand.running_in_this_thread());
co_return;
}
int main(int argc, char* argv[])
{
boost::asio::thread_pool pool{std::thread::hardware_concurrency()};
auto strand = boost::asio::make_strand(pool);
co_spawn(strand, f2(pool, strand), bind_executor(pool, [&](std::exception_ptr exception) {
assert(!strand.running_in_this_thread()); // Succeeds, but required an explicit `post` at the end of `f2`
}));
co_spawn(strand, f3(pool, strand), bind_executor(pool, [&](std::exception_ptr exception) {
assert(strand.running_in_this_thread()); // actual result : succeeds
assert(!strand.running_in_this_thread()); // expected result, but currently fails
}));
pool.join();
}
Editar
@sehe Acho que não entendi. Primeiro, no seu código de exemplo, f1
e f3
são idênticos. Não tenho certeza se são para ser idênticos ou não. Segundo, a conclusão passada para co_spawn
está sempre vinculada a uma string, mas nunca ao executor pai da string (o pool).
Tentarei expressar meu problema de uma maneira diferente. Posso simplificar o código de exemplo removendo completamente as corrotinas. Despachar para pool
de dentro strand
executa em linha porque é verdade que já estamos dentro strand
e dentro de pool
. Se eu quiser liberar strand, posso parar de usar dispatch
e usar post
or defer
.
Voltando às corrotinas, meu entendimento atual é que a awaitable
continuação será despachada para o executor associado. Ou seja, ela será executada em linha se o meu executor associado for o pai do comando, strand
porque eu já estou executando dentro dele.
No meu código de exemplo inicial, f2
ele sai da cadeia adicionando um extra bem post
no final. Depois, a conclusão da corrotina é despachada para a mesma cadeia pool
e nunca retorna em strand
. Mas me parece que há uma sobrecarga indevida em ter ambos post
e dispatch
para a mesma continuação.
https://gcc.godbolt.org/z/c98s8EMs9
int main()
{
boost::asio::thread_pool pool{1};
auto strand = boost::asio::make_strand(pool);
// Similar to my initial `f3`
boost::asio::dispatch(bind_executor(strand, [&]() {
boost::asio::dispatch(bind_executor(pool, [&]() {
assert(strand.running_in_this_thread());
}));
}));
// Similar to my initial `f2`
boost::asio::dispatch(bind_executor(strand, [&]() {
boost::asio::post(bind_executor(pool, [&]() {
boost::asio::dispatch(bind_executor(pool, [&]() {
assert(!strand.running_in_this_thread());
}));
}));
}));
// What I'm hoping for is the following
boost::asio::dispatch(bind_executor(strand, [&]() {
boost::asio::post(bind_executor(pool, [&]() {
assert(!strand.running_in_this_thread());
}));
}));
pool.join();
}