910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_
#define MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_

#include <iterator>
#include <optional>
#include <set>
#include <utility>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/id_type.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/runtime_features.h"

namespace mojo {

namespace internal {
struct RemoteSetElementIdTypeTag {};
}  // namespace internal

using RemoteSetElementId = base::IdTypeU32<internal::RemoteSetElementIdTypeTag>;

// Shared implementation of a set of remotes, used by both RemoteSet and
// AssociatedRemoteSet aliases (see below).
//
// A RemoteSet or AssociatedRemoteSet is a collection of remote interface
// endpoints whose lifetime is conveniently managed by the set, i.e., remotes
// are removed from the set automatically when losing a connection).
template <typename Interface,
          template <typename>
          class RemoteType,
          template <typename>
          class PendingRemoteType>
class RemoteSetImpl {
 public:
  using Storage = std::map<RemoteSetElementId, RemoteType<Interface>>;

  // An iterator definition to support range-for iteration over RemoteSet
  // objects. An iterator can be dereferenced to get at the Remote, and |id()|
  // can be called to get the element's ID (for e.g. later removal).
  struct Iterator {
    using self_type = Iterator;
    using value_type = RemoteType<Interface>;
    using reference = const value_type&;
    using pointer = const value_type*;
    using iterator_category = std::bidirectional_iterator_tag;
    using difference_type = std::ptrdiff_t;

    explicit Iterator(typename Storage::const_iterator it) : it_(it) {}

    self_type& operator++() {
      ++it_;
      return *this;
    }
    self_type operator++(int) {
      self_type result(*this);
      ++(*this);
      return result;
    }
    self_type& operator--() {
      --it_;
      return *this;
    }
    self_type operator--(int) {
      self_type result(*this);
      --(*this);
      return result;
    }

    RemoteSetElementId id() const { return it_->first; }

    reference operator*() const { return it_->second; }
    pointer operator->() const { return &it_->second; }
    bool operator==(const self_type& rhs) const { return it_ == rhs.it_; }
    bool operator!=(const self_type& rhs) const { return it_ != rhs.it_; }

   private:
    typename Storage::const_iterator it_;
  };

  RemoteSetImpl() = default;

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

  ~RemoteSetImpl() = default;

  // Adds a new remote to this set and returns a unique ID that can be used to
  // identify the remote later.
  RemoteSetElementId Add(RemoteType<Interface> remote)
    requires(!internal::kIsRuntimeFeatureGuarded<Interface>)
  {
    return AddImpl(std::move(remote));
  }

  // Adds a new remote to this set and returns a unique ID that can be used to
  // identify the remote later.
  RemoteSetElementId Add(
      PendingRemoteType<Interface> remote,
      scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr)
    requires(!internal::kIsRuntimeFeatureGuarded<Interface>)
  {
    return AddImpl(std::move(remote), std::move(task_runner));
  }

  // Adds a new remote to this set if the remote is runtime enabled and returns
  // a unique ID that can be used to identify the remote later.
  std::optional<RemoteSetElementId> Add(RemoteType<Interface> remote)
    requires(internal::kIsRuntimeFeatureGuarded<Interface>)
  {
    if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) {
      return std::nullopt;
    }
    return AddImpl(std::move(remote));
  }

  // Adds a new remote to this set if the remote is runtime enabled and returns
  // a unique ID that can be used to identify the remote later.
  std::optional<RemoteSetElementId> Add(
      PendingRemoteType<Interface> remote,
      scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr)
    requires(internal::kIsRuntimeFeatureGuarded<Interface>)
  {
    if (!internal::GetRuntimeFeature_ExpectEnabled<Interface>()) {
      return std::nullopt;
    }
    return AddImpl(std::move(remote), std::move(task_runner));
  }

  // Removes a remote from the set given |id|, if present.
  void Remove(RemoteSetElementId id) { storage_.erase(id); }
  // Similar to the method above, but also specifies a disconnect reason.
  void RemoveWithReason(RemoteSetElementId id,
                        uint32_t custom_reason_code,
                        const std::string& description) {
    auto it = storage_.find(id);
    if (it == storage_.end()) {
      return;
    }

    it->second.ResetWithReason(custom_reason_code, description);
    storage_.erase(it);
  }

