xref: /aosp_15_r20/external/cronet/base/threading/post_task_and_reply_impl.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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