Source code

Revision control

Other Tools

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/. */
6
7
#include "CanvasDrawEventRecorder.h"
8
9
#include <string.h>
10
11
namespace mozilla {
12
namespace layers {
13
14
static const int32_t kCheckpointEventType = -1;
15
static const uint32_t kMaxSpinCount = 200;
16
17
static const TimeDuration kTimeout = TimeDuration::FromMilliseconds(100);
18
static const int32_t kTimeoutRetryCount = 50;
19
20
static const uint32_t kCacheLineSize = 64;
21
static const uint32_t kStreamSize = 64 * 1024;
22
static const uint32_t kShmemSize = kStreamSize + (2 * kCacheLineSize);
23
24
static_assert((static_cast<uint64_t>(UINT32_MAX) + 1) % kStreamSize == 0,
25
"kStreamSize must be a power of two.");
26
27
bool CanvasEventRingBuffer::InitWriter(
28
base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aReadHandle,
29
CrossProcessSemaphoreHandle* aReaderSem,
30
CrossProcessSemaphoreHandle* aWriterSem,
31
UniquePtr<WriterServices> aWriterServices) {
32
mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
33
if (NS_WARN_IF(!mSharedMemory->Create(kShmemSize)) ||
34
NS_WARN_IF(!mSharedMemory->Map(kShmemSize))) {
35
return false;
36
}
37
38
if (NS_WARN_IF(!mSharedMemory->ShareToProcess(aOtherPid, aReadHandle))) {
39
return false;
40
}
41
42
mSharedMemory->CloseHandle();
43
44
mBuf = static_cast<char*>(mSharedMemory->memory());
45
mBufPos = mBuf;
46
mAvailable = kStreamSize;
47
48
static_assert(sizeof(ReadFooter) <= kCacheLineSize,
49
"ReadFooter must fit in kCacheLineSize.");
50
mRead = reinterpret_cast<ReadFooter*>(mBuf + kStreamSize);
51
mRead->count = 0;
52
mRead->returnCount = 0;
53
mRead->state = State::Processing;
54
55
static_assert(sizeof(WriteFooter) <= kCacheLineSize,
56
"WriteFooter must fit in kCacheLineSize.");
57
mWrite = reinterpret_cast<WriteFooter*>(mBuf + kStreamSize + kCacheLineSize);
58
mWrite->count = 0;
59
mWrite->returnCount = 0;
60
mWrite->requiredDifference = 0;
61
mWrite->state = State::Processing;
62
63
mReaderSemaphore.reset(
64
CrossProcessSemaphore::Create("SharedMemoryStreamParent", 0));
65
*aReaderSem = mReaderSemaphore->ShareToProcess(aOtherPid);
66
mReaderSemaphore->CloseHandle();
67
mWriterSemaphore.reset(
68
CrossProcessSemaphore::Create("SharedMemoryStreamChild", 0));
69
*aWriterSem = mWriterSemaphore->ShareToProcess(aOtherPid);
70
mWriterSemaphore->CloseHandle();
71
72
mWriterServices = std::move(aWriterServices);
73
74
mGood = true;
75
return true;
76
}
77
78
bool CanvasEventRingBuffer::InitReader(
79
const ipc::SharedMemoryBasic::Handle& aReadHandle,
80
const CrossProcessSemaphoreHandle& aReaderSem,
81
const CrossProcessSemaphoreHandle& aWriterSem,
82
UniquePtr<ReaderServices> aReaderServices) {
83
mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
84
if (NS_WARN_IF(!mSharedMemory->SetHandle(
85
aReadHandle, ipc::SharedMemory::RightsReadWrite)) ||
86
NS_WARN_IF(!mSharedMemory->Map(kShmemSize))) {
87
return false;
88
}
89
90
mSharedMemory->CloseHandle();
91
92
mBuf = static_cast<char*>(mSharedMemory->memory());
93
mRead = reinterpret_cast<ReadFooter*>(mBuf + kStreamSize);
94
mWrite = reinterpret_cast<WriteFooter*>(mBuf + kStreamSize + kCacheLineSize);
95
mReaderSemaphore.reset(CrossProcessSemaphore::Create(aReaderSem));
96
mReaderSemaphore->CloseHandle();
97
mWriterSemaphore.reset(CrossProcessSemaphore::Create(aWriterSem));
98
mWriterSemaphore->CloseHandle();
99
100
mReaderServices = std::move(aReaderServices);
101
102
mGood = true;
103
return true;
104
}
105
106
bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableSpace() {
107
if (!good()) {
108
return false;
109
}
110
111
uint32_t bufPos = mOurCount % kStreamSize;
112
uint32_t maxToWrite = kStreamSize - bufPos;
113
mAvailable = std::min(maxToWrite, WaitForBytesToWrite());
114
if (!mAvailable) {
115
mGood = false;
116
mBufPos = nullptr;
117
return false;
118
}
119
120
mBufPos = mBuf + bufPos;
121
return true;
122
}
123
124
void CanvasEventRingBuffer::write(const char* const aData, const size_t aSize) {
125
const char* curDestPtr = aData;
126
size_t remainingToWrite = aSize;
127
if (remainingToWrite > mAvailable) {
128
if (!WaitForAndRecalculateAvailableSpace()) {
129
return;
130
}
131
}
132
133
if (remainingToWrite <= mAvailable) {
134
memcpy(mBufPos, curDestPtr, remainingToWrite);
135
UpdateWriteTotalsBy(remainingToWrite);
136
return;
137
}
138
139
do {
140
memcpy(mBufPos, curDestPtr, mAvailable);
141
IncrementWriteCountBy(mAvailable);
142
curDestPtr += mAvailable;
143
remainingToWrite -= mAvailable;
144
if (!WaitForAndRecalculateAvailableSpace()) {
145
return;
146
}
147
} while (remainingToWrite > mAvailable);
148
149
memcpy(mBufPos, curDestPtr, remainingToWrite);
150
UpdateWriteTotalsBy(remainingToWrite);
151
}
152
153
void CanvasEventRingBuffer::IncrementWriteCountBy(uint32_t aCount) {
154
mOurCount += aCount;
155
mWrite->count = mOurCount;
156
if (mRead->state != State::Processing) {
157
CheckAndSignalReader();
158
}
159
}
160
161
void CanvasEventRingBuffer::UpdateWriteTotalsBy(uint32_t aCount) {
162
IncrementWriteCountBy(aCount);
163
mBufPos += aCount;
164
mAvailable -= aCount;
165
}
166
167
bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableData() {
168
if (!good()) {
169
return false;
170
}
171
172
uint32_t bufPos = mOurCount % kStreamSize;
173
uint32_t maxToRead = kStreamSize - bufPos;
174
mAvailable = std::min(maxToRead, WaitForBytesToRead());
175
if (!mAvailable) {
176
mGood = false;
177
mBufPos = nullptr;
178
return false;
179
}
180
181
mBufPos = mBuf + bufPos;
182
return true;
183
}
184
185
void CanvasEventRingBuffer::read(char* const aOut, const size_t aSize) {
186
char* curSrcPtr = aOut;
187
size_t remainingToRead = aSize;
188
if (remainingToRead > mAvailable) {
189
if (!WaitForAndRecalculateAvailableData()) {
190
return;
191
}
192
}
193
194
if (remainingToRead <= mAvailable) {
195
memcpy(curSrcPtr, mBufPos, remainingToRead);
196
UpdateReadTotalsBy(remainingToRead);
197
return;
198
}
199
200
do {
201
memcpy(curSrcPtr, mBufPos, mAvailable);
202
IncrementReadCountBy(mAvailable);
203
curSrcPtr += mAvailable;
204
remainingToRead -= mAvailable;
205
if (!WaitForAndRecalculateAvailableData()) {
206
return;
207
}
208
} while (remainingToRead > mAvailable);
209
210
memcpy(curSrcPtr, mBufPos, remainingToRead);
211
UpdateReadTotalsBy(remainingToRead);
212
}
213
214
void CanvasEventRingBuffer::IncrementReadCountBy(uint32_t aCount) {
215
mOurCount += aCount;
216
mRead->count = mOurCount;
217
if (mWrite->state != State::Processing) {
218
CheckAndSignalWriter();
219
}
220
}
221
222
void CanvasEventRingBuffer::UpdateReadTotalsBy(uint32_t aCount) {
223
IncrementReadCountBy(aCount);
224
mBufPos += aCount;
225
mAvailable -= aCount;
226
}
227
228
void CanvasEventRingBuffer::CheckAndSignalReader() {
229
do {
230
switch (mRead->state) {
231
case State::Processing:
232
return;
233
case State::AboutToWait:
234
// The reader is making a decision about whether to wait. So, we must
235
// wait until it has decided to avoid races. Check if the reader is
236
// closed to avoid hangs.
237
if (mWriterServices->ReaderClosed()) {
238
return;
239
}
240
continue;
241
case State::Waiting:
242
if (mRead->count != mOurCount) {
243
// We have to use compareExchange here because the reader can change
244
// from Waiting to Stopped.
245
if (mRead->state.compareExchange(State::Waiting, State::Processing)) {
246
mReaderSemaphore->Signal();
247
return;
248
}
249
250
MOZ_ASSERT(mRead->state == State::Stopped);
251
continue;
252
}
253
return;
254
case State::Stopped:
255
if (mRead->count != mOurCount) {
256
mRead->state = State::Processing;
257
mWriterServices->ResumeReader();
258
}
259
return;
260
default:
261
MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
262
return;
263
}
264
} while (true);
265
}
266
267
bool CanvasEventRingBuffer::HasDataToRead() {
268
return (mWrite->count != mOurCount);
269
}
270
271
bool CanvasEventRingBuffer::StopIfEmpty() {
272
// Double-check that the writer isn't waiting.
273
CheckAndSignalWriter();
274
mRead->state = State::AboutToWait;
275
if (HasDataToRead()) {
276
mRead->state = State::Processing;
277
return false;
278
}
279
280
mRead->state = State::Stopped;
281
return true;
282
}
283
284
bool CanvasEventRingBuffer::WaitForDataToRead(TimeDuration aTimeout,
285
int32_t aRetryCount) {
286
uint32_t spinCount = kMaxSpinCount;
287
do {
288
if (HasDataToRead()) {
289
return true;
290
}
291
} while (--spinCount != 0);
292
293
// Double-check that the writer isn't waiting.
294
CheckAndSignalWriter();
295
mRead->state = State::AboutToWait;
296
if (HasDataToRead()) {
297
mRead->state = State::Processing;
298
return true;
299
}
300
301
mRead->state = State::Waiting;
302
do {
303
if (mReaderSemaphore->Wait(Some(aTimeout))) {
304
MOZ_RELEASE_ASSERT(HasDataToRead());
305
return true;
306
}
307
308
if (mReaderServices->WriterClosed()) {
309
// Something has gone wrong on the writing side, just return false so
310
// that we can hopefully recover.
311
return false;
312
}
313
} while (aRetryCount-- > 0);
314
315
// We have to use compareExchange here because the writer can change our
316
// state if we are waiting. signaled
317
if (!mRead->state.compareExchange(State::Waiting, State::Stopped)) {
318
MOZ_RELEASE_ASSERT(HasDataToRead());
319
MOZ_RELEASE_ASSERT(mRead->state == State::Processing);
320
// The writer has just signaled us, so consume it before returning
321
MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait());
322
return true;
323
}
324
325
return false;
326
}
327
328
int32_t CanvasEventRingBuffer::ReadNextEvent() {
329
int32_t nextEvent;
330
ReadElement(*this, nextEvent);
331
while (nextEvent == kCheckpointEventType) {
332
ReadElement(*this, nextEvent);
333
}
334
335
return nextEvent;
336
}
337
338
uint32_t CanvasEventRingBuffer::CreateCheckpoint() {
339
WriteElement(*this, kCheckpointEventType);
340
return mOurCount;
341
}
342
343
bool CanvasEventRingBuffer::WaitForCheckpoint(uint32_t aCheckpoint) {
344
return WaitForReadCount(aCheckpoint, kTimeout);
345
}
346
347
void CanvasEventRingBuffer::CheckAndSignalWriter() {
348
do {
349
switch (mWrite->state) {
350
case State::Processing:
351
return;
352
case State::AboutToWait:
353
// The writer is making a decision about whether to wait. So, we must
354
// wait until it has decided to avoid races. Check if the writer is
355
// closed to avoid hangs.
356
if (mReaderServices->WriterClosed()) {
357
return;
358
}
359
continue;
360
case State::Waiting:
361
if (mWrite->count - mOurCount <= mWrite->requiredDifference) {
362
mWrite->state = State::Processing;
363
mWriterSemaphore->Signal();
364
}
365
return;
366
default:
367
MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
368
return;
369
}
370
} while (true);
371
}
372
373
bool CanvasEventRingBuffer::WaitForReadCount(uint32_t aReadCount,
374
TimeDuration aTimeout) {
375
uint32_t requiredDifference = mOurCount - aReadCount;
376
uint32_t spinCount = kMaxSpinCount;
377
do {
378
if (mOurCount - mRead->count <= requiredDifference) {
379
return true;
380
}
381
} while (--spinCount != 0);
382
383
// Double-check that the reader isn't waiting.
384
CheckAndSignalReader();
385
mWrite->state = State::AboutToWait;
386
if (mOurCount - mRead->count <= requiredDifference) {
387
mWrite->state = State::Processing;
388
return true;
389
}
390
391
mWrite->requiredDifference = requiredDifference;
392
mWrite->state = State::Waiting;
393
394
// Wait unless we detect the reading side has closed.
395
while (!mWriterServices->ReaderClosed()) {
396
if (mWriterSemaphore->Wait(Some(aTimeout))) {
397
MOZ_ASSERT(mOurCount - mRead->count <= requiredDifference);
398
return true;
399
}
400
}
401
402
return false;
403
}
404
405
uint32_t CanvasEventRingBuffer::WaitForBytesToWrite() {
406
uint32_t streamFullReadCount = mOurCount - kStreamSize;
407
if (!WaitForReadCount(streamFullReadCount + 1, kTimeout)) {
408
mGood = false;
409
return 0;
410
}
411
412
return mRead->count - streamFullReadCount;
413
}
414
415
uint32_t CanvasEventRingBuffer::WaitForBytesToRead() {
416
if (!WaitForDataToRead(kTimeout, kTimeoutRetryCount)) {
417
return 0;
418
}
419
420
return mWrite->count - mOurCount;
421
}
422
423
void CanvasEventRingBuffer::ReturnWrite(const char* aData, size_t aSize) {
424
uint32_t writeCount = mRead->returnCount;
425
uint32_t bufPos = writeCount % kStreamSize;
426
uint32_t bufRemaining = kStreamSize - bufPos;
427
uint32_t availableToWrite =
428
std::min(bufRemaining, (mWrite->returnCount + kStreamSize - writeCount));
429
while (availableToWrite < aSize) {
430
if (availableToWrite) {
431
memcpy(mBuf + bufPos, aData, availableToWrite);
432
writeCount += availableToWrite;
433
mRead->returnCount = writeCount;
434
bufPos = writeCount % kStreamSize;
435
bufRemaining = kStreamSize - bufPos;
436
aData += availableToWrite;
437
aSize -= availableToWrite;
438
}
439
440
availableToWrite = std::min(
441
bufRemaining, (mWrite->returnCount + kStreamSize - writeCount));
442
}
443
444
memcpy(mBuf + bufPos, aData, aSize);
445
writeCount += aSize;
446
mRead->returnCount = writeCount;
447
}
448
449
void CanvasEventRingBuffer::ReturnRead(char* aOut, size_t aSize) {
450
// First wait for the event returning the data to be read.
451
WaitForCheckpoint(mOurCount);
452
uint32_t readCount = mWrite->returnCount;
453
454
// If the event sending back data fails to play then it will ReturnWrite
455
// nothing. So, wait until something has been written or the reader has
456
// stopped processing.
457
while (readCount == mRead->returnCount) {
458
if (mRead->state != State::Processing) {
459
return;
460
}
461
}
462
463
uint32_t bufPos = readCount % kStreamSize;
464
uint32_t bufRemaining = kStreamSize - bufPos;
465
uint32_t availableToRead =
466
std::min(bufRemaining, (mRead->returnCount - readCount));
467
while (availableToRead < aSize) {
468
if (availableToRead) {
469
memcpy(aOut, mBuf + bufPos, availableToRead);
470
readCount += availableToRead;
471
mWrite->returnCount = readCount;
472
bufPos = readCount % kStreamSize;
473
bufRemaining = kStreamSize - bufPos;
474
aOut += availableToRead;
475
aSize -= availableToRead;
476
}
477
478
availableToRead = std::min(bufRemaining, (mRead->returnCount - readCount));
479
}
480
481
memcpy(aOut, mBuf + bufPos, aSize);
482
readCount += aSize;
483
mWrite->returnCount = readCount;
484
}
485
486
} // namespace layers
487
} // namespace mozilla