  // Indicates whether a remote with the given ID is present in the set.
  bool Contains(RemoteSetElementId id) { return base::Contains(storage_, id); }

  // Returns an `Interface*` for the given ID, that can be used to issue
  // interface calls.
  Interface* Get(RemoteSetElementId id) {
    auto it = storage_.find(id);
    if (it == storage_.end())
      return nullptr;
    return it->second.get();
  }

  // Sets a callback to invoke any time a remote in the set is disconnected.
  // Note that the remote in question is already removed from the set by the
  // time the callback is run for its disconnection.
  using DisconnectHandler = base::RepeatingCallback<void(RemoteSetElementId)>;
  using DisconnectWithReasonHandler =
      base::RepeatingCallback<void(RemoteSetElementId,
                                   uint32_t /* custom_reason */,
                                   const std::string& /* description */)>;

  void set_disconnect_handler(DisconnectHandler handler) {
    disconnect_handler_ = std::move(handler);
    disconnect_with_reason_handler_.Reset();
  }

  void set_disconnect_with_reason_handler(DisconnectWithReasonHandler handler) {
    disconnect_with_reason_handler_ = std::move(handler);
    disconnect_handler_.Reset();
  }

  void Clear() { storage_.clear(); }
  void ClearWithReason(uint32_t custom_reason_code,
                       const std::string& description) {
    for (auto& [_, remote] : storage_) {
      remote.ResetWithReason(custom_reason_code, description);
    }

    Clear();
  }

  bool empty() const { return storage_.empty(); }
  size_t size() const { return storage_.size(); }

  Iterator begin() { return Iterator(storage_.begin()); }
  Iterator begin() const { return Iterator(storage_.begin()); }
  Iterator end() { return Iterator(storage_.end()); }
  Iterator end() const { return Iterator(storage_.end()); }

  void FlushForTesting() {
    // Avoid flushing while iterating over `storage_` because this map may be
    // mutated during individual flush operations.  Instead, snapshot the
    // RemoteSetElementId first, then iterate over them.
    std::vector<RemoteSetElementId> ids;
    for (const auto& entry : storage_) {
      ids.push_back(entry.first);
    }

    for (auto& id : ids) {
      auto it = storage_.find(id);
      if (it != storage_.end()) {
        it->second.FlushForTesting();
      }
    }
  }

 private:
  // Adds a new remote to this set and returns a unique ID that can be used to
  // identify the remote later.
  RemoteSetElementId AddImpl(RemoteType<Interface> remote) {
    DCHECK(remote.is_bound());
    auto id = GenerateNextElementId();
    remote.set_disconnect_with_reason_handler(base::BindOnce(
        &RemoteSetImpl::OnDisconnect, base::Unretained(this), id));
    auto result = storage_.emplace(id, std::move(remote));
    DCHECK(result.second);
    return id;
  }

  // Same as above but for the equivalent pending remote type. If |task_runner|
  // is null, the value of |base::SequencedTaskRunner::GetCurrentDefault()| at
  // the time of the |Add()| call will be used to run scheduled tasks for the
  // remote.
  RemoteSetElementId AddImpl(
      PendingRemoteType<Interface> remote,
      scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
    DCHECK(remote.is_valid());
    return AddImpl(
        RemoteType<Interface>(std::move(remote), std::move(task_runner)));
  }

  RemoteSetElementId GenerateNextElementId() {
    return remote_set_element_id_generator_.GenerateNextId();
  }

  void OnDisconnect(RemoteSetElementId id,
                    uint32_t custom_reason_code,
                    const std::string& description) {
    Remove(id);
    if (disconnect_handler_)
      disconnect_handler_.Run(id);
    else if (disconnect_with_reason_handler_) {
      disconnect_with_reason_handler_.Run(id, custom_reason_code, description);
    }
  }

  RemoteSetElementId::Generator remote_set_element_id_generator_;
  Storage storage_;
  DisconnectHandler disconnect_handler_;
  DisconnectWithReasonHandler disconnect_with_reason_handler_;
};

template <typename Interface>
using RemoteSet = RemoteSetImpl<Interface, Remote, PendingRemote>;

template <typename Interface>
using AssociatedRemoteSet =
    RemoteSetImpl<Interface, AssociatedRemote, PendingAssociatedRemote>;

}  // namespace mojo

#endif  // MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_