blob: 1453d4acd7ec5b10263d6b3fbd8ca84044aa437c [file] [log] [blame]
// Copyright 2019 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.
#ifndef MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_
#define MOJO_PUBLIC_CPP_BINDINGS_REMOTE_SET_H_
#include <iterator>
#include <set>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/containers/contains.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequenced_task_runner.h"
#include "base/util/type_safety/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"
namespace mojo {
namespace internal {
struct RemoteSetElementIdTypeTag {};
} // namespace internal
using RemoteSetElementId = util::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) { return it_ == rhs.it_; }
bool operator!=(const self_type& rhs) { return it_ != rhs.it_; }
private:
typename Storage::const_iterator it_;
};
RemoteSetImpl() = default;
~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) {
DCHECK(remote.is_bound());
auto id = GenerateNextElementId();
remote.set_disconnect_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::SequencedTaskRunnerHandle::Get()| at the time
// of the |Add()| call will be used to run scheduled tasks for the remote.
RemoteSetElementId Add(
PendingRemoteType<Interface> remote,
scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) {
DCHECK(remote.is_valid());
return Add(
RemoteType<Interface>(std::move(remote), std::move(task_runner)));
}
// Removes a remote from the set given |id|, if present.
void Remove(RemoteSetElementId id) { storage_.erase(id); }
// 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)>;
void set_disconnect_handler(DisconnectHandler handler) {
disconnect_handler_ = std::move(handler);
}
void Clear() { storage_.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() {
for (auto& it : storage_) {
it.second.FlushForTesting();
}
}
private:
RemoteSetElementId GenerateNextElementId() {
return remote_set_element_id_generator_.GenerateNextId();
}
void OnDisconnect(RemoteSetElementId id) {
Remove(id);
if (disconnect_handler_)
disconnect_handler_.Run(id);
}
RemoteSetElementId::Generator remote_set_element_id_generator_;
Storage storage_;
DisconnectHandler disconnect_handler_;
DISALLOW_COPY_AND_ASSIGN(RemoteSetImpl);
};
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_