summaryrefslogtreecommitdiffstats
path: root/gfx/layers/CanvasDrawEventRecorder.h
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/CanvasDrawEventRecorder.h')
-rw-r--r--gfx/layers/CanvasDrawEventRecorder.h287
1 files changed, 287 insertions, 0 deletions
diff --git a/gfx/layers/CanvasDrawEventRecorder.h b/gfx/layers/CanvasDrawEventRecorder.h
new file mode 100644
index 0000000000..d67ce4fdd4
--- /dev/null
+++ b/gfx/layers/CanvasDrawEventRecorder.h
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_CanvasDrawEventRecorder_h
+#define mozilla_layers_CanvasDrawEventRecorder_h
+
+#include "mozilla/gfx/DrawEventRecorder.h"
+#include "mozilla/ipc/CrossProcessSemaphore.h"
+#include "mozilla/ipc/SharedMemoryBasic.h"
+
+namespace mozilla {
+namespace layers {
+
+class CanvasEventRingBuffer final : public gfx::EventRingBuffer {
+ public:
+ /**
+ * WriterServices allows consumers of CanvasEventRingBuffer to provide
+ * functions required by the write side of a CanvasEventRingBuffer without
+ * introducing unnecessary dependencies on IPC code.
+ */
+ class WriterServices {
+ public:
+ virtual ~WriterServices() = default;
+
+ /**
+ * @returns true if the reader of the CanvasEventRingBuffer has permanently
+ * stopped processing, otherwise returns false.
+ */
+ virtual bool ReaderClosed() = 0;
+
+ /**
+ * Causes the reader to resume processing when it is in a stopped state.
+ */
+ virtual void ResumeReader() = 0;
+ };
+
+ /**
+ * ReaderServices allows consumers of CanvasEventRingBuffer to provide
+ * functions required by the read side of a CanvasEventRingBuffer without
+ * introducing unnecessary dependencies on IPC code.
+ */
+ class ReaderServices {
+ public:
+ virtual ~ReaderServices() = default;
+
+ /**
+ * @returns true if the writer of the CanvasEventRingBuffer has permanently
+ * stopped processing, otherwise returns false.
+ */
+ virtual bool WriterClosed() = 0;
+ };
+
+ CanvasEventRingBuffer() {}
+
+ /**
+ * Initialize the write side of a CanvasEventRingBuffer returning handles to
+ * the shared memory for the buffer and the two semaphores for waiting in the
+ * reader and the writer.
+ *
+ * @param aOtherPid process ID to share the handles to
+ * @param aReadHandle handle to the shared memory for the buffer
+ * @param aReaderSem reading blocked semaphore
+ * @param aWriterSem writing blocked semaphore
+ * @param aWriterServices provides functions required by the writer
+ * @returns true if initialization succeeds
+ */
+ bool InitWriter(base::ProcessId aOtherPid,
+ ipc::SharedMemoryBasic::Handle* aReadHandle,
+ CrossProcessSemaphoreHandle* aReaderSem,
+ CrossProcessSemaphoreHandle* aWriterSem,
+ UniquePtr<WriterServices> aWriterServices);
+
+ /**
+ * Initialize the read side of a CanvasEventRingBuffer.
+ *
+ * @param aReadHandle handle to the shared memory for the buffer
+ * @param aReaderSem reading blocked semaphore
+ * @param aWriterSem writing blocked semaphore
+ * @param aReaderServices provides functions required by the reader
+ * @returns true if initialization succeeds
+ */
+ bool InitReader(ipc::SharedMemoryBasic::Handle aReadHandle,
+ CrossProcessSemaphoreHandle aReaderSem,
+ CrossProcessSemaphoreHandle aWriterSem,
+ UniquePtr<ReaderServices> aReaderServices);
+
+ bool good() const final { return mGood; }
+
+ bool WriterFailed() const { return mWrite->state == State::Failed; }
+
+ void SetIsBad() final {
+ mGood = false;
+ mRead->state = State::Failed;
+ }
+
+ void write(const char* const aData, const size_t aSize) final;
+
+ bool HasDataToRead();
+
+ /*
+ * This will put the reader into a stopped state if there is no more data to
+ * read. If this returns false the caller is responsible for continuing
+ * translation at a later point. If it returns false the writer will start the
+ * translation again when more data is written.
+ *
+ * @returns true if stopped
+ */
+ bool StopIfEmpty();
+
+ /*
+ * Waits for data to become available. This will wait for aTimeout duration
+ * aRetryCount number of times, checking to see if the other side is closed in
+ * between each one.
+ *
+ * @param aTimeout duration to wait
+ * @param aRetryCount number of times to retry
+ * @returns true if data is available to read.
+ */
+ bool WaitForDataToRead(TimeDuration aTimeout, int32_t aRetryCount);
+
+ int32_t ReadNextEvent();
+
+ void read(char* const aOut, const size_t aSize) final;
+
+ /**
+ * Writes a checkpoint event to the buffer.
+ *
+ * @returns the write count after the checkpoint has been written
+ */
+ uint32_t CreateCheckpoint();
+
+ /**
+ * Waits until the given checkpoint has been read from the buffer.
+ *
+ * @params aCheckpoint the checkpoint to wait for
+ * @params aTimeout duration to wait while reader is not active
+ * @returns true if the checkpoint was reached, false if the reader is closed
+ * or we timeout.
+ */
+ bool WaitForCheckpoint(uint32_t aCheckpoint);
+
+ /**
+ * Used to send data back to the writer. This is done through the same shared
+ * memory so the writer must wait and read the response after it has submitted
+ * the event that uses this.
+ *
+ * @param aData the data to be written back to the writer
+ * @param aSize the number of chars to write
+ */
+ void ReturnWrite(const char* aData, size_t aSize);
+
+ /**
+ * Used to read data sent back from the reader via ReturnWrite. This is done
+ * through the same shared memory so the writer must wait until all expected
+ * data is read before writing new events to the buffer.
+ *
+ * @param aOut the pointer to read into
+ * @param aSize the number of chars to read
+ */
+ void ReturnRead(char* aOut, size_t aSize);
+
+ protected:
+ bool WaitForAndRecalculateAvailableSpace() final;
+ void UpdateWriteTotalsBy(uint32_t aCount) final;
+
+ private:
+ enum class State : uint32_t {
+ Processing,
+
+ /**
+ * This is the important state to make sure the other side signals or starts
+ * us as soon as data or space is available. We set AboutToWait first and
+ * then re-check the condition. If we went straight to Waiting or Stopped
+ * then in between the last check and setting the state, the other side
+ * could have used all available data or space and never have signaled us
+ * because it didn't know we were about to wait, causing a deadlock.
+ * While we are in this state, the other side must wait until we resolve the
+ * AboutToWait state to one of the other states and then signal or start us
+ * if it needs to.
+ */
+ AboutToWait,
+ Waiting,
+ Stopped,
+ Failed,
+ };
+
+ struct ReadFooter {
+ Atomic<uint32_t> count;
+ Atomic<uint32_t> returnCount;
+ Atomic<State> state;
+ };
+
+ struct WriteFooter {
+ Atomic<uint32_t> count;
+ Atomic<uint32_t> returnCount;
+ Atomic<uint32_t> requiredDifference;
+ Atomic<State> state;
+ };
+
+ CanvasEventRingBuffer(const CanvasEventRingBuffer&) = delete;
+ void operator=(const CanvasEventRingBuffer&) = delete;
+
+ void IncrementWriteCountBy(uint32_t aCount);
+
+ bool WaitForReadCount(uint32_t aReadCount, TimeDuration aTimeout);
+
+ bool WaitForAndRecalculateAvailableData();
+
+ void UpdateReadTotalsBy(uint32_t aCount);
+ void IncrementReadCountBy(uint32_t aCount);
+
+ void CheckAndSignalReader();
+
+ void CheckAndSignalWriter();
+
+ uint32_t WaitForBytesToWrite();
+
+ uint32_t WaitForBytesToRead();
+
+ RefPtr<ipc::SharedMemoryBasic> mSharedMemory;
+ UniquePtr<CrossProcessSemaphore> mReaderSemaphore;
+ UniquePtr<CrossProcessSemaphore> mWriterSemaphore;
+ UniquePtr<WriterServices> mWriterServices;
+ UniquePtr<ReaderServices> mReaderServices;
+ char* mBuf = nullptr;
+ uint32_t mOurCount = 0;
+ WriteFooter* mWrite = nullptr;
+ ReadFooter* mRead = nullptr;
+ bool mGood = false;
+};
+
+class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate {
+ public:
+ MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CanvasDrawEventRecorder, final)
+ explicit CanvasDrawEventRecorder(){};
+
+ bool Init(base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aHandle,
+ CrossProcessSemaphoreHandle* aReaderSem,
+ CrossProcessSemaphoreHandle* aWriterSem,
+ UniquePtr<CanvasEventRingBuffer::WriterServices> aWriterServices) {
+ return mOutputStream.InitWriter(aOtherPid, aHandle, aReaderSem, aWriterSem,
+ std::move(aWriterServices));
+ }
+
+ void RecordEvent(const gfx::RecordedEvent& aEvent) final {
+ if (!mOutputStream.good()) {
+ return;
+ }
+
+ aEvent.RecordToStream(mOutputStream);
+ }
+
+ void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface,
+ const char* aReason) final;
+
+ void RecordSourceSurfaceDestruction(void* aSurface) final;
+
+ void Flush() final {}
+
+ void ReturnRead(char* aOut, size_t aSize) {
+ mOutputStream.ReturnRead(aOut, aSize);
+ }
+
+ uint32_t CreateCheckpoint() { return mOutputStream.CreateCheckpoint(); }
+
+ /**
+ * Waits until the given checkpoint has been read by the translator.
+ *
+ * @params aCheckpoint the checkpoint to wait for
+ * @returns true if the checkpoint was reached, false if the reader is closed
+ * or we timeout.
+ */
+ bool WaitForCheckpoint(uint32_t aCheckpoint) {
+ return mOutputStream.WaitForCheckpoint(aCheckpoint);
+ }
+
+ private:
+ CanvasEventRingBuffer mOutputStream;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CanvasDrawEventRecorder_h