Chromium Chronicle no 1: práticas recomendadas para programação de tarefas

A equipe do Chrome tem o orgulho de apresentar o Chromium Chronicle, uma série mensal voltada especificamente para desenvolvedores do Chromium, que criam o navegador.

O foco do Chromium Chronicle será a distribuição de conhecimento técnico e práticas recomendadas para programar, criar e testar o Chrome. Nosso plano é destacar temas relevantes e úteis para os desenvolvedores do Chromium, como integridade do código, ferramentas úteis, teste de unidade, acessibilidade e muito mais. Cada artigo será escrito e editado por engenheiros do Chrome.

Estamos animados com essa nova série e esperamos que você também esteja! Vamos nessa? Confira nosso primeiro episódio abaixo.

Práticas recomendadas de agendamento de tarefas

Episódio 1:de Gabriel Charette em Montréal, PQ (abril de 2019)
Episódios anteriores

O código do Chrome que precisa de execução assíncrona no processo normalmente publica tarefas em sequências. As sequências são "linhas de execução virtuais" gerenciadas pelo Chrome e preferem criar sua própria linha de execução. Como um objeto sabe em qual sequência postar?

O que não fazer

O paradigma antigo é receber um SequencedTaskRunner do criador:

Foo::Foo(scoped_refptr backend_task_runner)
    : backend_task_runner_(std::move(backend_task_runner)) {}
O que fazer

O paradigma recomendado é criar um SequencedTaskRunner independente:

Foo::Foo()
    : backend_task_runner_(
          base::CreateSequencedTaskRunnerWithTraits({
              base::MayBlock(), base::TaskPriority::BEST_EFFORT})) {}

Isso é mais fácil de ler e gravar, porque todas as informações são locais e não há risco de interdependência com tarefas não relacionadas.

Esse paradigma também é melhor em relação aos testes. Em vez de injetar executores de tarefas manualmente, os testes podem instanciar um ambiente de tarefas controlado para gerenciar as tarefas do Foo:

class FooTest : public testing::Test {
 public
  (...)
 protected:
  base::test::TaskEnvironment task_environment_;
  Foo foo_;
};

Ter o TaskEnvironment como o principal recurso naturalmente garante que ele gerencie o ambiente de tarefas durante todo o ciclo de vida de Foo. O TaskEnvironment vai capturar a solicitação na construção de Foo para criar um SequencedTaskRunner e gerenciar as tarefas em cada FooTest.

Para testar o resultado da execução assíncrona, use o paradigma RunLoop::Run()+QuitClosure():

TEST_F(FooTest, TestAsyncWork) {
  RunLoop run_loop;
  foo_.BeginAsyncWork(run_loop.QuitClosure());
  run_loop.Run();
  EXPECT_TRUE(foo_.work_done());
}

Isso é preferível a RunUntilIdle(), que pode ser instável se a carga de trabalho assíncrona envolver uma tarefa fora do escopo do TaskEnvironment, por exemplo, um evento do sistema. Portanto, use RunUntilIdle() com cuidado.

Quer saber mais? Leia nossa documentação sobre linhas de execução e tarefas ou participe da migração para o TaskEnvironment.