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

base / threading / post_task_and_reply_impl.cc [blame]

// 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