Skip to content

Commit

Permalink
ipcz: Implement two-phase get/put operations
Browse files Browse the repository at this point in the history
This implements two-phase get and put operations via the
{Begin, End} x {Put, Get} APIs. In most cases these APIs effectively
allow applications to read and write directly from/to shared memory
which is already mapped by the source and destination processes.

Bug: 1299283
Change-Id: I62c4dff91737f4e6ee7824a154cf60eaed69b7ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3803589
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1034782}
  • Loading branch information
krockot authored and Chromium LUCI CQ committed Aug 13, 2022
1 parent 9ba2df5 commit bf7b3a3
Show file tree
Hide file tree
Showing 7 changed files with 535 additions and 19 deletions.
59 changes: 55 additions & 4 deletions third_party/ipcz/src/api.cc
Expand Up @@ -164,7 +164,27 @@ IpczResult BeginPut(IpczHandle portal_handle,
const IpczBeginPutOptions* options,
size_t* num_bytes,
void** data) {
return IPCZ_RESULT_UNIMPLEMENTED;
ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle);
if (!portal) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}
if (num_bytes && *num_bytes > 0 && !data) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}
if (options && options->size < sizeof(IpczBeginPutOptions)) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}

const IpczPutLimits* limits = options ? options->limits : nullptr;
if (limits && limits->size < sizeof(IpczPutLimits)) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}

size_t dummy_num_bytes = 0;
if (!num_bytes) {
num_bytes = &dummy_num_bytes;
}
return portal->BeginPut(flags, limits, *num_bytes, data);
}

IpczResult EndPut(IpczHandle portal_handle,
Expand All @@ -173,7 +193,20 @@ IpczResult EndPut(IpczHandle portal_handle,
size_t num_handles,
IpczEndPutFlags flags,
const void* options) {
return IPCZ_RESULT_UNIMPLEMENTED;
ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle);
if (!portal) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}
if (num_handles > 0 && !handles) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}

if (flags & IPCZ_END_PUT_ABORT) {
return portal->AbortPut();
}

return portal->CommitPut(num_bytes_produced,
absl::MakeSpan(handles, num_handles));
}

IpczResult Get(IpczHandle portal_handle,
Expand All @@ -196,7 +229,12 @@ IpczResult BeginGet(IpczHandle portal_handle,
const void** data,
size_t* num_bytes,
size_t* num_handles) {
return IPCZ_RESULT_UNIMPLEMENTED;
ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle);
if (!portal) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}

return portal->BeginGet(data, num_bytes, num_handles);
}

IpczResult EndGet(IpczHandle portal_handle,
Expand All @@ -205,7 +243,20 @@ IpczResult EndGet(IpczHandle portal_handle,
IpczEndGetFlags flags,
const void* options,
IpczHandle* handles) {
return IPCZ_RESULT_UNIMPLEMENTED;
ipcz::Portal* portal = ipcz::Portal::FromHandle(portal_handle);
if (!portal) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}
if (num_handles > 0 && !handles) {
return IPCZ_RESULT_INVALID_ARGUMENT;
}

if (flags & IPCZ_END_GET_ABORT) {
return portal->AbortGet();
}

return portal->CommitGet(num_bytes_consumed,
absl::MakeSpan(handles, num_handles));
}

IpczResult Trap(IpczHandle portal_handle,
Expand Down
164 changes: 149 additions & 15 deletions third_party/ipcz/src/api_test.cc
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <cstring>
#include <string>

#include "ipcz/ipcz.h"
Expand All @@ -16,21 +17,6 @@ const IpczDriver& kDefaultDriver = reference_drivers::kSyncReferenceDriver;

using APITest = test::Test;

TEST_F(APITest, Unimplemented) {
EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
ipcz().BeginPut(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr,
nullptr, nullptr));
EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
ipcz().EndPut(IPCZ_INVALID_HANDLE, 0, nullptr, 0, IPCZ_NO_FLAGS,
nullptr));
EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
ipcz().BeginGet(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr,
nullptr, nullptr, nullptr));
EXPECT_EQ(IPCZ_RESULT_UNIMPLEMENTED,
ipcz().EndGet(IPCZ_INVALID_HANDLE, 0, 0, IPCZ_NO_FLAGS, nullptr,
nullptr));
}

TEST_F(APITest, CloseInvalid) {
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().Close(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr));
Expand Down Expand Up @@ -326,6 +312,154 @@ TEST_F(APITest, PutGet) {
CloseAll({a, b, c, node});
}

TEST_F(APITest, BeginEndPutFailure) {
const IpczHandle node = CreateNode(kDefaultDriver);
auto [a, b] = OpenPortals(node);

// Invalid portal.
constexpr size_t kPutSize = 64;
size_t num_bytes = kPutSize;
void* data;
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().BeginPut(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr,
&num_bytes, &data));

// Non-zero size but null data.
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().BeginPut(a, IPCZ_NO_FLAGS, nullptr, &num_bytes, nullptr));

// Invalid options.
IpczBeginPutOptions options = {.size = 0};
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().BeginPut(a, IPCZ_NO_FLAGS, &options, &num_bytes, &data));

// Duplicate two-phase Put.
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().BeginPut(a, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr));
EXPECT_EQ(IPCZ_RESULT_ALREADY_EXISTS,
ipcz().BeginPut(a, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr));

