summaryrefslogtreecommitdiffstats
path: root/chromium/base/threading/post_task_and_reply_impl.cc
blob: cc802746ee41fa7290d56e507ef18de9a95b9092 (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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Copyright (c) 2011 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 "base/threading/post_task_and_reply_impl.h"

#include <utility>

#include "base/bind.h"
#include "base/debug/leak_annotations.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/sequenced_task_runner_handle.h"

namespace base {

namespace {

class PostTaskAndReplyRelay {
 public:
  PostTaskAndReplyRelay(const Location& from_here,
                        OnceClosure task,
                        OnceClosure reply,
                        scoped_refptr<SequencedTaskRunner> reply_task_runner)
      : from_here_(from_here),
        task_(std::move(task)),
        reply_(std::move(reply)),
        reply_task_runner_(std::move(reply_task_runner)) {}
  PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default;

  // It is important that |reply_| always be deleted on the origin sequence
  // (|reply_task_runner_|) since its destructor can be affine to it. More
  // sutbly, it is also important that |task_| be destroyed on the origin
  // sequence when it fails to run. This is because |task_| can own state which
  // is affine to |reply_task_runner_| and was intended to be handed to
  // |reply_|, e.g. https://crbug.com/829122. Since |task_| already needs to
  // support deletion on the origin sequence (since the initial PostTask can
  // always fail), it's safer to delete it there when PostTask succeeds but
  // |task_| is later prevented from running.
  //
  // PostTaskAndReplyRelay's move semantics along with logic in this destructor
  // enforce the above semantics in all the following cases :
  //  1) Posting |task_| fails right away on the origin sequence:
  //    a) |reply_task_runner_| is null (i.e. during late shutdown);
  //    b) |reply_task_runner_| is set.
  //  2) ~PostTaskAndReplyRelay() runs on the destination sequence:
  //    a) RunTaskAndPostReply() is cancelled before running;
  //    b) RunTaskAndPostReply() is skipped on shutdown;
  //    c) Posting RunReply() fails.
  //  3) ~PostTaskAndReplyRelay() runs on the origin sequence:
  //    a) RunReply() is cancelled before running;
  //    b) RunReply() is skipped on shutdown;
  //    c) The DeleteSoon() posted by (2) runs.
  //  4) ~PostTaskAndReplyRelay() should no-op:
  //    a) This relay was moved to another relay instance;
  //    b) RunReply() ran and completed this relay's mandate.
  ~PostTaskAndReplyRelay() {
    // Case 1a and 4a:
    if (!reply_task_runner_) {
      DCHECK_EQ(task_.is_null(), reply_.is_null());
      return;
    }

    // Case 4b:
    if (!reply_) {
      DCHECK(!task_);
      return;
    }

    // Case 2:
    if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
      DCHECK(reply_);

      SequencedTaskRunner* reply_task_runner_raw = reply_task_runner_.get();
      auto relay_to_delete =
          std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
      // In case 2c, posting the DeleteSoon will also fail and |relay_to_delete|
      // will be leaked. This only happens during shutdown and leaking is better
      // than thread-unsafe execution.
      ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
      reply_task_runner_raw->DeleteSoon(from_here_, std::move(relay_to_delete));
      return;
    }

    // Case 1b and 3: Any remaining state will be destroyed synchronously at the
    // end of this scope.
  }

  // No assignment operator because of const member.
  PostTaskAndReplyRelay& operator=(PostTaskAndReplyRelay&&) = delete;

  // Static function is used because it is not possible to bind a method call to
  // a non-pointer type.
  static void RunTaskAndPostReply(PostTaskAndReplyRelay relay) {
    DCHECK(relay.task_);
    std::move(relay.task_).Run();

    // Keep a pointer to the reply TaskRunner for the PostTask() call before
    // |relay| is moved into a callback.
    SequencedTaskRunner* reply_task_runner_raw = relay.reply_task_runner_.get();

    reply_task_runner_raw->PostTask(
        relay.from_here_,
        BindOnce(&PostTaskAndReplyRelay::RunReply, std::move(relay)));
  }

 private:
  // Static function is used because it is not possible to bind a method call to
  // a non-pointer type.
  static void RunReply(PostTaskAndReplyRelay relay) {
    DCHECK(!relay.task_);
    DCHECK(relay.reply_);
    std::move(relay.reply_).Run();
  }

  const Location from_here_;
  OnceClosure task_;
  OnceClosure reply_;
  // Not const to allow moving.
  scoped_refptr<SequencedTaskRunner> reply_task_runner_;

  DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyRelay);
};

}  // namespace

namespace internal {

bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here,
                                            OnceClosure task,
                                            OnceClosure reply) {
  DCHECK(task) << from_here.ToString();
  DCHECK(reply) << from_here.ToString();

  const bool has_sequenced_context = SequencedTaskRunnerHandle::IsSet();

  const bool post_task_success = PostTask(
      from_here,
      BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
               PostTaskAndReplyRelay(
                   from_here, std::move(task), std::move(reply),
                   has_sequenced_context ? SequencedTaskRunnerHandle::Get()
                                         : nullptr)));

  // PostTaskAndReply() requires a SequencedTaskRunnerHandle to post the reply.
  // Having no SequencedTaskRunnerHandle is allowed when posting the task fails,
  // to simplify calls during shutdown (https://crbug.com/922938).
  CHECK(has_sequenced_context || !post_task_success);

  return post_task_success;
}

}  // namespace internal

}  // namespace base