summaryrefslogtreecommitdiff
path: root/base/threading/post_task_and_reply_impl.cc
blob: f27e5fffe24d71254cdd2f1347753d39be4a1153 (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
// Copyright 2011 The Chromium Authors
// 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/check_op.h"
#include "base/debug/leak_annotations.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool/thread_pool_instance.h"

namespace base::internal {

PostTaskAndReplyRelay::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(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::~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_);
    // Allow this task to be leaked on shutdown even if `reply_task_runner_`
    // has the TaskShutdownBehaviour::BLOCK_SHUTDOWN trait. Without `fizzler`,
    // such a task runner would DCHECK when posting to `reply_task_runner_`
    // after shutdown. Ignore this DCHECK as the poster isn't in control when
    // its Callback is destroyed late into shutdown. Ref. crbug.com/1375270.
    base::ThreadPoolInstance::ScopedFizzleBlockShutdownTasks fizzler;

    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.
}

}  // namespace base::internal