166 lines
6.4 KiB
C++
166 lines
6.4 KiB
C++
// 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/functional/bind.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/task/sequenced_task_runner.h"
|
|
#include "base/task/thread_pool/thread_pool_instance.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;
|
|
|
|
PostTaskAndReplyRelay(const PostTaskAndReplyRelay&) = delete;
|
|
PostTaskAndReplyRelay& operator=(const PostTaskAndReplyRelay&) = delete;
|
|
|
|
// 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_);
|
|
// 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.
|
|
}
|
|
|
|
// 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();
|
|
|
|
const Location from_here = relay.from_here_;
|
|
reply_task_runner_raw->PostTask(
|
|
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_;
|
|
};
|
|
|
|
} // 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 = SequencedTaskRunner::HasCurrentDefault();
|
|
|
|
const bool post_task_success = PostTask(
|
|
from_here, BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
|
|
PostTaskAndReplyRelay(
|
|
from_here, std::move(task), std::move(reply),
|
|
has_sequenced_context
|
|
? SequencedTaskRunner::GetCurrentDefault()
|
|
: nullptr)));
|
|
|
|
// PostTaskAndReply() requires a SequencedTaskRunner::CurrentDefaultHandle to
|
|
// post the reply. Having no SequencedTaskRunner::CurrentDefaultHandle 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
|