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