Home | History | Annotate | Download | only in threading
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      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/bind.h"
     10 #include "base/debug/leak_annotations.h"
     11 #include "base/logging.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/sequenced_task_runner.h"
     14 #include "base/threading/sequenced_task_runner_handle.h"
     15 
     16 namespace base {
     17 
     18 namespace {
     19 
     20 class PostTaskAndReplyRelay {
     21  public:
     22   PostTaskAndReplyRelay(const Location& from_here,
     23                         OnceClosure task,
     24                         OnceClosure reply)
     25       : from_here_(from_here),
     26         task_(std::move(task)),
     27         reply_(std::move(reply)) {}
     28   PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default;
     29 
     30   ~PostTaskAndReplyRelay() {
     31     if (reply_) {
     32       // This can run:
     33       // 1) On origin sequence, when:
     34       //    1a) Posting |task_| fails.
     35       //    1b) |reply_| is cancelled before running.
     36       //    1c) The DeleteSoon() below is scheduled.
     37       // 2) On destination sequence, when:
     38       //    2a) |task_| is cancelled before running.
     39       //    2b) Posting |reply_| fails.
     40 
     41       if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
     42         // Case 2a) or 2b).
     43         //
     44         // Destroy callbacks asynchronously on |reply_task_runner| since their
     45         // destructors can rightfully be affine to it. As always, DeleteSoon()
     46         // might leak its argument if the target execution environment is
     47         // shutdown (e.g. MessageLoop deleted, TaskScheduler shutdown).
     48         //
     49         // Note: while it's obvious why |reply_| can be affine to
     50         // |reply_task_runner|, the reason that |task_| can also be affine to it
     51         // is that it if neither tasks ran, |task_| may still hold an object
     52         // which was intended to be moved to |reply_| when |task_| ran (such an
     53         // object's destruction can be affine to |reply_task_runner_| -- e.g.
     54         // https://crbug.com/829122).
     55         auto relay_to_delete =
     56             std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
     57         ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
     58         reply_task_runner_->DeleteSoon(from_here_, std::move(relay_to_delete));
     59       }
     60 
     61       // Case 1a), 1b), 1c).
     62       //
     63       // Callbacks will be destroyed synchronously at the end of this scope.
     64     } else {
     65       // This can run when both callbacks have run or have been moved to another
     66       // PostTaskAndReplyRelay instance. If |reply_| is null, |task_| must be
     67       // null too.
     68       DCHECK(!task_);
     69     }
     70   }
     71 
     72   // No assignment operator because of const members.
     73   PostTaskAndReplyRelay& operator=(PostTaskAndReplyRelay&&) = delete;
     74 
     75   // Static function is used because it is not possible to bind a method call to
     76   // a non-pointer type.
     77   static void RunTaskAndPostReply(PostTaskAndReplyRelay relay) {
     78     DCHECK(relay.task_);
     79     std::move(relay.task_).Run();
     80 
     81     // Keep a reference to the reply TaskRunner for the PostTask() call before
     82     // |relay| is moved into a callback.
     83     scoped_refptr<SequencedTaskRunner> reply_task_runner =
     84         relay.reply_task_runner_;
     85 
     86     reply_task_runner->PostTask(
     87         relay.from_here_,
     88         BindOnce(&PostTaskAndReplyRelay::RunReply, std::move(relay)));
     89   }
     90 
     91  private:
     92   // Static function is used because it is not possible to bind a method call to
     93   // a non-pointer type.
     94   static void RunReply(PostTaskAndReplyRelay relay) {
     95     DCHECK(!relay.task_);
     96     DCHECK(relay.reply_);
     97     std::move(relay.reply_).Run();
     98   }
     99 
    100   const Location from_here_;
    101   OnceClosure task_;
    102   OnceClosure reply_;
    103   const scoped_refptr<SequencedTaskRunner> reply_task_runner_ =
    104       SequencedTaskRunnerHandle::Get();
    105 
    106   DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyRelay);
    107 };
    108 
    109 }  // namespace
    110 
    111 namespace internal {
    112 
    113 bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here,
    114                                             OnceClosure task,
    115                                             OnceClosure reply) {
    116   DCHECK(task) << from_here.ToString();
    117   DCHECK(reply) << from_here.ToString();
    118 
    119   return PostTask(from_here,
    120                   BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
    121                            PostTaskAndReplyRelay(from_here, std::move(task),
    122                                                  std::move(reply))));
    123 }
    124 
    125 }  // namespace internal
    126 
    127 }  // namespace base
    128