summaryrefslogtreecommitdiff
path: root/chromium/net/dns/serial_worker.h
blob: c226fe6d010c2dacc97f1ec3a8cd6f816192e222 (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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2012 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.

#ifndef NET_DNS_SERIAL_WORKER_H_
#define NET_DNS_SERIAL_WORKER_H_

#include <memory>

#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/task_traits.h"
#include "base/timer/timer.h"
#include "net/base/backoff_entry.h"
#include "net/base/net_export.h"

namespace net {

// `SerialWorker` executes a job on `ThreadPool` serially -- **one at a time**.
// On `WorkNow()`, a `WorkItem` is created using `CreateWorkItem()` and sent to
// the `ThreadPool`. There, a call to `DoWork()` is made. On completion of work,
// `OnWorkFinished()` is called on the origin thread (if the `SerialWorker` is
// still alive), passing back the `WorkItem` to allow retrieving any results or
// passed objects. If `WorkNow()` is called (1 or more times) while a `WorkItem`
// is already under way, after completion of the work and before any call is
// made to `OnWorkFinished()` the same `WorkItem` will be passed back to the
// `ThreadPool`, and `DoWork()` will be called once more.
//
// If |OnWorkFinished| returns a failure and |max_number_of_retries|
// is non-zero, retries will be scheduled according to the |backoff_policy|.
// A default backoff policy is used if one is not provided.
//
// This behavior is designed for updating a result after some trigger, for
// example reading a file once FilePathWatcher indicates it changed.
//
// Derived classes should store results of work in the `WorkItem` and retrieve
// results from it when passed back to `OnWorkFinished()`. The `SerialWorker` is
// guaranteed to only run one `WorkItem` at a time, always passing it back to
// `OnWorkFinished()` before calling `CreateWorkItem()` again. Therefore, a
// derived class may safely pass objects between `WorkItem`s, or even reuse the
// same `WorkItem`, to allow storing helper objects directly in the `WorkItem`.
// However, it is not guaranteed that the `SerialWorker` will remain alive while
// the `WorkItem` runs. Therefore, the `WorkItem` should never access any memory
// owned by the `SerialWorker` or derived class.
class NET_EXPORT_PRIVATE SerialWorker {
 public:
  // A work item that will be passed to and run on the `ThreadPool` (potentially
  // multiple times if the `SerialWorker` needs to run again immediately) and
  // then passed back to the origin thread on completion. Expected usage is to
  // store any parameters, results, and helper objects in the `WorkItem` and
  // read results from it when passed back to the origin thread.
  //
  // `SerialWorker` calls `FollowupWork()` *on the origin thread* after calling
  // `DoWork()` on the `ThreadPool` to asynchronously handle any work that must
  // be part of the serialization but that cannot run on a worker thread.
  class NET_EXPORT_PRIVATE WorkItem {
   public:
    virtual ~WorkItem() = default;
    virtual void DoWork() = 0;
    virtual void FollowupWork(base::OnceClosure closure);
  };

  explicit SerialWorker(
      int max_number_of_retries = 0,
      const net::BackoffEntry::Policy* backoff_policy = nullptr);

  SerialWorker(const SerialWorker&) = delete;
  SerialWorker& operator=(const SerialWorker&) = delete;

  // Unless already scheduled, post |DoWork| to ThreadPool.
  // Made virtual to allow mocking.
  virtual void WorkNow();

  // Stop scheduling jobs.
  void Cancel();

  bool IsCancelled() const { return state_ == State::kCancelled; }

  // Allows tests to inspect the current backoff/retry state.
  const BackoffEntry& GetBackoffEntryForTesting() const;
  const base::OneShotTimer& GetRetryTimerForTesting() const;

 protected:
  // protected to allow sub-classing, but prevent deleting
  virtual ~SerialWorker();

  // Create a new WorkItem to be passed to and run on the ThreadPool.
  virtual std::unique_ptr<WorkItem> CreateWorkItem() = 0;

  // Executed on origin thread after `WorkItem` completes.
  // Must return true on success.
  virtual bool OnWorkFinished(std::unique_ptr<WorkItem> work_item) = 0;

  base::WeakPtr<SerialWorker> AsWeakPtr();

  // Used to verify that the constructor, WorkNow(), Cancel() and
  // OnWorkJobFinished() are called on the same sequence.
  SEQUENCE_CHECKER(sequence_checker_);

 private:
  enum class State {
    kCancelled = -1,
    kIdle = 0,
    kWorking,  // |DoWorkJob| posted to ThreadPool, until |OnWorkJobFinished|
    kPending,  // |WorkNow| while WORKING, must re-do work
  };

  void WorkNowInternal();

  // Called on the origin thread after `WorkItem::DoWork()` completes.
  void OnDoWorkFinished(std::unique_ptr<WorkItem> work_item);

  // Called on the origin thread after `WorkItem::FollowupWork()` completes.
  void OnFollowupWorkFinished(std::unique_ptr<WorkItem> work_item);

  void RerunWork(std::unique_ptr<WorkItem> work_item);

  State state_ = State::kIdle;

  // Max retries and backoff entry to control timing.
  const int max_number_of_retries_;
  BackoffEntry backoff_entry_;
  base::OneShotTimer retry_timer_;

  base::WeakPtrFactory<SerialWorker> weak_factory_{this};
};

}  // namespace net

#endif  // NET_DNS_SERIAL_WORKER_H_