blob: 9b6b1e5ec1b1732a1b57f3f68fc3c5d521e65e6d [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/zx/vmo.h>
#include "vmo_store.h"
namespace vmo_store {
// A `VmoStore` that may only be accessed through a `RegistrationAgent`.
// `OwnedVmoStore` composes with `VmoStore` to provide a wrapper that only allows access to the
// registered VMOs through the creation of `RegistrationAgent`s.
// `Backing` is the data structure used to store the registered VMOs. It must implement
// `AbstractStorage`.
// Example usage:
// ```
// using namespace vmo_store;
// // Declaring our types first.
// // `MyKey` is the key type that is used to register and retrieve VMOs from a VmoStore.
// using MyKey = size_t;
// // `MyMeta` is extra user metadata associated with every stored VMO (can be `void`).
// using MyMeta = std::string;
// // Declare our store alias, we're using `HashTableStorage` in this example.
// // See `storage_types.h` for other Backing storage types.
// using MyOwnedVmoStore = OwnedVmoStore<HashTableStorage<MyKey, MyMeta>>;
// MyOwnedVmoStore store(Options{...});
// // Declare a registration agent for it. The agent provides a view into the store.
// MyOwnedVmoStore::RegistrationAgent agent = store.CreateRegistrationAgent();
// // Now let's register, retrieve, and unregister a `zx::vmo` obtained through `GetVmo()`.
// // The second argument to `Register` is our user metadata.
// fit::result<size_t, zx_status_t> result = agent.Register(GetVmo(), "my first VMO");
// size_t key = result.take_value();
// auto * my_registered_vmo = agent.GetVmo(key);
// // Print metadata associated with VMO.
// std::cout << "Got Vmo called " << my_registered_vmo->meta() << std::endl;
// // see `stored_vmo.h` for other `StoredVmo` methods, like retrieving mapped or pinned memory.
// // A different agent will not have access to the same VMO using the key.
// MyVmoStore::RegistrationAgent other_agent = store.CreateRegistrationAgent();
// ZX_ASSERT(other_agent.GetVmo(key) == nullptr, "no soup for other_agent");
// // Finally, unregister the VMO, which will discard the VMO handle along with any mapping or
// // pinning. Destroying the agent without unregistering all its VMOs will cause a crash.
// agent.Unregister(key);
// ```
template <typename Backing>
class OwnedVmoStore : public VmoStoreBase<VmoStore<Backing>> {
template <typename... StoreArgs>
explicit OwnedVmoStore(StoreArgs... store_args)
: VmoStoreBase<VmoStore<Backing>>(std::forward<StoreArgs>(store_args)...) {}
class RegistrationAgent;
// Creates a `RegistrationAgent` attached to this `OwnedVmoStore`.
// The returned agent may not outlive this `OwnedVmoStore`.
RegistrationAgent CreateRegistrationAgent() { return RegistrationAgent(this); }
// An agent which owns VMOs in an `OwnedVmoStore`.
// `RegistrationAgent` serves as the registration point for VMOs stored in an `OwnedVmoStore`.
// A `RegistrationAgent` provides runtime guardrails so that multiple agents can use the same
// store, but they can't access each other's VMOs.
// Destroying a `RegistrationAgent` without first unregistering all the VMOs that it registered is
// invalid, and causes the program to crash.
// Note that `RegistrationAgent` does not provide any thread-safety guarantees, users must provide
// their own locking mechanisms to ensure that different `RegistrationAgent`s can't compete across
// threads, taking into account the chosen `Backing` method for the `OwnedVmoStore`.
class RegistrationAgent : internal::VmoOwner {
using VmoStore = ::vmo_store::VmoStore<Backing>;
using StoredVmo = typename VmoStore::StoredVmo;
// Creates a `RegistrationAgent` attached to `store`.
// `RegistrationAgent` may not outlive `store`.
explicit RegistrationAgent(OwnedVmoStore<Backing>* store)
: store_(&store->impl_), registration_count_(0) {}
~RegistrationAgent() {
size_t registered = registration_count_;
ZX_ASSERT_MSG(registered == 0,
"Attempted to destroy a RegistrationAgent with %ld registered VMOs",
// Same as `VmoStore::Register`, but the registered VMO is only accessible through this
// `RegistrationAgent`.
template <typename... MetaArgs>
fit::result<typename VmoStore::Key, zx_status_t> Register(zx::vmo vmo, MetaArgs... vmo_args) {
auto result =
store_->Register(CreateStore(std::move(vmo), std::forward<MetaArgs>(vmo_args)...));
if (result.is_ok()) {
return result;
// Same as `VmoStore::RegisterWithKey`, but the registered VMO is only accessible through this
// `RegistrationAgent`.
template <typename... MetaArgs>
zx_status_t RegisterWithKey(typename VmoStore::Key key, zx::vmo vmo, MetaArgs... vmo_args) {
zx_status_t result = store_->RegisterWithKey(
std::move(key), CreateStore(std::move(vmo), std::forward<MetaArgs>(vmo_args)...));
if (result == ZX_OK) {
return result;
// Same as `VmoStore::Unregister`, but unregistration fails with `ZX_ERR_ACCESS_DENIED` if the
// VMO was not initially registered by this `RegistrationAgent`.
zx_status_t Unregister(typename VmoStore::Key key) {
auto* vmo = store_->GetVmo(key);
if (vmo && GetOwner(*vmo) != this) {
zx_status_t status = store_->Unregister(std::move(key));
if (status == ZX_OK) {
return status;
// Same as `VmoStore::GetVmo`, but only returns non-null if the VMO referenced by `key` was
// originally registered by this `RegistrationAgent`.
StoredVmo* GetVmo(const typename VmoStore::Key& key) {
auto* vmo = store_->GetVmo(key);
if (vmo && GetOwner(*vmo) == this) {
return vmo;
return nullptr;
template <typename... MetaArgs>
StoredVmo CreateStore(zx::vmo vmo, MetaArgs... vmo_args) {
StoredVmo stored_vmo(std::move(vmo), std::forward<MetaArgs>(vmo_args)...);
SetOwner(&stored_vmo, this);
return stored_vmo;
// Pointer to parent store, not owned.
VmoStore* store_;
std::atomic_size_t registration_count_;
} // namespace vmo_store