1 #include "TestOffMainThreadPainting.h"
2 
3 #include "IPDLUnitTests.h"  // fail etc.
4 #include "mozilla/Unused.h"
5 #include <prinrval.h>
6 #include <prthread.h>
7 
8 namespace mozilla {
9 namespace _ipdltest {
10 
TestOffMainThreadPaintingParent()11 TestOffMainThreadPaintingParent::TestOffMainThreadPaintingParent()
12     : mAsyncMessages(0), mSyncMessages(0) {}
13 
~TestOffMainThreadPaintingParent()14 TestOffMainThreadPaintingParent::~TestOffMainThreadPaintingParent() {}
15 
Main()16 void TestOffMainThreadPaintingParent::Main() {
17   ipc::Endpoint<PTestPaintThreadParent> parentPipe;
18   ipc::Endpoint<PTestPaintThreadChild> childPipe;
19   nsresult rv = PTestPaintThread::CreateEndpoints(
20       base::GetCurrentProcId(), OtherPid(), &parentPipe, &childPipe);
21   if (NS_FAILED(rv)) {
22     fail("create pipes");
23   }
24 
25   mPaintActor = new TestPaintThreadParent(this);
26   if (!mPaintActor->Bind(std::move(parentPipe))) {
27     fail("bind parent pipe");
28   }
29 
30   if (!SendStartTest(std::move(childPipe))) {
31     fail("sending Start");
32   }
33 }
34 
RecvFinishedLayout(const uint64_t & aTxnId)35 ipc::IPCResult TestOffMainThreadPaintingParent::RecvFinishedLayout(
36     const uint64_t& aTxnId) {
37   if (!mPaintedTxn || mPaintedTxn.value() != aTxnId) {
38     fail("received transaction before receiving paint");
39   }
40   mPaintedTxn = Nothing();
41   mCompletedTxn = Some(aTxnId);
42   return IPC_OK();
43 }
44 
NotifyFinishedPaint(const uint64_t & aTxnId)45 void TestOffMainThreadPaintingParent::NotifyFinishedPaint(
46     const uint64_t& aTxnId) {
47   if (mCompletedTxn && mCompletedTxn.value() >= aTxnId) {
48     fail("received paint after receiving transaction");
49   }
50   if (mPaintedTxn) {
51     fail("painted again before completing previous transaction");
52   }
53   mPaintedTxn = Some(aTxnId);
54 }
55 
RecvAsyncMessage(const uint64_t & aTxnId)56 ipc::IPCResult TestOffMainThreadPaintingParent::RecvAsyncMessage(
57     const uint64_t& aTxnId) {
58   if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) {
59     fail("sync message received out of order");
60     return IPC_FAIL_NO_REASON(this);
61   }
62   mAsyncMessages++;
63   return IPC_OK();
64 }
65 
RecvSyncMessage(const uint64_t & aTxnId)66 ipc::IPCResult TestOffMainThreadPaintingParent::RecvSyncMessage(
67     const uint64_t& aTxnId) {
68   if (!mCompletedTxn || mCompletedTxn.value() != aTxnId) {
69     fail("sync message received out of order");
70     return IPC_FAIL_NO_REASON(this);
71   }
72   if (mSyncMessages >= mAsyncMessages) {
73     fail("sync message received before async message");
74     return IPC_FAIL_NO_REASON(this);
75   }
76   mSyncMessages++;
77   return IPC_OK();
78 }
79 
RecvEndTest()80 ipc::IPCResult TestOffMainThreadPaintingParent::RecvEndTest() {
81   if (!mCompletedTxn || mCompletedTxn.value() != 1) {
82     fail("expected to complete a transaction");
83   }
84   if (mAsyncMessages != 1) {
85     fail("expected to get 1 async message");
86   }
87   if (mSyncMessages != 1) {
88     fail("expected to get 1 sync message");
89   }
90 
91   passed("ok");
92 
93   mPaintActor->Close();
94   Close();
95   return IPC_OK();
96 }
97 
ActorDestroy(ActorDestroyReason aWhy)98 void TestOffMainThreadPaintingParent::ActorDestroy(ActorDestroyReason aWhy) {
99   if (aWhy != NormalShutdown) {
100     fail("child process aborted");
101   }
102   QuitParent();
103 }
104 
105 /**************************
106  * PTestLayoutThreadChild *
107  **************************/
108 
TestOffMainThreadPaintingChild()109 TestOffMainThreadPaintingChild::TestOffMainThreadPaintingChild()
110     : mNextTxnId(1) {}
111 
~TestOffMainThreadPaintingChild()112 TestOffMainThreadPaintingChild::~TestOffMainThreadPaintingChild() {}
113 
RecvStartTest(ipc::Endpoint<PTestPaintThreadChild> && aEndpoint)114 ipc::IPCResult TestOffMainThreadPaintingChild::RecvStartTest(
115     ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint) {
116   mPaintThread = MakeUnique<base::Thread>("PaintThread");
117   if (!mPaintThread->Start()) {
118     return IPC_FAIL_NO_REASON(this);
119   }
120 
121   mPaintActor = new TestPaintThreadChild(GetIPCChannel());
122   RefPtr<Runnable> task =
123       NewRunnableMethod<ipc::Endpoint<PTestPaintThreadChild>&&>(
124           "TestPaintthreadChild::Bind", mPaintActor,
125           &TestPaintThreadChild::Bind, std::move(aEndpoint));
126   mPaintThread->message_loop()->PostTask(task.forget());
127 
128   IssueTransaction();
129   return IPC_OK();
130 }
131 
ActorDestroy(ActorDestroyReason aWhy)132 void TestOffMainThreadPaintingChild::ActorDestroy(ActorDestroyReason aWhy) {
133   RefPtr<Runnable> task = NewRunnableMethod(
134       "TestPaintThreadChild::Close", mPaintActor, &TestPaintThreadChild::Close);
135   mPaintThread->message_loop()->PostTask(task.forget());
136   mPaintThread = nullptr;
137 
138   QuitChild();
139 }
140 
ProcessingError(Result aCode,const char * aReason)141 void TestOffMainThreadPaintingChild::ProcessingError(Result aCode,
142                                                      const char* aReason) {
143   MOZ_CRASH("Aborting child due to IPC error");
144 }
145 
IssueTransaction()146 void TestOffMainThreadPaintingChild::IssueTransaction() {
147   GetIPCChannel()->BeginPostponingSends();
148 
149   uint64_t txnId = mNextTxnId++;
150 
151   // Start painting before we send the message.
152   RefPtr<Runnable> task = NewRunnableMethod<uint64_t>(
153       "TestPaintThreadChild::BeginPaintingForTxn", mPaintActor,
154       &TestPaintThreadChild::BeginPaintingForTxn, txnId);
155   mPaintThread->message_loop()->PostTask(task.forget());
156 
157   // Simulate some gecko main thread stuff.
158   SendFinishedLayout(txnId);
159   SendAsyncMessage(txnId);
160   SendSyncMessage(txnId);
161   SendEndTest();
162 }
163 
164 /**************************
165  * PTestPaintThreadParent *
166  **************************/
167 
TestPaintThreadParent(TestOffMainThreadPaintingParent * aMainBridge)168 TestPaintThreadParent::TestPaintThreadParent(
169     TestOffMainThreadPaintingParent* aMainBridge)
170     : mMainBridge(aMainBridge) {}
171 
~TestPaintThreadParent()172 TestPaintThreadParent::~TestPaintThreadParent() {}
173 
Bind(ipc::Endpoint<PTestPaintThreadParent> && aEndpoint)174 bool TestPaintThreadParent::Bind(
175     ipc::Endpoint<PTestPaintThreadParent>&& aEndpoint) {
176   if (!aEndpoint.Bind(this)) {
177     return false;
178   }
179 
180   AddRef();
181   return true;
182 }
183 
RecvFinishedPaint(const uint64_t & aTxnId)184 ipc::IPCResult TestPaintThreadParent::RecvFinishedPaint(
185     const uint64_t& aTxnId) {
186   mMainBridge->NotifyFinishedPaint(aTxnId);
187   return IPC_OK();
188 }
189 
ActorDestroy(ActorDestroyReason aWhy)190 void TestPaintThreadParent::ActorDestroy(ActorDestroyReason aWhy) {}
191 
DeallocPTestPaintThreadParent()192 void TestPaintThreadParent::DeallocPTestPaintThreadParent() { Release(); }
193 
194 /*************************
195  * PTestPaintThreadChild *
196  *************************/
197 
TestPaintThreadChild(MessageChannel * aMainChannel)198 TestPaintThreadChild::TestPaintThreadChild(MessageChannel* aMainChannel)
199     : mCanSend(false), mMainChannel(aMainChannel) {}
200 
~TestPaintThreadChild()201 TestPaintThreadChild::~TestPaintThreadChild() {}
202 
Bind(ipc::Endpoint<PTestPaintThreadChild> && aEndpoint)203 void TestPaintThreadChild::Bind(
204     ipc::Endpoint<PTestPaintThreadChild>&& aEndpoint) {
205   if (!aEndpoint.Bind(this)) {
206     MOZ_CRASH("could not bind paint child endpoint");
207   }
208   AddRef();
209   mCanSend = true;
210 }
211 
BeginPaintingForTxn(uint64_t aTxnId)212 void TestPaintThreadChild::BeginPaintingForTxn(uint64_t aTxnId) {
213   MOZ_RELEASE_ASSERT(!NS_IsMainThread());
214 
215   // Sleep for some time to simulate painting being slow.
216   PR_Sleep(PR_MillisecondsToInterval(500));
217   SendFinishedPaint(aTxnId);
218 
219   mMainChannel->StopPostponingSends();
220 }
221 
ActorDestroy(ActorDestroyReason aWhy)222 void TestPaintThreadChild::ActorDestroy(ActorDestroyReason aWhy) {
223   mCanSend = false;
224 }
225 
Close()226 void TestPaintThreadChild::Close() {
227   MOZ_RELEASE_ASSERT(!NS_IsMainThread());
228 
229   if (mCanSend) {
230     PTestPaintThreadChild::Close();
231   }
232 }
233 
DeallocPTestPaintThreadChild()234 void TestPaintThreadChild::DeallocPTestPaintThreadChild() { Release(); }
235 
236 }  // namespace _ipdltest
237 }  // namespace mozilla
238