Bug 1632310 [wpt PR 23186] - Add test for computed versus resolved style., a=testonly
[gecko.git] / gfx / layers / CanvasDrawEventRecorder.cpp
blobadcb927c098fc53a34119fa9f6298c430f4583c7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CanvasDrawEventRecorder.h"
9 #include <string.h>
11 #include "nsThreadUtils.h"
13 namespace mozilla {
14 namespace layers {
16 static const int32_t kCheckpointEventType = -1;
17 static const uint32_t kMaxSpinCount = 200;
19 static const TimeDuration kTimeout = TimeDuration::FromMilliseconds(100);
20 static const int32_t kTimeoutRetryCount = 50;
22 static const uint32_t kCacheLineSize = 64;
23 static const uint32_t kStreamSize = 64 * 1024;
24 static const uint32_t kShmemSize = kStreamSize + (2 * kCacheLineSize);
26 static_assert((static_cast<uint64_t>(UINT32_MAX) + 1) % kStreamSize == 0,
27 "kStreamSize must be a power of two.");
29 bool CanvasEventRingBuffer::InitWriter(
30 base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aReadHandle,
31 CrossProcessSemaphoreHandle* aReaderSem,
32 CrossProcessSemaphoreHandle* aWriterSem,
33 UniquePtr<WriterServices> aWriterServices) {
34 mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
35 if (NS_WARN_IF(!mSharedMemory->Create(kShmemSize)) ||
36 NS_WARN_IF(!mSharedMemory->Map(kShmemSize))) {
37 return false;
40 if (NS_WARN_IF(!mSharedMemory->ShareToProcess(aOtherPid, aReadHandle))) {
41 return false;
44 mSharedMemory->CloseHandle();
46 mBuf = static_cast<char*>(mSharedMemory->memory());
47 mBufPos = mBuf;
48 mAvailable = kStreamSize;
50 static_assert(sizeof(ReadFooter) <= kCacheLineSize,
51 "ReadFooter must fit in kCacheLineSize.");
52 mRead = reinterpret_cast<ReadFooter*>(mBuf + kStreamSize);
53 mRead->count = 0;
54 mRead->returnCount = 0;
55 mRead->state = State::Processing;
57 static_assert(sizeof(WriteFooter) <= kCacheLineSize,
58 "WriteFooter must fit in kCacheLineSize.");
59 mWrite = reinterpret_cast<WriteFooter*>(mBuf + kStreamSize + kCacheLineSize);
60 mWrite->count = 0;
61 mWrite->returnCount = 0;
62 mWrite->requiredDifference = 0;
63 mWrite->state = State::Processing;
65 mReaderSemaphore.reset(
66 CrossProcessSemaphore::Create("SharedMemoryStreamParent", 0));
67 *aReaderSem = mReaderSemaphore->ShareToProcess(aOtherPid);
68 mReaderSemaphore->CloseHandle();
69 mWriterSemaphore.reset(
70 CrossProcessSemaphore::Create("SharedMemoryStreamChild", 0));
71 *aWriterSem = mWriterSemaphore->ShareToProcess(aOtherPid);
72 mWriterSemaphore->CloseHandle();
74 mWriterServices = std::move(aWriterServices);
76 mGood = true;
77 return true;
80 bool CanvasEventRingBuffer::InitReader(
81 const ipc::SharedMemoryBasic::Handle& aReadHandle,
82 const CrossProcessSemaphoreHandle& aReaderSem,
83 const CrossProcessSemaphoreHandle& aWriterSem,
84 UniquePtr<ReaderServices> aReaderServices) {
85 mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
86 if (NS_WARN_IF(!mSharedMemory->SetHandle(
87 aReadHandle, ipc::SharedMemory::RightsReadWrite)) ||
88 NS_WARN_IF(!mSharedMemory->Map(kShmemSize))) {
89 return false;
92 mSharedMemory->CloseHandle();
94 mBuf = static_cast<char*>(mSharedMemory->memory());
95 mRead = reinterpret_cast<ReadFooter*>(mBuf + kStreamSize);
96 mWrite = reinterpret_cast<WriteFooter*>(mBuf + kStreamSize + kCacheLineSize);
97 mReaderSemaphore.reset(CrossProcessSemaphore::Create(aReaderSem));
98 mReaderSemaphore->CloseHandle();
99 mWriterSemaphore.reset(CrossProcessSemaphore::Create(aWriterSem));
100 mWriterSemaphore->CloseHandle();
102 mReaderServices = std::move(aReaderServices);
104 mGood = true;
105 return true;
108 bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableSpace() {
109 if (!good()) {
110 return false;
113 uint32_t bufPos = mOurCount % kStreamSize;
114 uint32_t maxToWrite = kStreamSize - bufPos;
115 mAvailable = std::min(maxToWrite, WaitForBytesToWrite());
116 if (!mAvailable) {
117 mGood = false;
118 mBufPos = nullptr;
119 return false;
122 mBufPos = mBuf + bufPos;
123 return true;
126 void CanvasEventRingBuffer::write(const char* const aData, const size_t aSize) {
127 const char* curDestPtr = aData;
128 size_t remainingToWrite = aSize;
129 if (remainingToWrite > mAvailable) {
130 if (!WaitForAndRecalculateAvailableSpace()) {
131 return;
135 if (remainingToWrite <= mAvailable) {
136 memcpy(mBufPos, curDestPtr, remainingToWrite);
137 UpdateWriteTotalsBy(remainingToWrite);
138 return;
141 do {
142 memcpy(mBufPos, curDestPtr, mAvailable);
143 IncrementWriteCountBy(mAvailable);
144 curDestPtr += mAvailable;
145 remainingToWrite -= mAvailable;
146 if (!WaitForAndRecalculateAvailableSpace()) {
147 return;
149 } while (remainingToWrite > mAvailable);
151 memcpy(mBufPos, curDestPtr, remainingToWrite);
152 UpdateWriteTotalsBy(remainingToWrite);
155 void CanvasEventRingBuffer::IncrementWriteCountBy(uint32_t aCount) {
156 mOurCount += aCount;
157 mWrite->count = mOurCount;
158 if (mRead->state != State::Processing) {
159 CheckAndSignalReader();
163 void CanvasEventRingBuffer::UpdateWriteTotalsBy(uint32_t aCount) {
164 IncrementWriteCountBy(aCount);
165 mBufPos += aCount;
166 mAvailable -= aCount;
169 bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableData() {
170 if (!good()) {
171 return false;
174 uint32_t bufPos = mOurCount % kStreamSize;
175 uint32_t maxToRead = kStreamSize - bufPos;
176 mAvailable = std::min(maxToRead, WaitForBytesToRead());
177 if (!mAvailable) {
178 mGood = false;
179 mBufPos = nullptr;
180 return false;
183 mBufPos = mBuf + bufPos;
184 return true;
187 void CanvasEventRingBuffer::read(char* const aOut, const size_t aSize) {
188 char* curSrcPtr = aOut;
189 size_t remainingToRead = aSize;
190 if (remainingToRead > mAvailable) {
191 if (!WaitForAndRecalculateAvailableData()) {
192 return;
196 if (remainingToRead <= mAvailable) {
197 memcpy(curSrcPtr, mBufPos, remainingToRead);
198 UpdateReadTotalsBy(remainingToRead);
199 return;
202 do {
203 memcpy(curSrcPtr, mBufPos, mAvailable);
204 IncrementReadCountBy(mAvailable);
205 curSrcPtr += mAvailable;
206 remainingToRead -= mAvailable;
207 if (!WaitForAndRecalculateAvailableData()) {
208 return;
210 } while (remainingToRead > mAvailable);
212 memcpy(curSrcPtr, mBufPos, remainingToRead);
213 UpdateReadTotalsBy(remainingToRead);
216 void CanvasEventRingBuffer::IncrementReadCountBy(uint32_t aCount) {
217 mOurCount += aCount;
218 mRead->count = mOurCount;
219 if (mWrite->state != State::Processing) {
220 CheckAndSignalWriter();
224 void CanvasEventRingBuffer::UpdateReadTotalsBy(uint32_t aCount) {
225 IncrementReadCountBy(aCount);
226 mBufPos += aCount;
227 mAvailable -= aCount;
230 void CanvasEventRingBuffer::CheckAndSignalReader() {
231 do {
232 switch (mRead->state) {
233 case State::Processing:
234 return;
235 case State::AboutToWait:
236 // The reader is making a decision about whether to wait. So, we must
237 // wait until it has decided to avoid races. Check if the reader is
238 // closed to avoid hangs.
239 if (mWriterServices->ReaderClosed()) {
240 return;
242 continue;
243 case State::Waiting:
244 if (mRead->count != mOurCount) {
245 // We have to use compareExchange here because the reader can change
246 // from Waiting to Stopped.
247 if (mRead->state.compareExchange(State::Waiting, State::Processing)) {
248 mReaderSemaphore->Signal();
249 return;
252 MOZ_ASSERT(mRead->state == State::Stopped);
253 continue;
255 return;
256 case State::Stopped:
257 if (mRead->count != mOurCount) {
258 mRead->state = State::Processing;
259 mWriterServices->ResumeReader();
261 return;
262 default:
263 MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
264 return;
266 } while (true);
269 bool CanvasEventRingBuffer::HasDataToRead() {
270 return (mWrite->count != mOurCount);
273 bool CanvasEventRingBuffer::StopIfEmpty() {
274 // Double-check that the writer isn't waiting.
275 CheckAndSignalWriter();
276 mRead->state = State::AboutToWait;
277 if (HasDataToRead()) {
278 mRead->state = State::Processing;
279 return false;
282 mRead->state = State::Stopped;
283 return true;
286 bool CanvasEventRingBuffer::WaitForDataToRead(TimeDuration aTimeout,
287 int32_t aRetryCount) {
288 uint32_t spinCount = kMaxSpinCount;
289 do {
290 if (HasDataToRead()) {
291 return true;
293 } while (--spinCount != 0);
295 // Double-check that the writer isn't waiting.
296 CheckAndSignalWriter();
297 mRead->state = State::AboutToWait;
298 if (HasDataToRead()) {
299 mRead->state = State::Processing;
300 return true;
303 mRead->state = State::Waiting;
304 do {
305 if (mReaderSemaphore->Wait(Some(aTimeout))) {
306 MOZ_RELEASE_ASSERT(HasDataToRead());
307 return true;
310 if (mReaderServices->WriterClosed()) {
311 // Something has gone wrong on the writing side, just return false so
312 // that we can hopefully recover.
313 return false;
315 } while (aRetryCount-- > 0);
317 // We have to use compareExchange here because the writer can change our
318 // state if we are waiting. signaled
319 if (!mRead->state.compareExchange(State::Waiting, State::Stopped)) {
320 MOZ_RELEASE_ASSERT(HasDataToRead());
321 MOZ_RELEASE_ASSERT(mRead->state == State::Processing);
322 // The writer has just signaled us, so consume it before returning
323 MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait());
324 return true;
327 return false;
330 int32_t CanvasEventRingBuffer::ReadNextEvent() {
331 int32_t nextEvent;
332 ReadElement(*this, nextEvent);
333 while (nextEvent == kCheckpointEventType) {
334 ReadElement(*this, nextEvent);
337 return nextEvent;
340 uint32_t CanvasEventRingBuffer::CreateCheckpoint() {
341 WriteElement(*this, kCheckpointEventType);
342 return mOurCount;
345 bool CanvasEventRingBuffer::WaitForCheckpoint(uint32_t aCheckpoint) {
346 return WaitForReadCount(aCheckpoint, kTimeout);
349 void CanvasEventRingBuffer::CheckAndSignalWriter() {
350 do {
351 switch (mWrite->state) {
352 case State::Processing:
353 return;
354 case State::AboutToWait:
355 // The writer is making a decision about whether to wait. So, we must
356 // wait until it has decided to avoid races. Check if the writer is
357 // closed to avoid hangs.
358 if (mReaderServices->WriterClosed()) {
359 return;
361 continue;
362 case State::Waiting:
363 if (mWrite->count - mOurCount <= mWrite->requiredDifference) {
364 mWrite->state = State::Processing;
365 mWriterSemaphore->Signal();
367 return;
368 default:
369 MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
370 return;
372 } while (true);
375 bool CanvasEventRingBuffer::WaitForReadCount(uint32_t aReadCount,
376 TimeDuration aTimeout) {
377 uint32_t requiredDifference = mOurCount - aReadCount;
378 uint32_t spinCount = kMaxSpinCount;
379 do {
380 if (mOurCount - mRead->count <= requiredDifference) {
381 return true;
383 } while (--spinCount != 0);
385 // Double-check that the reader isn't waiting.
386 CheckAndSignalReader();
387 mWrite->state = State::AboutToWait;
388 if (mOurCount - mRead->count <= requiredDifference) {
389 mWrite->state = State::Processing;
390 return true;
393 mWrite->requiredDifference = requiredDifference;
394 mWrite->state = State::Waiting;
396 // Wait unless we detect the reading side has closed.
397 while (!mWriterServices->ReaderClosed()) {
398 if (mWriterSemaphore->Wait(Some(aTimeout))) {
399 MOZ_ASSERT(mOurCount - mRead->count <= requiredDifference);
400 return true;
404 return false;
407 uint32_t CanvasEventRingBuffer::WaitForBytesToWrite() {
408 uint32_t streamFullReadCount = mOurCount - kStreamSize;
409 if (!WaitForReadCount(streamFullReadCount + 1, kTimeout)) {
410 mGood = false;
411 return 0;
414 return mRead->count - streamFullReadCount;
417 uint32_t CanvasEventRingBuffer::WaitForBytesToRead() {
418 if (!WaitForDataToRead(kTimeout, kTimeoutRetryCount)) {
419 return 0;
422 return mWrite->count - mOurCount;
425 void CanvasEventRingBuffer::ReturnWrite(const char* aData, size_t aSize) {
426 uint32_t writeCount = mRead->returnCount;
427 uint32_t bufPos = writeCount % kStreamSize;
428 uint32_t bufRemaining = kStreamSize - bufPos;
429 uint32_t availableToWrite =
430 std::min(bufRemaining, (mWrite->returnCount + kStreamSize - writeCount));
431 while (availableToWrite < aSize) {
432 if (availableToWrite) {
433 memcpy(mBuf + bufPos, aData, availableToWrite);
434 writeCount += availableToWrite;
435 mRead->returnCount = writeCount;
436 bufPos = writeCount % kStreamSize;
437 bufRemaining = kStreamSize - bufPos;
438 aData += availableToWrite;
439 aSize -= availableToWrite;
440 } else if (mReaderServices->WriterClosed()) {
441 return;
444 availableToWrite = std::min(
445 bufRemaining, (mWrite->returnCount + kStreamSize - writeCount));
448 memcpy(mBuf + bufPos, aData, aSize);
449 writeCount += aSize;
450 mRead->returnCount = writeCount;
453 void CanvasEventRingBuffer::ReturnRead(char* aOut, size_t aSize) {
454 // First wait for the event returning the data to be read.
455 WaitForCheckpoint(mOurCount);
456 uint32_t readCount = mWrite->returnCount;
458 // If the event sending back data fails to play then it will ReturnWrite
459 // nothing. So, wait until something has been written or the reader has
460 // stopped processing.
461 while (readCount == mRead->returnCount) {
462 // We recheck the count, because the other side can write all the data and
463 // started waiting in between these two lines.
464 if (mRead->state != State::Processing && readCount == mRead->returnCount) {
465 return;
469 uint32_t bufPos = readCount % kStreamSize;
470 uint32_t bufRemaining = kStreamSize - bufPos;
471 uint32_t availableToRead =
472 std::min(bufRemaining, (mRead->returnCount - readCount));
473 while (availableToRead < aSize) {
474 if (availableToRead) {
475 memcpy(aOut, mBuf + bufPos, availableToRead);
476 readCount += availableToRead;
477 mWrite->returnCount = readCount;
478 bufPos = readCount % kStreamSize;
479 bufRemaining = kStreamSize - bufPos;
480 aOut += availableToRead;
481 aSize -= availableToRead;
482 } else if (mWriterServices->ReaderClosed()) {
483 return;
486 availableToRead = std::min(bufRemaining, (mRead->returnCount - readCount));
489 memcpy(aOut, mBuf + bufPos, aSize);
490 readCount += aSize;
491 mWrite->returnCount = readCount;
494 void CanvasDrawEventRecorder::RecordSourceSurfaceDestruction(
495 gfx::SourceSurface* aSurface) {
496 // We must only record things on the main thread and surfaces that have been
497 // recorded can sometimes be destroyed off the main thread.
498 if (NS_IsMainThread()) {
499 DrawEventRecorderPrivate::RecordSourceSurfaceDestruction(aSurface);
500 return;
503 NS_DispatchToMainThread(NewRunnableMethod<gfx::SourceSurface*>(
504 "DrawEventRecorderPrivate::RecordSourceSurfaceDestruction", this,
505 &DrawEventRecorderPrivate::RecordSourceSurfaceDestruction, aSurface));
508 } // namespace layers
509 } // namespace mozilla