fune/ipc/ipdl/test/cxx/TestOffMainThreadPainting.cpp
David Anderson aa1457713c Allow IPDL message sends to be deferred and re-sent as needed. (bug 1369529 part 2, r=billm)
--HG--
extra : rebase_source : 46d2af94da6b38e8c2fe70fd4566650d8cec8fe4
2017-06-21 13:40:18 -07:00

290 lines
6.3 KiB
C++

#include "TestOffMainThreadPainting.h"
#include "IPDLUnitTests.h" // fail etc.
#include "mozilla/Unused.h"
#include <prinrval.h>
#include <prthread.h>
namespace mozilla {
namespace _ipdltest {
TestOffMainThreadPaintingParent::TestOffMainThreadPaintingParent()
: mAsyncMessages(0),
mSyncMessages(0)
{
}
TestOffMainThreadPaintingParent::~TestOffMainThreadPaintingParent()
{
}
void
TestOffMainThreadPaintingParent::Main()
{
ipc::Endpoint<PTestPaintThreadParent> parentPipe;
ipc::Endpoint<PTestPaintThreadChild> childPipe;
nsresult rv = PTestPaintThread::CreateEndpoints(
base::GetCurrentProcId(),
OtherPid(),
&parentPipe,
&childPipe);
if (NS_FAILED(rv)) {
fail("create pipes");
}
mPaintActor = new TestPaintThreadParent(this);
if (!mPaintActor->Bind(Move(parentPipe))) {
fail("bind parent pipe");
}
if (!SendStartTest(Move(childPipe))) {
fail("sending Start");
}
}
ipc::IPCResult
TestOffMainThreadPaintingParent::RecvFinishedLayout(const uint64_t& aTxnId)
{
if (!mPaintedTxn || mPaintedTxn.value() != aTxnId) {
fail("received transaction before receiving paint");
}
mPaintedTxn = Nothing();
mCompletedTxn = Some(aTxnId);
return IPC_OK();
}
void
TestOffMainThreadPaintingParent::NotifyFinishedPaint(const uint64_t& aTxnId)
{
if (mCompletedTxn && mCompletedTxn.value() >= aTxnId) {
fail("received paint after receiving transaction");
}
if (mPaintedTxn) {
fail("painted again before completing previous transaction");
}
mPaintedTxn = Some(aTxnId);
}
ipc::IPCResult
TestOffMainThreadPaintingParent::RecvAsyncMessage(const uint64_t& aTxnId)
{
if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) {
fail("sync message received out of order");
return IPC_FAIL_NO_REASON(this);
}
mAsyncMessages++;
return IPC_OK();
}
ipc::IPCResult
TestOffMainThreadPaintingParent::RecvSyncMessage(const uint64_t& aTxnId)
{
if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) {
fail("sync message received out of order");
return IPC_FAIL_NO_REASON(this);
}
if (mSyncMessages >= mAsyncMessages) {
fail("sync message received before async message");
return IPC_FAIL_NO_REASON(this);
}
mSyncMessages++;
return IPC_OK();
}
ipc::IPCResult
TestOffMainThreadPaintingParent::RecvEndTest()
{
if (!mCompletedTxn || mCompletedTxn.value() != 1) {
fail("expected to complete a transaction");
}
if (mAsyncMessages != 1) {
fail("expected to get 1 async message");
}
if (mSyncMessages != 1) {
fail("expected to get 1 sync message");
}
passed("ok");
mPaintActor->Close();
Close();
return IPC_OK();
}
void
TestOffMainThreadPaintingParent::ActorDestroy(ActorDestroyReason aWhy)
{
if (aWhy != NormalShutdown) {
fail("child process aborted");
}
QuitParent();
}
/**************************
* PTestLayoutThreadChild *
**************************/
TestOffMainThreadPaintingChild::TestOffMainThreadPaintingChild()
: mNextTxnId(1)
{
}
TestOffMainThreadPaintingChild::~TestOffMainThreadPaintingChild()
{
}
ipc::IPCResult
TestOffMainThreadPaintingChild::RecvStartTest(ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint)
{
mPaintThread = MakeUnique<base::Thread>("PaintThread");
if (!mPaintThread->Start()) {
return IPC_FAIL_NO_REASON(this);
}
mPaintActor = new TestPaintThreadChild(GetIPCChannel());
RefPtr<Runnable> task = NewRunnableMethod<ipc::Endpoint<PTestPaintThreadChild>&&>(
"TestPaintthreadChild::Bind", mPaintActor, &TestPaintThreadChild::Bind, Move(aEndpoint));
mPaintThread->message_loop()->PostTask(task.forget());
IssueTransaction();
return IPC_OK();
}
void
TestOffMainThreadPaintingChild::ActorDestroy(ActorDestroyReason aWhy)
{
RefPtr<Runnable> task = NewRunnableMethod(
"TestPaintThreadChild::Close", mPaintActor, &TestPaintThreadChild::Close);
mPaintThread->message_loop()->PostTask(task.forget());
mPaintThread = nullptr;
QuitChild();
}
void
TestOffMainThreadPaintingChild::ProcessingError(Result aCode, const char* aReason)
{
MOZ_CRASH("Aborting child due to IPC error");
}
void
TestOffMainThreadPaintingChild::IssueTransaction()
{
GetIPCChannel()->BeginPostponingSends();
uint64_t txnId = mNextTxnId++;
// Start painting before we send the message.
RefPtr<Runnable> task = NewRunnableMethod<uint64_t>(
"TestPaintThreadChild::BeginPaintingForTxn", mPaintActor, &TestPaintThreadChild::BeginPaintingForTxn, txnId);
mPaintThread->message_loop()->PostTask(task.forget());
// Simulate some gecko main thread stuff.
SendFinishedLayout(txnId);
SendAsyncMessage(txnId);
SendSyncMessage(txnId);
SendEndTest();
}
/**************************
* PTestPaintThreadParent *
**************************/
TestPaintThreadParent::TestPaintThreadParent(TestOffMainThreadPaintingParent* aMainBridge)
: mMainBridge(aMainBridge)
{
}
TestPaintThreadParent::~TestPaintThreadParent()
{
}
bool
TestPaintThreadParent::Bind(ipc::Endpoint<PTestPaintThreadParent>&& aEndpoint)
{
if (!aEndpoint.Bind(this)) {
return false;
}
AddRef();
return true;
}
ipc::IPCResult
TestPaintThreadParent::RecvFinishedPaint(const uint64_t& aTxnId)
{
mMainBridge->NotifyFinishedPaint(aTxnId);
return IPC_OK();
}
void
TestPaintThreadParent::ActorDestroy(ActorDestroyReason aWhy)
{
}
void
TestPaintThreadParent::DeallocPTestPaintThreadParent()
{
Release();
}
/*************************
* PTestPaintThreadChild *
*************************/
TestPaintThreadChild::TestPaintThreadChild(MessageChannel* aMainChannel)
: mCanSend(false),
mMainChannel(aMainChannel)
{
}
TestPaintThreadChild::~TestPaintThreadChild()
{
}
void
TestPaintThreadChild::Bind(ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint)
{
if (!aEndpoint.Bind(this)) {
MOZ_CRASH("could not bind paint child endpoint");
}
AddRef();
mCanSend = true;
}
void
TestPaintThreadChild::BeginPaintingForTxn(uint64_t aTxnId)
{
MOZ_RELEASE_ASSERT(!NS_IsMainThread());
// Sleep for some time to simulate painting being slow.
PR_Sleep(PR_MillisecondsToInterval(500));
SendFinishedPaint(aTxnId);
mMainChannel->StopPostponingSends();
}
void
TestPaintThreadChild::ActorDestroy(ActorDestroyReason aWhy)
{
mCanSend = false;
}
void
TestPaintThreadChild::Close()
{
MOZ_RELEASE_ASSERT(!NS_IsMainThread());
if (mCanSend) {
PTestPaintThreadChild::Close();
}
}
void
TestPaintThreadChild::DeallocPTestPaintThreadChild()
{
Release();
}
} // namespace _ipdltest
} // namespace mozilla