blob: 5a8aba0822c46fcdd14ea520c3d62b1840e74df8 [file] [log] [blame]
// Copyright 2022 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.
#include "ipcz/node_link_memory.h"
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <utility>
#include "ipcz/buffer_id.h"
#include "ipcz/driver_memory.h"
#include "ipcz/ipcz.h"
#include "ipcz/node.h"
#include "ipcz/node_link.h"
#include "util/ref_counted.h"
namespace ipcz {
namespace {
constexpr BufferId kPrimaryBufferId{0};
// Fixed allocation size for each NodeLink's primary shared buffer.
constexpr size_t kPrimaryBufferSize = 65536;
} // namespace
// This structure always sits at offset 0 in the primary buffer and has a fixed
// layout according to the NodeLink's agreed upon protocol version. This is the
// layout for version 0 (currently the only version.)
struct IPCZ_ALIGN(8) NodeLinkMemory::PrimaryBuffer {
// Atomic generator for new unique BufferIds to use across the associated
// NodeLink. This allows each side of a NodeLink to generate new BufferIds
// spontaneously without synchronization or risk of collisions.
std::atomic<uint64_t> next_buffer_id;
// Atomic generator for new unique SublinkIds to use across the associated
// NodeLink. This allows each side of a NodeLink to generate new SublinkIds
// spontaneously without synchronization or risk of collisions.
std::atomic<uint64_t> next_sublink_id;
};
NodeLinkMemory::NodeLinkMemory(Ref<Node> node,
DriverMemoryMapping primary_buffer_memory)
: node_(std::move(node)),
primary_buffer_memory_(primary_buffer_memory.bytes()),
primary_buffer_(
*reinterpret_cast<PrimaryBuffer*>(primary_buffer_memory_.data())) {
// Consistency check here, because PrimaryBuffer is private to NodeLinkMemory.
static_assert(sizeof(PrimaryBuffer) <= kPrimaryBufferSize,
"PrimaryBuffer structure is too large.");
buffer_pool_.AddBuffer(BufferId{kPrimaryBufferId},
std::move(primary_buffer_memory));
}
NodeLinkMemory::~NodeLinkMemory() = default;
// static
NodeLinkMemory::Allocation NodeLinkMemory::Allocate(Ref<Node> node) {
DriverMemory primary_buffer_memory(node->driver(), sizeof(PrimaryBuffer));
if (!primary_buffer_memory.is_valid()) {
return {.node_link_memory = nullptr, .primary_buffer_memory = {}};
}
auto memory = AdoptRef(
new NodeLinkMemory(std::move(node), primary_buffer_memory.Map()));
PrimaryBuffer& primary_buffer = memory->primary_buffer_;
// The first allocable BufferId is 1, because the primary buffer uses 0.
primary_buffer.next_buffer_id.store(1, std::memory_order_relaxed);
// The first allocable SublinkId is kMaxInitialPortals. This way it doesn't
// matter whether the two ends of a NodeLink initiate their connection with a
// different initial portal count: neither can request more than
// kMaxInitialPortals, so neither will be assuming initial ownership of any
// SublinkIds at or above this value.
primary_buffer.next_sublink_id.store(kMaxInitialPortals,
std::memory_order_release);
return {
.node_link_memory = std::move(memory),
.primary_buffer_memory = std::move(primary_buffer_memory),
};
}
// static
Ref<NodeLinkMemory> NodeLinkMemory::Adopt(Ref<Node> node,
DriverMemory primary_buffer_memory) {
return AdoptRef(
new NodeLinkMemory(std::move(node), primary_buffer_memory.Map()));
}
BufferId NodeLinkMemory::AllocateNewBufferId() {
return BufferId{
primary_buffer_.next_buffer_id.fetch_add(1, std::memory_order_relaxed)};
}
SublinkId NodeLinkMemory::AllocateSublinkIds(size_t count) {
return SublinkId{primary_buffer_.next_sublink_id.fetch_add(
count, std::memory_order_relaxed)};
}
} // namespace ipcz