summaryrefslogtreecommitdiff
path: root/chromium/components/optimization_guide/core/page_content_annotation_job_executor.cc
blob: 38f617cef941c8b5e1e1c337f663d2870ab81417 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/optimization_guide/core/page_content_annotation_job_executor.h"

#include "base/barrier_closure.h"
#include "base/check_op.h"

namespace optimization_guide {

PageContentAnnotationJobExecutor::PageContentAnnotationJobExecutor() = default;
PageContentAnnotationJobExecutor::~PageContentAnnotationJobExecutor() = default;

void PageContentAnnotationJobExecutor::ExecuteJob(
    base::OnceClosure on_job_complete_callback_from_caller,
    std::unique_ptr<PageContentAnnotationJob> unique_job) {
  PageContentAnnotationJob* job = unique_job.get();
  DCHECK(job);

  base::OnceClosure my_on_job_complete_callback = base::BindOnce(
      &PageContentAnnotationJobExecutor::OnJobExecutionComplete,
      weak_ptr_factory_.GetWeakPtr(),
      std::move(on_job_complete_callback_from_caller), std::move(unique_job));
  base::RepeatingClosure on_each_done_callback =
      base::BarrierClosure(job->CountOfRemainingNonNullInputs(),
                           std::move(my_on_job_complete_callback));
  //////// Notes on why the code is architected like this:
  // A job contains many inputs which each need to be executed. A single
  // execution can take up to 1 second so that latency is multiplied by the size
  // of the job. While it is important for jobs to execute and finish decently
  // quickly, it is also important to be good stewards of the background message
  // loop which is using the same hardware thread as other operations in Chrome.
  //
  // Running all the inputs in a single background message pump could hang the
  // underlying background thread for quite a while. However, if we only send
  // one input for execution at a time then completion of the job is subject to
  // the latency from other tasks in Chrome. Both of those approaches would
  // allow us to only use the unique_ptr by passing ownership of the job between
  // threads, reducing the complexity of the code below.
  //
  // But because of the potential latency regression on either other Chrome
  // tasks or our job, we've taken the approach of making many task scheduling
  // requests at once. This will let the background task runner to figure the
  // best way to schedule everything sooner. It comes at the cost of a more
  // complex lifetime of the job, which is detailed below.

  //////// Notes on lifetimes:
  // As of this point, |on_each_done_callback| owns |on_complete_callback|
  // which owns the underlying |job| instance.
  //
  // Running |on_each_done_callback| (especially when the input is empty, see
  // below) may destroy |job|. That makes it tricky for the loop to check
  // whether to continue since we need to use some state that is not tied to the
  // lifetime of |job|. Therefore, as pretty as
  // `while (auto input = job->GetNextInput())` would be, a more traditional for
  // loop is used using simple counting. In the same way,
  // `for ( ; i < job->CountOfRemainingNonNullInputs(); )` doesn't work either,
  // since |job| will be destroyed when the loop does its last exit check.
  const size_t num_inputs = job->CountOfRemainingNonNullInputs();
  for (size_t i = 0; i < num_inputs; i++) {
    std::string input = *job->GetNextInput();
    ExecuteOnSingleInput(
        job->type(), input,
        base::BindOnce(
            &PageContentAnnotationJobExecutor::OnSingleInputExecutionComplete,
            weak_ptr_factory_.GetWeakPtr(), job, on_each_done_callback));
  }
}

void PageContentAnnotationJobExecutor::OnJobExecutionComplete(
    base::OnceClosure on_job_complete_callback_from_caller,
    std::unique_ptr<PageContentAnnotationJob> job) {
  job->OnComplete();
  // Intentionally reset |job| here to make lifetime clearer and less bug-prone.
  job.reset();

  std::move(on_job_complete_callback_from_caller).Run();
}

void PageContentAnnotationJobExecutor::OnSingleInputExecutionComplete(
    PageContentAnnotationJob* job,
    base::OnceClosure on_single_input_done_barrier_closure,
    const BatchAnnotationResult& output) {
  job->PostNewResult(output);

  // Running |on_single_input_done_barrier_closure| may destroy |job|.
  std::move(on_single_input_done_barrier_closure).Run();
}

}  // namespace optimization_guide