11#include "gtest/gtest.h"
25static_assert(std::is_same_v<HostMem::HostMemRegion, HostMemRegion>);
26static_assert(std::is_base_of_v<services::HostMemAllocator, HostMem>);
27static_assert(std::is_aggregate_v<Segment>);
31struct FakeRegion :
public HostMemRegion {
32 FakeRegion(uintptr_t host, uintptr_t dev, std::size_t size)
33 : host(host), dev(dev), size(size) {}
34 void *getPtr()
const override {
return reinterpret_cast<void *
>(host); }
35 void *getDevicePtr()
const override {
return reinterpret_cast<void *
>(dev); }
36 std::size_t getSize()
const override {
return size; }
42struct BufferRegion :
public HostMemRegion {
43 BufferRegion(std::size_t size, uintptr_t dev,
bool *destroyed =
nullptr)
44 : buf(size), dev(dev), destroyed(destroyed) {}
45 ~BufferRegion()
override {
49 void *getPtr()
const override {
return const_cast<uint8_t *
>(buf.data()); }
50 void *getDevicePtr()
const override {
return reinterpret_cast<void *
>(dev); }
51 std::size_t getSize()
const override {
return buf.size(); }
52 std::vector<uint8_t> buf;
57static const void *at(uintptr_t addr) {
58 return reinterpret_cast<const void *
>(
addr);
61TEST(HostMemRegionTest, GetDeviceAddress) {
62 FakeRegion r(0x1000, 0xA000'0000, 0x100);
63 EXPECT_EQ(r.getDeviceAddress(at(0x1000), 0x100), 0xA000'0000u);
64 EXPECT_EQ(r.getDeviceAddress(at(0x10FF), 1), 0xA000'00FFu);
66 EXPECT_FALSE(r.getDeviceAddress(at(0x1000), 0));
67 EXPECT_FALSE(r.getDeviceAddress(at(0x0FFF), 2));
68 EXPECT_FALSE(r.getDeviceAddress(at(0x10FF), 2));
69 EXPECT_FALSE(r.getDeviceAddress(at(0x1100), 1));
70 EXPECT_FALSE(r.getDeviceAddress(at(0x1080), SIZE_MAX));
72 constexpr uintptr_t maxPtr = std::numeric_limits<uintptr_t>::max();
74 FakeRegion top(maxPtr - 0xF, 0x2000, 0x10);
75 EXPECT_EQ(top.getDeviceAddress(at(maxPtr - 0x7), 8), 0x2008u);
76 EXPECT_FALSE(top.getDeviceAddress(at(maxPtr - 0x7), 9));
79 if constexpr (
sizeof(uintptr_t) ==
sizeof(uint64_t)) {
80 FakeRegion devTop(0x1000, maxPtr - 0x3, 0x10);
81 EXPECT_EQ(devTop.getDeviceAddress(at(0x1000), 4), maxPtr - 0x3);
82 EXPECT_FALSE(devTop.getDeviceAddress(at(0x1000), 5));
83 EXPECT_FALSE(devTop.getDeviceAddress(at(0x1004), 1));
87TEST(SegmentTest, DeviceAddress) {
88 uint8_t bytes[4] = {};
91 BufferRegion region(16, 0x8000'0000);
92 const uint8_t *
data = region.buf.data();
100 TestMessage(
bool *destroyed =
nullptr)
101 : header{0xA0, 0xA1},
102 region(std::make_unique<BufferRegion>(8, 0xD000'0000, destroyed)) {
103 base =
static_cast<const uint8_t *
>(region->getPtr());
106 size_t numSegments()
const override {
return 3; }
107 Segment segment(
size_t idx)
const override {
109 throw std::runtime_error(
"segment has been taken");
111 return {header.data(), header.size()};
112 return {base + (idx - 1) * 4, 4, region.get()};
114 std::unique_ptr<HostMemRegion> take(
size_t idx)
override {
118 throw std::runtime_error(
"segment has been taken");
120 return taken[1] && taken[2] ? std::move(region) : nullptr;
123 std::vector<uint8_t> header;
124 std::unique_ptr<HostMemRegion> region;
126 std::vector<bool> taken = {
false,
false,
false};
129TEST(SegmentTest, CursorRemainingSegmentKeepsRegion) {
132 EXPECT_EQ(cursor.remainingSegment().region,
nullptr);
136 Segment s = cursor.remainingSegment();
137 EXPECT_EQ(
s.data, msg.base + 1);
138 EXPECT_EQ(
s.size, 3u);
139 EXPECT_EQ(
s.region, msg.region.get());
140 EXPECT_EQ(
s.getDeviceAddress(), 0xD000'0001u);
141 EXPECT_EQ(cursor.remaining().data(),
s.data);
144 EXPECT_EQ(cursor.remainingSegment().getDeviceAddress(), 0xD000'0004u);
146 EXPECT_TRUE(cursor.done());
147 EXPECT_EQ(cursor.remainingSegment().data,
nullptr);
150TEST(SegmentTest, TakeHandsOverSharedRegionAfterLastUser) {
151 bool destroyed =
false;
152 auto msg = std::make_unique<TestMessage>(&destroyed);
153 HostMemRegion *shared = msg->region.get();
156 EXPECT_EQ(msg->take(0),
nullptr);
157 EXPECT_EQ(msg->segment(0).size, 2u);
160 EXPECT_EQ(msg->take(1),
nullptr);
161 EXPECT_THROW(msg->segment(1), std::runtime_error);
162 EXPECT_THROW(msg->take(1), std::runtime_error);
163 EXPECT_THROW(msg->toMessageData(), std::runtime_error);
164 EXPECT_EQ(msg->segment(2).region, shared);
168 EXPECT_THROW(cursor.advance(3), std::runtime_error);
171 std::unique_ptr<HostMemRegion> region = msg->take(2);
172 EXPECT_EQ(region.get(), shared);
174 EXPECT_FALSE(destroyed);
176 EXPECT_TRUE(destroyed);
180 { TestMessage untaken(&destroyed); }
181 EXPECT_TRUE(destroyed);
184TEST(SegmentTest, MessageDataHasNoRegions) {
186 EXPECT_EQ(md.segment(0).region,
nullptr);
187 EXPECT_EQ(md.take(0),
nullptr);
188 EXPECT_EQ(md.segment(0).data, md.getBytes());
A concrete flat message backed by a single vector of bytes.
Cursor for incremental consumption of a SegmentedMessageData.
Abstract multi-segment message.
A contiguous, non-owning view of bytes within a SegmentedMessageData.
std::optional< uint64_t > getDeviceAddress() const
Device address of this segment's bytes, if region is set and contains them; std::nullopt otherwise.
RAII memory region for host memory in accelerator-accessible address space.