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
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141

media / mojo / services / deferred_destroy_unique_receiver_set.h [blame]

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MEDIA_MOJO_SERVICES_DEFERRED_DESTROY_UNIQUE_RECEIVER_SET_H_
#define MEDIA_MOJO_SERVICES_DEFERRED_DESTROY_UNIQUE_RECEIVER_SET_H_

#include <stdint.h>

#include <map>
#include <memory>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/task/bind_post_task.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"

namespace media {

// A class that can be deferred destroyed by its owner. For example, when used
// in DeferredDestroyUniqueReceiverSet.
template <typename Interface>
class DeferredDestroy : public Interface {
 public:
  // Runs the |destroy_cb| to notify that it's okay to destroy |this|. The
  // callback can be called synchronously. |this| will always be destroyed
  // asynchronously after running |destroy_cb| to avoid reentrance issues.
  virtual void OnDestroyPending(base::OnceClosure destroy_cb) = 0;
};

// Similar to mojo::UniqueReceiverSet, but provide a way to defer the
// destruction of the interface implementation:
// - When disconnection happened on a receiver, the receiver is immediately
// destroyed and removed from the set. The interface implementation will be
// destroyed when the DestroyCallback is called.
// - When the DeferredDestroyUniqueReceiverSet is destructed, all outstanding
// receivers and interface implementations in the set are destroyed immediately
// without any deferral.
template <typename Interface>
class DeferredDestroyUniqueReceiverSet {
 public:
  // Converts a delete callback to a deleter. If the callback is null or has
  // been cancelled, callback bound with invalidated weak pointer, the pointer
  // will be deleted with "delete" immediately.
  class Deleter {
   public:
    using DeleteCallback =
        base::RepeatingCallback<void(std::unique_ptr<Interface>)>;

    Deleter() = default;
    explicit Deleter(DeleteCallback delete_cb)
        : delete_cb_(std::move(delete_cb)) {}

    void operator()(Interface* p) {
      // Immediately wrap |p| into a unique_ptr to avoid any potential leak.
      auto ptr = base::WrapUnique<Interface>(p);

      // Can be cancelled during DeferredDestroyUniqueReceiverSet destruction.
      if (delete_cb_ && !delete_cb_.IsCancelled())
        delete_cb_.Run(std::move(ptr));
      else
        ptr.reset();
    }

   private:
    DeleteCallback delete_cb_;
  };

  DeferredDestroyUniqueReceiverSet() {}

  DeferredDestroyUniqueReceiverSet(const DeferredDestroyUniqueReceiverSet&) =
      delete;
  DeferredDestroyUniqueReceiverSet& operator=(
      const DeferredDestroyUniqueReceiverSet&) = delete;

  void Add(std::unique_ptr<DeferredDestroy<Interface>> impl,
           mojo::PendingReceiver<Interface> receiver) {
    // Wrap the pointer into a unique_ptr with a deleter.
    Deleter deleter(base::BindRepeating(
        &DeferredDestroyUniqueReceiverSet::OnReceiverRemoved,
        weak_factory_.GetWeakPtr()));
    std::unique_ptr<Interface, Deleter> impl_with_deleter(impl.release(),
                                                          deleter);

    receivers_.Add(std::move(impl_with_deleter), std::move(receiver));
  }

  // TODO(xhwang): Add RemoveReceiver() if needed.

  void CloseAllReceivers() {
    weak_factory_.InvalidateWeakPtrs();
    receivers_.Clear();
    unbound_impls_.clear();
  }

  bool empty() const { return receivers_.empty(); }

  size_t size() const { return receivers_.size(); }

  size_t unbound_size() const { return unbound_impls_.size(); }

 private:
  void OnReceiverRemoved(std::unique_ptr<Interface> ptr) {
    DVLOG(1) << __func__;

    id_++;

    // The cast is safe since AddReceiver() takes DeferredDestroy<Interface>.
    auto* impl_ptr = static_cast<DeferredDestroy<Interface>*>(ptr.get());

    // Put the |ptr| in the map before calling OnDestroyPending() because the
    // callback could be called synchronously.
    unbound_impls_[id_] = std::move(ptr);

    // Use base::BindPostTaskToCurrentDefault() to force post the destroy
    // callback. This is needed because the callback may be called directly in
    // the same stack where the implementation is being destroyed.
    impl_ptr->OnDestroyPending(base::BindPostTaskToCurrentDefault(
        base::BindOnce(&DeferredDestroyUniqueReceiverSet::OnDestroyable,
                       weak_factory_.GetWeakPtr(), id_)));
  }

  void OnDestroyable(int id) {
    DVLOG(1) << __func__;
    unbound_impls_.erase(id);
  }

  uint32_t id_ = 0;
  std::map<uint32_t, std::unique_ptr<Interface>> unbound_impls_;
  mojo::UniqueReceiverSet<Interface, void, Deleter> receivers_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<DeferredDestroyUniqueReceiverSet> weak_factory_{this};
};

}  // namespace media

#endif  // MEDIA_MOJO_SERVICES_DEFERRED_DESTROY_UNIQUE_RECEIVER_SET_H_