// Invalid portal.
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().EndPut(IPCZ_INVALID_HANDLE, 0, nullptr, 0, IPCZ_NO_FLAGS,
nullptr));

// Non-zero number of handles, but null handle buffer.
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().EndPut(a, 0, nullptr, 1, IPCZ_NO_FLAGS, nullptr));

// Oversized data.
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().EndPut(a, kPutSize * 2, nullptr, 0, IPCZ_NO_FLAGS, nullptr));

// Invalid handle attachment.
IpczHandle invalid_handle = IPCZ_INVALID_HANDLE;
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().EndPut(a, 0, &invalid_handle, 1, IPCZ_NO_FLAGS, nullptr));

// Two-phase Put not in progress.
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().EndPut(a, 0, nullptr, 0, IPCZ_NO_FLAGS, nullptr));
EXPECT_EQ(IPCZ_RESULT_FAILED_PRECONDITION,
ipcz().EndPut(a, 0, nullptr, 0, IPCZ_NO_FLAGS, nullptr));

CloseAll({a, b, node});
}

TEST_F(APITest, BeginEndGetFailure) {
const IpczHandle node = CreateNode(kDefaultDriver);
auto [a, b] = OpenPortals(node);

// No parcel yet.
EXPECT_EQ(
IPCZ_RESULT_UNAVAILABLE,
ipcz().BeginGet(a, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr, nullptr));

constexpr std::string_view kMessage = "ipcz";
EXPECT_EQ(IPCZ_RESULT_OK, Put(b, kMessage));

// Invalid portal.
const void* data;
size_t num_bytes;
size_t num_handles;
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().BeginGet(IPCZ_INVALID_HANDLE, IPCZ_NO_FLAGS, nullptr, &data,
&num_bytes, &num_handles));

// No storage for data.
EXPECT_EQ(
IPCZ_RESULT_RESOURCE_EXHAUSTED,
ipcz().BeginGet(a, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr, nullptr));
EXPECT_EQ(
IPCZ_RESULT_RESOURCE_EXHAUSTED,
ipcz().BeginGet(a, IPCZ_NO_FLAGS, nullptr, &data, nullptr, nullptr));
EXPECT_EQ(
IPCZ_RESULT_RESOURCE_EXHAUSTED,
ipcz().BeginGet(a, IPCZ_NO_FLAGS, nullptr, nullptr, &num_bytes, nullptr));

EXPECT_EQ(IPCZ_RESULT_OK, ipcz().BeginGet(a, IPCZ_NO_FLAGS, nullptr, &data,
&num_bytes, nullptr));

// Invalid handle.
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().EndGet(IPCZ_INVALID_HANDLE, 0, 0, IPCZ_NO_FLAGS, nullptr,
nullptr));

// Non-zero handle count with null handle buffer.
EXPECT_EQ(IPCZ_RESULT_INVALID_ARGUMENT,
ipcz().EndGet(a, 0, 1, IPCZ_NO_FLAGS, nullptr, nullptr));

// Data size out of range.
EXPECT_EQ(
IPCZ_RESULT_OUT_OF_RANGE,
ipcz().EndGet(a, num_bytes + 1, 0, IPCZ_NO_FLAGS, nullptr, nullptr));

// Two-phase Get not in progress.
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().EndGet(a, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr));
EXPECT_EQ(IPCZ_RESULT_FAILED_PRECONDITION,
ipcz().EndGet(a, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr));

CloseAll({a, b, node});
}

TEST_F(APITest, TwoPhasePutGet) {
const IpczHandle node = CreateNode(kDefaultDriver);
auto [a, b] = OpenPortals(node);

constexpr std::string_view kMessage = "ipcz!";
size_t num_bytes = kMessage.size();
void* out_data;
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().BeginPut(a, IPCZ_NO_FLAGS, nullptr, &num_bytes, &out_data));
EXPECT_EQ(kMessage.size(), num_bytes);
memcpy(out_data, kMessage.data(), kMessage.size());
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().EndPut(a, num_bytes, nullptr, 0, IPCZ_NO_FLAGS, nullptr));

const void* in_data;
EXPECT_EQ(IPCZ_RESULT_OK, ipcz().BeginGet(b, IPCZ_NO_FLAGS, nullptr, &in_data,
&num_bytes, nullptr));
EXPECT_EQ(kMessage[0], *reinterpret_cast<const char*>(in_data));

EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().EndGet(b, 1, 0, IPCZ_NO_FLAGS, nullptr, nullptr));

EXPECT_EQ(IPCZ_RESULT_OK, ipcz().BeginGet(b, IPCZ_NO_FLAGS, nullptr, &in_data,
&num_bytes, nullptr));
EXPECT_EQ(
kMessage.substr(1),
std::string_view(reinterpret_cast<const char*>(in_data), num_bytes));
EXPECT_EQ(IPCZ_RESULT_OK,
ipcz().EndGet(b, num_bytes, 0, IPCZ_NO_FLAGS, nullptr, nullptr));

EXPECT_EQ(
IPCZ_RESULT_UNAVAILABLE,
ipcz().BeginGet(b, IPCZ_NO_FLAGS, nullptr, nullptr, nullptr, nullptr));

CloseAll({a, b, node});
}

TEST_F(APITest, TrapInvalid) {
const IpczHandle node = CreateNode(kDefaultDriver);
auto [a, b] = OpenPortals(node);
Expand Down

0 comments on commit bf7b3a3

Please sign in to comment.