1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/threading/post_task_and_reply_impl.h"
6
7 #include <utility>
8
9 #include "base/check_op.h"
10 #include "base/debug/leak_annotations.h"
11 #include "base/task/sequenced_task_runner.h"
12 #include "base/task/thread_pool/thread_pool_instance.h"
13
14 namespace base::internal {
15
PostTaskAndReplyRelay(const Location & from_here,OnceClosure task,OnceClosure reply,scoped_refptr<SequencedTaskRunner> reply_task_runner)16 PostTaskAndReplyRelay::PostTaskAndReplyRelay(
17 const Location& from_here,
18 OnceClosure task,
19 OnceClosure reply,
20 scoped_refptr<SequencedTaskRunner> reply_task_runner)
21 : from_here_(from_here),
22 task_(std::move(task)),
23 reply_(std::move(reply)),
24 reply_task_runner_(std::move(reply_task_runner)) {}
25
26 PostTaskAndReplyRelay::PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default;
27
28 // It is important that `reply_` always be deleted on the origin sequence
29 // (`reply_task_runner_`) since its destructor can be affine to it. More
30 // sutbly, it is also important that `task_` be destroyed on the origin
31 // sequence when it fails to run. This is because `task_` can own state which
32 // is affine to `reply_task_runner_` and was intended to be handed to
33 // `reply_`, e.g. https://crbug.com/829122. Since `task_` already needs to
34 // support deletion on the origin sequence (since the initial PostTask can
35 // always fail), it's safer to delete it there when PostTask succeeds but
36 // `task_` is later prevented from running.
37 //
38 // PostTaskAndReplyRelay's move semantics along with logic in this destructor
39 // enforce the above semantics in all the following cases :
40 // 1) Posting `task_` fails right away on the origin sequence:
41 // a) `reply_task_runner_` is null (i.e. during late shutdown);
42 // b) `reply_task_runner_` is set.
43 // 2) ~PostTaskAndReplyRelay() runs on the destination sequence:
44 // a) RunTaskAndPostReply() is cancelled before running;
45 // b) RunTaskAndPostReply() is skipped on shutdown;
46 // c) Posting RunReply() fails.
47 // 3) ~PostTaskAndReplyRelay() runs on the origin sequence:
48 // a) RunReply() is cancelled before running;
49 // b) RunReply() is skipped on shutdown;
50 // c) The DeleteSoon() posted by (2) runs.
51 // 4) ~PostTaskAndReplyRelay() should no-op:
52 // a) This relay was moved to another relay instance;
53 // b) RunReply() ran and completed this relay's mandate.
~PostTaskAndReplyRelay()54 PostTaskAndReplyRelay::~PostTaskAndReplyRelay() {
55 // Case 1a and 4a:
56 if (!reply_task_runner_) {
57 DCHECK_EQ(task_.is_null(), reply_.is_null());
58 return;
59 }
60
61 // Case 4b:
62 if (!reply_) {
63 DCHECK(!task_);
64 return;
65 }
66
67 // Case 2:
68 if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
69 DCHECK(reply_);
70 // Allow this task to be leaked on shutdown even if `reply_task_runner_`
71 // has the TaskShutdownBehaviour::BLOCK_SHUTDOWN trait. Without `fizzler`,
72 // such a task runner would DCHECK when posting to `reply_task_runner_`
73 // after shutdown. Ignore this DCHECK as the poster isn't in control when
74 // its Callback is destroyed late into shutdown. Ref. crbug.com/1375270.
75 base::ThreadPoolInstance::ScopedFizzleBlockShutdownTasks fizzler;
76
77 SequencedTaskRunner* reply_task_runner_raw = reply_task_runner_.get();
78 auto relay_to_delete =
79 std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
80 // In case 2c, posting the DeleteSoon will also fail and `relay_to_delete`
81 // will be leaked. This only happens during shutdown and leaking is better
82 // than thread-unsafe execution.
83 ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
84 reply_task_runner_raw->DeleteSoon(from_here_, std::move(relay_to_delete));
85 return;
86 }
87
88 // Case 1b and 3: Any remaining state will be destroyed synchronously at the
89 // end of this scope.
90 }
91
92 } // namespace base::internal
93