Automatically generated with:
cp dom/.clang-format xpcom/ && mach format xpcom/**.{cpp,h}
Manual changes:
* nsTDependentSubstring.cpp was relying on include order, prevent that
from being changed by clang-format.
* Missing nsAtom.h include from nsGkAtoms.h
* nsWindowsHelpers.h needed to keep the include order
* MemoryPressureLevelMac.h needed mozilla/Attributes.h
Differential Revision: https://phabricator.services.mozilla.com/D311687
358 lines
11 KiB
C++
358 lines
11 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "Helpers.h"
|
|
#include "base/eintr_wrapper.h"
|
|
#include "gtest/gtest.h"
|
|
#include "mozilla/AsyncPlatformPipes.h"
|
|
#include "mozilla/RandomNum.h"
|
|
#include "mozilla/SpinEventLoopUntil.h"
|
|
#include "mozilla/gtest/MozAssertions.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsStreamUtils.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
#ifdef XP_WIN
|
|
# include <windows.h>
|
|
#else
|
|
# include <fcntl.h>
|
|
# include <unistd.h>
|
|
#endif
|
|
|
|
using mozilla::PlatformPipeReader;
|
|
using mozilla::SpinEventLoopUntil;
|
|
using mozilla::UniqueFileHandle;
|
|
|
|
namespace {
|
|
|
|
struct PipePair {
|
|
RefPtr<PlatformPipeReader> mReader;
|
|
UniqueFileHandle mWriteHandle;
|
|
};
|
|
|
|
PipePair CreatePipePair(uint32_t aCapacity) {
|
|
#ifdef XP_WIN
|
|
wchar_t name[128];
|
|
swprintf(name, std::size(name),
|
|
L"\\\\.\\pipe\\gecko-test-platform-pipe-%lu-%I64u",
|
|
::GetCurrentProcessId(), mozilla::RandomUint64OrDie());
|
|
|
|
UniqueFileHandle readHandle(
|
|
CreateNamedPipeW(name,
|
|
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED |
|
|
FILE_FLAG_FIRST_PIPE_INSTANCE,
|
|
PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, nullptr));
|
|
MOZ_RELEASE_ASSERT(readHandle);
|
|
|
|
UniqueFileHandle writeHandle(
|
|
CreateFileW(name, GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
|
|
SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS, nullptr));
|
|
MOZ_RELEASE_ASSERT(writeHandle);
|
|
|
|
MOZ_RELEASE_ASSERT(!ConnectNamedPipe(readHandle.get(), nullptr) &&
|
|
GetLastError() == ERROR_PIPE_CONNECTED);
|
|
|
|
return {new PlatformPipeReader(std::move(readHandle), aCapacity),
|
|
std::move(writeHandle)};
|
|
#else
|
|
int fds[2];
|
|
MOZ_RELEASE_ASSERT(pipe(fds) == 0);
|
|
MOZ_RELEASE_ASSERT(
|
|
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK) == 0);
|
|
return {new PlatformPipeReader(UniqueFileHandle(fds[0]), aCapacity),
|
|
UniqueFileHandle(fds[1])};
|
|
#endif
|
|
}
|
|
|
|
void WriteAllRaw(UniqueFileHandle& aHandle, const char* aData,
|
|
uint32_t aCount) {
|
|
#ifdef XP_WIN
|
|
DWORD written = 0;
|
|
BOOL ok = WriteFile(aHandle.get(), aData, aCount, &written, nullptr);
|
|
MOZ_RELEASE_ASSERT(ok);
|
|
MOZ_RELEASE_ASSERT(written == aCount);
|
|
#else
|
|
uint32_t total = 0;
|
|
while (total < aCount) {
|
|
ssize_t rv =
|
|
HANDLE_EINTR(write(aHandle.get(), aData + total, aCount - total));
|
|
MOZ_RELEASE_ASSERT(rv > 0);
|
|
total += static_cast<uint32_t>(rv);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void ReadExactly(nsIInputStream* aStream, uint32_t aCount,
|
|
nsACString& aResult) {
|
|
aResult.SetLength(aCount);
|
|
|
|
uint32_t read = 0;
|
|
ASSERT_NS_SUCCEEDED(aStream->Read(aResult.BeginWriting(), aCount, &read));
|
|
ASSERT_EQ(read, aCount);
|
|
}
|
|
|
|
struct PartialConsumeClosure {
|
|
uint32_t mLimit;
|
|
char* mBuffer;
|
|
};
|
|
|
|
nsresult ConsumeUpToLimit(nsIInputStream*, void* aClosure,
|
|
const char* aFromSegment, uint32_t aOffset,
|
|
uint32_t aCount, uint32_t* aWriteCount) {
|
|
auto* state = static_cast<PartialConsumeClosure*>(aClosure);
|
|
uint32_t toRead = std::min(state->mLimit - aOffset, aCount);
|
|
memcpy(state->mBuffer + aOffset, aFromSegment, toRead);
|
|
*aWriteCount = toRead;
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(AsyncPlatformPipes, ReadAsyncWait)
|
|
{
|
|
auto pipe = CreatePipePair(64);
|
|
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
RefPtr callback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(callback, 0, 0, thread));
|
|
|
|
nsCString payload("hello async pipe"_ns);
|
|
WriteAllRaw(pipe.mWriteHandle, payload.get(), payload.Length());
|
|
|
|
MOZ_ALWAYS_TRUE(
|
|
SpinEventLoopUntil("xpcom:TEST(AsyncPlatformPipes, ReadAsyncWait)"_ns,
|
|
[&] { return callback->Called(); }));
|
|
|
|
nsCString output;
|
|
ReadExactly(pipe.mReader, payload.Length(), output);
|
|
ASSERT_TRUE(payload.Equals(output));
|
|
pipe.mWriteHandle.reset();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, ReadAsyncWaitClosureOnly)
|
|
{
|
|
auto pipe = CreatePipePair(64);
|
|
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
RefPtr callback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(
|
|
callback, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, thread));
|
|
|
|
pipe.mWriteHandle.reset();
|
|
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, ReadAsyncWaitClosureOnly)"_ns,
|
|
[&] { return callback->Called(); }));
|
|
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, InputStreamIsBufferedDoesNotStartRead)
|
|
{
|
|
auto pipe = CreatePipePair(8);
|
|
|
|
nsCString payload("12345678"_ns);
|
|
WriteAllRaw(pipe.mWriteHandle, payload.get(), payload.Length());
|
|
|
|
uint64_t available = 1;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Available(&available));
|
|
ASSERT_EQ(available, 0u);
|
|
|
|
ASSERT_TRUE(NS_InputStreamIsBuffered(pipe.mReader));
|
|
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Available(&available));
|
|
ASSERT_EQ(available, 0u);
|
|
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
RefPtr readCallback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(readCallback, 0, 0, thread));
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, InputStreamIsBufferedDoesNotStartRead, Read)"_ns,
|
|
[&] { return readCallback->Called(); }));
|
|
|
|
nsCString output;
|
|
ReadExactly(pipe.mReader, payload.Length(), output);
|
|
ASSERT_TRUE(payload.Equals(output));
|
|
pipe.mWriteHandle.reset();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, LargeDataMultipleCycles)
|
|
{
|
|
// Use a small reader buffer (8 bytes) to force multiple read cycles for a
|
|
// larger payload (1024 bytes).
|
|
auto pipe = CreatePipePair(8);
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
|
|
const uint32_t kTotalBytes = 1024;
|
|
nsCString payload;
|
|
payload.SetLength(kTotalBytes);
|
|
for (uint32_t i = 0; i < kTotalBytes; ++i) {
|
|
payload.BeginWriting()[i] = static_cast<char>(i & 0xff);
|
|
}
|
|
|
|
WriteAllRaw(pipe.mWriteHandle, payload.get(), payload.Length());
|
|
pipe.mWriteHandle.reset();
|
|
|
|
nsCString result;
|
|
uint32_t totalRead = 0;
|
|
while (totalRead < kTotalBytes) {
|
|
RefPtr readCallback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(readCallback, 0, 0, thread));
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, LargeDataMultipleCycles)"_ns,
|
|
[&] { return readCallback->Called(); }));
|
|
|
|
uint64_t avail = 0;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Available(&avail));
|
|
if (avail == 0) {
|
|
break;
|
|
}
|
|
|
|
uint32_t prevLen = result.Length();
|
|
result.SetLength(prevLen + static_cast<uint32_t>(avail));
|
|
uint32_t read = 0;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Read(
|
|
result.BeginWriting() + prevLen, static_cast<uint32_t>(avail), &read));
|
|
ASSERT_EQ(read, static_cast<uint32_t>(avail));
|
|
totalRead += read;
|
|
}
|
|
|
|
ASSERT_EQ(totalRead, kTotalBytes);
|
|
ASSERT_TRUE(payload.Equals(result));
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, ClosureOnlyCallbackIgnoresData)
|
|
{
|
|
auto pipe = CreatePipePair(64);
|
|
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
RefPtr callback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(
|
|
callback, nsIAsyncInputStream::WAIT_CLOSURE_ONLY, 0, thread));
|
|
|
|
nsCString payload("closure ignores this"_ns);
|
|
WriteAllRaw(pipe.mWriteHandle, payload.get(), payload.Length());
|
|
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, ClosureOnlyCallbackIgnoresData) buffered"_ns,
|
|
[&] {
|
|
uint64_t avail = 0;
|
|
return NS_SUCCEEDED(pipe.mReader->Available(&avail)) && avail > 0;
|
|
}));
|
|
|
|
ASSERT_FALSE(callback->Called());
|
|
|
|
nsCString output;
|
|
ReadExactly(pipe.mReader, payload.Length(), output);
|
|
ASSERT_TRUE(payload.Equals(output));
|
|
ASSERT_FALSE(callback->Called());
|
|
|
|
pipe.mWriteHandle.reset();
|
|
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, ClosureOnlyCallbackIgnoresData) closed"_ns,
|
|
[&] { return callback->Called(); }));
|
|
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, ReadSegmentsPartialConsume)
|
|
{
|
|
auto pipe = CreatePipePair(64);
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
|
|
nsCString payload("abcdefghij"_ns);
|
|
WriteAllRaw(pipe.mWriteHandle, payload.get(), payload.Length());
|
|
|
|
RefPtr callback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(callback, 0, 0, thread));
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, ReadSegmentsPartialConsume) wait"_ns,
|
|
[&] { return callback->Called(); }));
|
|
|
|
uint64_t avail = 0;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Available(&avail));
|
|
ASSERT_EQ(avail, payload.Length());
|
|
|
|
char firstHalf[5] = {0};
|
|
PartialConsumeClosure closure{5, firstHalf};
|
|
uint32_t read = 0;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->ReadSegments(ConsumeUpToLimit, &closure,
|
|
payload.Length(), &read));
|
|
ASSERT_EQ(read, 5u);
|
|
ASSERT_EQ(memcmp(firstHalf, "abcde", 5), 0);
|
|
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Available(&avail));
|
|
ASSERT_EQ(avail, 5u);
|
|
|
|
nsCString rest;
|
|
ReadExactly(pipe.mReader, 5, rest);
|
|
ASSERT_TRUE(rest.Equals("fghij"_ns));
|
|
|
|
pipe.mWriteHandle.reset();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, ReadAfterWriteSideClosed)
|
|
{
|
|
auto pipe = CreatePipePair(64);
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
|
|
nsCString payload("farewell"_ns);
|
|
WriteAllRaw(pipe.mWriteHandle, payload.get(), payload.Length());
|
|
pipe.mWriteHandle.reset();
|
|
|
|
RefPtr readCallback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(readCallback, 0, 0, thread));
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, ReadAfterWriteSideClosed) data"_ns,
|
|
[&] { return readCallback->Called(); }));
|
|
|
|
nsCString output;
|
|
ReadExactly(pipe.mReader, payload.Length(), output);
|
|
ASSERT_TRUE(payload.Equals(output));
|
|
|
|
RefPtr eofCallback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(eofCallback, 0, 0, thread));
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, ReadAfterWriteSideClosed) eof"_ns,
|
|
[&] { return eofCallback->Called(); }));
|
|
|
|
ASSERT_EQ(pipe.mReader->StreamStatus(), NS_BASE_STREAM_CLOSED);
|
|
|
|
char extra[8] = {0};
|
|
uint32_t read = 42;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Read(extra, sizeof(extra), &read));
|
|
ASSERT_EQ(read, 0u);
|
|
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
}
|
|
|
|
TEST(AsyncPlatformPipes, CloseWithOutstandingIO)
|
|
{
|
|
auto pipe = CreatePipePair(64);
|
|
nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
|
|
|
|
RefPtr callback = mozilla::MakeRefPtr<testing::InputStreamCallback>();
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->AsyncWait(callback, 0, 0, thread));
|
|
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Close());
|
|
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
|
|
"xpcom:TEST(AsyncPlatformPipes, CloseWithOutstandingIO)"_ns,
|
|
[&] { return callback->Called(); }));
|
|
|
|
ASSERT_EQ(pipe.mReader->StreamStatus(), NS_BASE_STREAM_CLOSED);
|
|
|
|
char buf[8] = {0};
|
|
uint32_t read = 42;
|
|
ASSERT_NS_SUCCEEDED(pipe.mReader->Read(buf, sizeof(buf), &read));
|
|
ASSERT_EQ(read, 0u);
|
|
|
|
pipe.mWriteHandle.reset();
|
|
}
|