blob: 2d051f36522d50789acd8c84430b8079256b619d [file] [log] [blame]
// 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_