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"
11 #include "nsThreadUtils.h"
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
))) {
40 if (NS_WARN_IF(!mSharedMemory
->ShareToProcess(aOtherPid
, aReadHandle
))) {
44 mSharedMemory
->CloseHandle();
46 mBuf
= static_cast<char*>(mSharedMemory
->memory());
48 mAvailable
= kStreamSize
;
50 static_assert(sizeof(ReadFooter
) <= kCacheLineSize
,
51 "ReadFooter must fit in kCacheLineSize.");
52 mRead
= reinterpret_cast<ReadFooter
*>(mBuf
+ kStreamSize
);
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
);
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
);
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
))) {
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
);
108 bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableSpace() {
113 uint32_t bufPos
= mOurCount
% kStreamSize
;
114 uint32_t maxToWrite
= kStreamSize
- bufPos
;
115 mAvailable
= std::min(maxToWrite
, WaitForBytesToWrite());
122 mBufPos
= mBuf
+ bufPos
;
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()) {
135 if (remainingToWrite
<= mAvailable
) {
136 memcpy(mBufPos
, curDestPtr
, remainingToWrite
);
137 UpdateWriteTotalsBy(remainingToWrite
);
142 memcpy(mBufPos
, curDestPtr
, mAvailable
);
143 IncrementWriteCountBy(mAvailable
);
144 curDestPtr
+= mAvailable
;
145 remainingToWrite
-= mAvailable
;
146 if (!WaitForAndRecalculateAvailableSpace()) {
149 } while (remainingToWrite
> mAvailable
);
151 memcpy(mBufPos
, curDestPtr
, remainingToWrite
);
152 UpdateWriteTotalsBy(remainingToWrite
);
155 void CanvasEventRingBuffer::IncrementWriteCountBy(uint32_t aCount
) {
157 mWrite
->count
= mOurCount
;
158 if (mRead
->state
!= State::Processing
) {
159 CheckAndSignalReader();
163 void CanvasEventRingBuffer::UpdateWriteTotalsBy(uint32_t aCount
) {
164 IncrementWriteCountBy(aCount
);
166 mAvailable
-= aCount
;
169 bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableData() {
174 uint32_t bufPos
= mOurCount
% kStreamSize
;
175 uint32_t maxToRead
= kStreamSize
- bufPos
;
176 mAvailable
= std::min(maxToRead
, WaitForBytesToRead());
183 mBufPos
= mBuf
+ bufPos
;
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()) {
196 if (remainingToRead
<= mAvailable
) {
197 memcpy(curSrcPtr
, mBufPos
, remainingToRead
);
198 UpdateReadTotalsBy(remainingToRead
);
203 memcpy(curSrcPtr
, mBufPos
, mAvailable
);
204 IncrementReadCountBy(mAvailable
);
205 curSrcPtr
+= mAvailable
;
206 remainingToRead
-= mAvailable
;
207 if (!WaitForAndRecalculateAvailableData()) {
210 } while (remainingToRead
> mAvailable
);
212 memcpy(curSrcPtr
, mBufPos
, remainingToRead
);
213 UpdateReadTotalsBy(remainingToRead
);
216 void CanvasEventRingBuffer::IncrementReadCountBy(uint32_t aCount
) {
218 mRead
->count
= mOurCount
;
219 if (mWrite
->state
!= State::Processing
) {
220 CheckAndSignalWriter();
224 void CanvasEventRingBuffer::UpdateReadTotalsBy(uint32_t aCount
) {
225 IncrementReadCountBy(aCount
);
227 mAvailable
-= aCount
;
230 void CanvasEventRingBuffer::CheckAndSignalReader() {
232 switch (mRead
->state
) {
233 case State::Processing
:
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()) {
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();
252 MOZ_ASSERT(mRead
->state
== State::Stopped
);
257 if (mRead
->count
!= mOurCount
) {
258 mRead
->state
= State::Processing
;
259 mWriterServices
->ResumeReader();
263 MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
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
;
282 mRead
->state
= State::Stopped
;
286 bool CanvasEventRingBuffer::WaitForDataToRead(TimeDuration aTimeout
,
287 int32_t aRetryCount
) {
288 uint32_t spinCount
= kMaxSpinCount
;
290 if (HasDataToRead()) {
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
;
303 mRead
->state
= State::Waiting
;
305 if (mReaderSemaphore
->Wait(Some(aTimeout
))) {
306 MOZ_RELEASE_ASSERT(HasDataToRead());
310 if (mReaderServices
->WriterClosed()) {
311 // Something has gone wrong on the writing side, just return false so
312 // that we can hopefully recover.
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());
330 int32_t CanvasEventRingBuffer::ReadNextEvent() {
332 ReadElement(*this, nextEvent
);
333 while (nextEvent
== kCheckpointEventType
) {
334 ReadElement(*this, nextEvent
);
340 uint32_t CanvasEventRingBuffer::CreateCheckpoint() {
341 WriteElement(*this, kCheckpointEventType
);
345 bool CanvasEventRingBuffer::WaitForCheckpoint(uint32_t aCheckpoint
) {
346 return WaitForReadCount(aCheckpoint
, kTimeout
);
349 void CanvasEventRingBuffer::CheckAndSignalWriter() {
351 switch (mWrite
->state
) {
352 case State::Processing
:
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()) {
363 if (mWrite
->count
- mOurCount
<= mWrite
->requiredDifference
) {
364 mWrite
->state
= State::Processing
;
365 mWriterSemaphore
->Signal();
369 MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
375 bool CanvasEventRingBuffer::WaitForReadCount(uint32_t aReadCount
,
376 TimeDuration aTimeout
) {
377 uint32_t requiredDifference
= mOurCount
- aReadCount
;
378 uint32_t spinCount
= kMaxSpinCount
;
380 if (mOurCount
- mRead
->count
<= requiredDifference
) {
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
;
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
);
407 uint32_t CanvasEventRingBuffer::WaitForBytesToWrite() {
408 uint32_t streamFullReadCount
= mOurCount
- kStreamSize
;
409 if (!WaitForReadCount(streamFullReadCount
+ 1, kTimeout
)) {
414 return mRead
->count
- streamFullReadCount
;
417 uint32_t CanvasEventRingBuffer::WaitForBytesToRead() {
418 if (!WaitForDataToRead(kTimeout
, kTimeoutRetryCount
)) {
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()) {
444 availableToWrite
= std::min(
445 bufRemaining
, (mWrite
->returnCount
+ kStreamSize
- writeCount
));
448 memcpy(mBuf
+ bufPos
, aData
, 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
) {
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()) {
486 availableToRead
= std::min(bufRemaining
, (mRead
->returnCount
- readCount
));
489 memcpy(aOut
, mBuf
+ bufPos
, 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
);
503 NS_DispatchToMainThread(NewRunnableMethod
<gfx::SourceSurface
*>(
504 "DrawEventRecorderPrivate::RecordSourceSurfaceDestruction", this,
505 &DrawEventRecorderPrivate::RecordSourceSurfaceDestruction
, aSurface
));
508 } // namespace layers
509 } // namespace mozilla