| // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| // 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/bind.h" | |
| #include "base/debug/leak_annotations.h" | |
| #include "base/logging.h" | |
| #include "base/memory/ref_counted.h" | |
| #include "base/sequenced_task_runner.h" | |
| #include "base/threading/sequenced_task_runner_handle.h" | |
| namespace base { | |
| namespace { | |
| class PostTaskAndReplyRelay { | |
| public: | |
| PostTaskAndReplyRelay(const Location& from_here, | |
| OnceClosure task, | |
| OnceClosure reply) | |
| : from_here_(from_here), | |
| task_(std::move(task)), | |
| reply_(std::move(reply)) {} | |
| PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default; | |
| ~PostTaskAndReplyRelay() { | |
| if (reply_) { | |
| // This can run: | |
| // 1) On origin sequence, when: | |
| // 1a) Posting |task_| fails. | |
| // 1b) |reply_| is cancelled before running. | |
| // 1c) The DeleteSoon() below is scheduled. | |
| // 2) On destination sequence, when: | |
| // 2a) |task_| is cancelled before running. | |
| // 2b) Posting |reply_| fails. | |
| if (!reply_task_runner_->RunsTasksInCurrentSequence()) { | |
| // Case 2a) or 2b). | |
| // | |
| // Destroy callbacks asynchronously on |reply_task_runner| since their | |
| // destructors can rightfully be affine to it. As always, DeleteSoon() | |
| // might leak its argument if the target execution environment is | |
| // shutdown (e.g. MessageLoop deleted, TaskScheduler shutdown). | |
| // | |
| // Note: while it's obvious why |reply_| can be affine to | |
| // |reply_task_runner|, the reason that |task_| can also be affine to it | |
| // is that it if neither tasks ran, |task_| may still hold an object | |
| // which was intended to be moved to |reply_| when |task_| ran (such an | |
| // object's destruction can be affine to |reply_task_runner_| -- e.g. | |
| // https://crbug.com/829122). | |
| auto relay_to_delete = | |
| std::make_unique<PostTaskAndReplyRelay>(std::move(*this)); | |
| ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get()); | |
| reply_task_runner_->DeleteSoon(from_here_, std::move(relay_to_delete)); | |
| } | |
| // Case 1a), 1b), 1c). | |
| // | |
| // Callbacks will be destroyed synchronously at the end of this scope. | |
| } else { | |
| // This can run when both callbacks have run or have been moved to another | |
| // PostTaskAndReplyRelay instance. If |reply_| is null, |task_| must be | |
| // null too. | |
| DCHECK(!task_); | |
| } | |
| } | |
| // No assignment operator because of const members. | |
| 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 reference to the reply TaskRunner for the PostTask() call before | |
| // |relay| is moved into a callback. | |
| scoped_refptr<SequencedTaskRunner> reply_task_runner = | |
| relay.reply_task_runner_; | |
| reply_task_runner->PostTask( | |
| relay.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_; | |
| const scoped_refptr<SequencedTaskRunner> reply_task_runner_ = | |
| SequencedTaskRunnerHandle::Get(); | |
| DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyRelay); | |
| }; | |
| } // namespace | |
| namespace internal { | |
| bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here, | |
| OnceClosure task, | |
| OnceClosure reply) { | |
| DCHECK(task) << from_here.ToString(); | |
| DCHECK(reply) << from_here.ToString(); | |
| return PostTask(from_here, | |
| BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply, | |
| PostTaskAndReplyRelay(from_here, std::move(task), | |
| std::move(reply)))); | |
| } | |
| } // namespace internal | |
| } // namespace base |