diff --git a/dom/canvas/ClientWebGLContext.cpp b/dom/canvas/ClientWebGLContext.cpp index 1282194b6dd0..d61830519392 100644 --- a/dom/canvas/ClientWebGLContext.cpp +++ b/dom/canvas/ClientWebGLContext.cpp @@ -461,15 +461,7 @@ void ClientWebGLContext::ClearVRFrame() const { Run(); } RefPtr ClientWebGLContext::GetVRFrame( const WebGLFramebufferJS* fb) const { - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return nullptr; - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - return inProcessContext->GetVRFrame(fb ? fb->mId : 0); - } - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetVRFrame"); - return nullptr; + return Run(fb ? fb->mId : 0); } already_AddRefed ClientWebGLContext::GetCanvasLayer( @@ -732,55 +724,32 @@ bool ClientWebGLContext::CreateHostContext(const uvec2& requestedSize) { return Err("!CompositorBridgeChild::Get()"); } - outOfProcess.mWebGLChild = new WebGLChild(*this); - outOfProcess.mWebGLChild = static_cast( - cbc->SendPWebGLConstructor(outOfProcess.mWebGLChild)); - if (!outOfProcess.mWebGLChild) { + // Construct the WebGL command queue, used to send commands from the client + // process to the host for execution. It takes a response queue that is + // used to return responses to synchronous messages. + // TODO: Be smarter in choosing these. + static constexpr size_t CommandQueueSize = 256 * 1024; // 256K + static constexpr size_t ResponseQueueSize = 8 * 1024; // 8K + auto commandPcq = ProducerConsumerQueue::Create(cbc, CommandQueueSize); + auto responsePcq = ProducerConsumerQueue::Create(cbc, ResponseQueueSize); + if (!commandPcq || !responsePcq) { + return Err("Failed to create command/response PCQ"); + } + + outOfProcess.mCommandSource = MakeUnique( + std::move(commandPcq->mProducer), std::move(responsePcq->mConsumer)); + auto sink = MakeUnique( + std::move(commandPcq->mConsumer), std::move(responsePcq->mProducer)); + + // Use the error/warning and command queues to construct a + // ClientWebGLContext in this process and a HostWebGLContext + // in the host process. + outOfProcess.mWebGLChild = new dom::WebGLChild(*this); + if (!cbc->SendPWebGLConstructor(outOfProcess.mWebGLChild.get(), initDesc, + ¬Lost.info)) { return Err("SendPWebGLConstructor failed"); } - UniquePtr sinkP; - UniquePtr sinkI; - switch (StaticPrefs::webgl_prototype_ipc_pcq()) { - case 0: { - using mozilla::webgl::ProducerConsumerQueue; - static constexpr size_t CommandQueueSize = 256 * 1024; // 256K - static constexpr size_t ResponseQueueSize = 8 * 1024; // 8K - auto command = ProducerConsumerQueue::Create(cbc, CommandQueueSize); - auto response = ProducerConsumerQueue::Create(cbc, ResponseQueueSize); - if (!command || !response) { - return Err("Failed to create command/response PCQ"); - } - - outOfProcess.mCommandSourcePcq = MakeUnique( - command->TakeProducer(), response->TakeConsumer()); - sinkP = MakeUnique(command->TakeConsumer(), - response->TakeProducer()); - break; - } - default: - using mozilla::IpdlWebGLCommandQueue; - using mozilla::IpdlWebGLResponseQueue; - auto command = - IpdlWebGLCommandQueue::Create(outOfProcess.mWebGLChild.get()); - auto response = - IpdlWebGLResponseQueue::Create(outOfProcess.mWebGLChild.get()); - if (!command || !response) { - return Err("Failed to create command/response IpdlQueue"); - } - - outOfProcess.mCommandSourceIpdl = MakeUnique( - command->TakeProducer(), response->TakeConsumer()); - sinkI = MakeUnique(command->TakeConsumer(), - response->TakeProducer()); - break; - } - - if (!outOfProcess.mWebGLChild->SendInitialize( - initDesc, std::move(sinkP), std::move(sinkI), ¬Lost.info)) { - return Err("WebGL actor Initialize failed"); - } - notLost.outOfProcess = Some(std::move(outOfProcess)); return Ok(); }(); @@ -972,16 +941,8 @@ already_AddRefed ClientWebGLContext::GetSurfaceSnapshot( const auto range = Range(map.GetData(), stride * size.y); auto view = RawBufferView(range); + Run(desc, view); - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return nullptr; - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - inProcessContext->ReadPixels(desc, view); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetSurfaceSnapshot"); - } // - const auto swapRowRedBlue = [&](uint8_t* const row) { @@ -1623,18 +1584,8 @@ void ClientWebGLContext::GetInternalformatParameter( JSContext* cx, GLenum target, GLenum internalformat, GLenum pname, JS::MutableHandle retval, ErrorResult& rv) { retval.set(JS::NullValue()); - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return; - const auto& inProcessContext = notLost->inProcess; - Maybe> maybe; - if (inProcessContext) { - maybe = inProcessContext->GetInternalformatParameter(target, internalformat, - pname); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetInternalformatParameter"); - } - + auto maybe = + Run(target, internalformat, pname); if (!maybe) { return; } @@ -2835,15 +2786,7 @@ void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset, } auto view = RawBuffer(byteLen, bytes); - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return; - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - inProcessContext->GetBufferSubData(target, srcByteOffset, view); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetBufferSubData"); - } + Run(target, srcByteOffset, view); } //// @@ -3591,18 +3534,10 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget, } } - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return; - const auto& inProcessContext = notLost->inProcess; - Maybe> maybe; - if (inProcessContext) { - inProcessContext->TexImage(imageTarget, static_cast(level), - respecFormat, CastUvec3(offset), CastUvec3(size), - pi, src, *GetCanvas()); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetInternalformatParameter"); - } + // TODO: Convert TexImageSource into something IPC-capable. + Run(imageTarget, static_cast(level), respecFormat, + CastUvec3(offset), CastUvec3(size), pi, src, + *GetCanvas()); } void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims, @@ -4088,16 +4023,7 @@ void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width, state.mPixelPackState}; const auto range = Range(bytes, byteLen); auto view = RawBufferView(range); - - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return; - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - inProcessContext->ReadPixels(desc, view); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote ReadPixels"); - } + Run(desc, view); } bool ClientWebGLContext::ReadPixels_SharedPrecheck( @@ -5385,15 +5311,7 @@ const webgl::CompileResult& ClientWebGLContext::GetCompileResult( const webgl::LinkResult& ClientWebGLContext::GetLinkResult( const WebGLProgramJS& prog) const { if (prog.mResult->pending) { - const auto notLost = - mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF. - if (!notLost) return *(prog.mResult); - const auto& inProcessContext = notLost->inProcess; - if (inProcessContext) { - *(prog.mResult) = inProcessContext->GetLinkResult(prog.mId); - } else { - MOZ_ASSERT_UNREACHABLE("TODO: Remote GetLinkResult"); - } + *(prog.mResult) = Run(prog.mId); prog.mUniformBlockBindings.resize( prog.mResult->active.activeUniformBlocks.size()); diff --git a/dom/canvas/ClientWebGLContext.h b/dom/canvas/ClientWebGLContext.h index 035ce1c27001..6ee779d1129f 100644 --- a/dom/canvas/ClientWebGLContext.h +++ b/dom/canvas/ClientWebGLContext.h @@ -20,7 +20,6 @@ #include "mozilla/Logging.h" #include "WebGLCrossProcessCommandQueue.h" -#include "WebGLCommandQueue.h" #include #include @@ -198,8 +197,7 @@ struct RemotingData final { // // where 'A -> B' means "A owns B" RefPtr mWebGLChild; - UniquePtr mCommandSourcePcq; - UniquePtr mCommandSourceIpdl; + UniquePtr mCommandSource; }; struct NotLostData final { @@ -2083,9 +2081,9 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, template void DispatchAsync(Args&&... aArgs) const { const auto& oop = *mNotLost->outOfProcess; - QueueStatus status = oop.mCommandSource->RunAsyncCommand(command, - aArgs...); - if (!IsSuccess(status)) { if (status == QueueStatus::kOOMError) { + PcqStatus status = oop.mCommandSource->RunAsyncCommand(command, aArgs...); + if (!IsSuccess(status)) { + if (status == PcqStatus::PcqOOMError) { JsWarning("Ran out-of-memory during WebGL IPC."); } // Not much to do but shut down. Since this was a Pcq failure and @@ -2100,11 +2098,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, ReturnType DispatchSync(Args&&... aArgs) const { const auto& oop = *mNotLost->outOfProcess; ReturnType returnValue; - QueueStatus status = + PcqStatus status = oop.mCommandSource->RunSyncCommand(command, returnValue, aArgs...); if (!IsSuccess(status)) { - if (status == QueueStatus::kOOMError) { + if (status == PcqStatus::PcqOOMError) { JsWarning("Ran out-of-memory during WebGL IPC."); } // Not much to do but shut down. Since this was a Pcq failure and @@ -2122,7 +2120,7 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal, const auto status = oop.mCommandSource->RunVoidSyncCommand(command, aArgs...); if (!IsSuccess(status)) { - if (status == QueueStatus::kOOMError) { + if (status == PcqStatus::PcqOOMError) { JsWarning("Ran out-of-memory during WebGL IPC."); } // Not much to do but shut down. Since this was a Pcq failure and diff --git a/dom/canvas/HostWebGLContext.cpp b/dom/canvas/HostWebGLContext.cpp index bb4b0bc38904..0224d481090c 100644 --- a/dom/canvas/HostWebGLContext.cpp +++ b/dom/canvas/HostWebGLContext.cpp @@ -67,12 +67,7 @@ UniquePtr HostWebGLContext::Create( HostWebGLContext::HostWebGLContext(OwnerData&& ownerData) : mOwnerData(std::move(ownerData)) { if (mOwnerData.outOfProcess) { - if (mOwnerData.outOfProcess->mCommandSinkP) { - mOwnerData.outOfProcess->mCommandSinkP->mHostContext = this; - } - if (mOwnerData.outOfProcess->mCommandSinkI) { - mOwnerData.outOfProcess->mCommandSinkI->mHostContext = this; - } + mOwnerData.outOfProcess->mCommandSink->mHostContext = this; } { diff --git a/dom/canvas/HostWebGLContext.h b/dom/canvas/HostWebGLContext.h index dff1e8f43b74..b438887f4440 100644 --- a/dom/canvas/HostWebGLContext.h +++ b/dom/canvas/HostWebGLContext.h @@ -14,10 +14,6 @@ #include "WebGL2Context.h" #include "WebGLFramebuffer.h" #include "WebGLTypes.h" -#include "WebGLCommandQueue.h" -#include "WebGLCrossProcessCommandQueue.h" -#include "ProducerConsumerQueue.h" -#include "IpdlQueue.h" #include #include @@ -32,6 +28,8 @@ namespace mozilla { +class HostWebGLCommandSink; + extern LazyLogModule gWebGLBridgeLog; namespace dom { @@ -81,8 +79,7 @@ class HostWebGLContext final : public SupportsWeakPtr { struct RemotingData final { dom::WebGLParent& mParent; - UniquePtr mCommandSinkP; - UniquePtr mCommandSinkI; + UniquePtr mCommandSink; }; struct OwnerData final { Maybe inProcess; diff --git a/dom/canvas/IpdlQueue.h b/dom/canvas/IpdlQueue.h deleted file mode 100644 index b92bd8b5e350..000000000000 --- a/dom/canvas/IpdlQueue.h +++ /dev/null @@ -1,553 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: sw=2 ts=4 et : - */ -/* 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 IPDLQUEUE_H_ -#define IPDLQUEUE_H_ 1 - -#include -#include -#include -#include -#include "mozilla/dom/QueueParamTraits.h" -#include "mozilla/ipc/SharedMemoryBasic.h" -#include "mozilla/Assertions.h" -#include "mozilla/ipc/Shmem.h" -#include "mozilla/ipc/ProtocolUtils.h" -#include "mozilla/Logging.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/TypeTraits.h" -#include "nsString.h" -#include "mozilla/WeakPtr.h" - -namespace IPC { -template -struct ParamTraits; -} // namespace IPC - -namespace mozilla { -namespace dom { - -using mozilla::webgl::QueueStatus; - -extern LazyLogModule gIpdlQueueLog; -#define IPDLQUEUE_LOG_(lvl, ...) \ - MOZ_LOG(mozilla::dom::gIpdlQueueLog, lvl, (__VA_ARGS__)) -#define IPDLQUEUE_LOGD(...) IPDLQUEUE_LOG_(LogLevel::Debug, __VA_ARGS__) -#define IPDLQUEUE_LOGE(...) IPDLQUEUE_LOG_(LogLevel::Error, __VA_ARGS__) - -template -class IpdlQueue; -template -class SyncConsumerActor; - -constexpr uint64_t kIllegalQueueId = 0; -inline uint64_t NewIpdlQueueId() { - static std::atomic sNextIpdlQueueId = 1; - return sNextIpdlQueueId++; -} - -struct IpdlQueueBuffer { - uint64_t id = kIllegalQueueId; - nsTArray data; - - IpdlQueueBuffer() = default; - IpdlQueueBuffer(const IpdlQueueBuffer&) = delete; - IpdlQueueBuffer(IpdlQueueBuffer&&) = default; -}; - -using IpdlQueueBuffers = nsTArray; - -// Any object larger than this will be inserted into its own Shmem. -// TODO: Base this on something. -static constexpr size_t kMaxIpdlQueueArgSize = 256 * 1024; - -template -class AsyncProducerActor { - public: - virtual bool TransmitIpdlQueueData(bool aSendSync, IpdlQueueBuffer&& aData) { - MOZ_ASSERT(aSendSync == false); - if (mResponseBuffers) { - // We are in the middle of a sync transaction. Store the data so - // that we can return it with the response. - const uint64_t id = aData.id; - for (auto& elt : *mResponseBuffers) { - if (elt.id == id) { - elt.data.AppendElements(aData.data); - return true; - } - } - mResponseBuffers->AppendElement(std::move(aData)); - return true; - } - - // We are not inside of a transaction. Send normally. - Derived* self = static_cast(this); - return self->SendTransmitIpdlQueueData( - std::forward(aData)); - } - - template - bool ShouldSendSync(const Args&...) { - return false; - } - - protected: - friend SyncConsumerActor; - - void SetResponseBuffers(IpdlQueueBuffers* aResponse) { - MOZ_ASSERT(!mResponseBuffers); - mResponseBuffers = aResponse; - } - - void ClearResponseBuffers() { - MOZ_ASSERT(mResponseBuffers); - mResponseBuffers = nullptr; - } - - IpdlQueueBuffers* mResponseBuffers = nullptr; -}; - -template -class SyncProducerActor : public AsyncProducerActor { - public: - bool TransmitIpdlQueueData(bool aSendSync, IpdlQueueBuffer&& aData) override { - Derived* self = static_cast(this); - if (mResponseBuffers || !aSendSync) { - return AsyncProducerActor::TransmitIpdlQueueData( - aSendSync, std::forward(aData)); - } - - IpdlQueueBuffers responses; - if (!self->SendExchangeIpdlQueueData(std::forward(aData), - &responses)) { - return false; - } - - for (auto& buf : responses) { - if (!self->StoreIpdlQueueData(std::move(buf))) { - return false; - } - } - return true; - } - - protected: - using AsyncProducerActor::mResponseBuffers; -}; - -template -class AsyncConsumerActor { - public: - // Returns the ipdlQueue contents that were Recv'ed in a prior IPDL - // transmission. No new data is received via IPDL during this operation. - nsTArray TakeIpdlQueueData(uint64_t aId) { - auto it = mIpdlQueueBuffers.find(aId); - if (it != mIpdlQueueBuffers.end()) { - return std::move(it->second.data); - } - return nsTArray(); - } - - protected: - friend SyncProducerActor; - - // Store data received from the producer, to be read by local IpdlConsumers. - bool StoreIpdlQueueData(IpdlQueueBuffer&& aBuffer) { - auto it = mIpdlQueueBuffers.find(aBuffer.id); - if (it == mIpdlQueueBuffers.end()) { - return mIpdlQueueBuffers.insert({aBuffer.id, std::move(aBuffer)}).second; - } - return it->second.data.AppendElements(aBuffer.data, fallible); - } - - mozilla::ipc::IPCResult RecvTransmitIpdlQueueData(IpdlQueueBuffer&& aBuffer) { - if (StoreIpdlQueueData(std::forward(aBuffer))) { - return IPC_OK(); - } - return IPC_FAIL_NO_REASON(static_cast(this)); - } - - std::unordered_map mIpdlQueueBuffers; -}; - -template -class SyncConsumerActor : public AsyncConsumerActor { - protected: - using AsyncConsumerActor::StoreIpdlQueueData; - - mozilla::ipc::IPCResult RecvExchangeIpdlQueueData( - IpdlQueueBuffer&& aBuffer, IpdlQueueBuffers* aResponse) { - uint64_t id = aBuffer.id; - if (!StoreIpdlQueueData(std::forward(aBuffer))) { - return IPC_FAIL_NO_REASON(static_cast(this)); - } - - // Mark the actor as in a sync operation, then calls handler. - // During handler, if actor is used as producer (for ALL queues) - // then instead of immediately sending, it writes the data into - // aResponse. When handler is done, we unmark the actor. - // Note that we must buffer for _all_ queues associated with the - // actor as the intended response queue is indistinguishable from - // the rest from our vantage point. - Derived* actor = static_cast(this); - actor->SetResponseBuffers(aResponse); - auto clearResponseBuffer = - MakeScopeExit([&] { actor->ClearResponseBuffers(); }); - return actor->RunQueue(id) ? IPC_OK() : IPC_FAIL_NO_REASON(actor); - } -}; - -template -class IpdlProducer final : public SupportsWeakPtr> { - nsTArray mSerializedData; - WeakPtr<_Actor> mActor; - uint64_t mId; - - public: - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(IpdlProducer<_Actor>) - using Actor = _Actor; - using SelfType = IpdlProducer; - - // For IPDL: - IpdlProducer() : mId(kIllegalQueueId) {} - - /** - * Insert aArgs into the queue. If the operation does not succeed then - * the queue is unchanged. - */ - template - QueueStatus TryInsert(Args&&... aArgs) { - MOZ_ASSERT(mId != kIllegalQueueId); - if (!mActor) { - NS_WARNING("TryInsert with actor that was already freed."); - return QueueStatus::kFatalError; - } - - // Fill mSerializedData with the data to send. Clear it when done. - MOZ_ASSERT(mSerializedData.IsEmpty()); - auto self = *this; - auto clearData = MakeScopeExit([&] { self.mSerializedData.Clear(); }); - const bool toSendSync = mActor->ShouldSendSync(aArgs...); - QueueStatus status = SerializeAllArgs(std::forward(aArgs)...); - if (status != QueueStatus::kSuccess) { - return status; - } - return mActor->TransmitIpdlQueueData( - toSendSync, IpdlQueueBuffer{mId, std::move(mSerializedData)}) - ? QueueStatus::kSuccess - : QueueStatus::kFatalError; - } - - /** - * Same as TryInsert. IPDL send failures are considered fatal to the - * IpdlQueue. - */ - template - QueueStatus TryWaitInsert(const Maybe&, Args&&... aArgs) { - return TryInsert(std::forward(aArgs)...); - } - - protected: - template - friend class IpdlQueue; - friend struct mozilla::ipc::IPDLParamTraits; - - explicit IpdlProducer(uint64_t aId, Actor* aActor = nullptr) - : mActor(aActor), mId(aId) {} - - template - QueueStatus SerializeAllArgs(Args&&... aArgs) { - size_t read = 0; - size_t write = 0; - mozilla::webgl::ProducerView view(this, read, &write); - size_t bytesNeeded = MinSizeofArgs(view, &aArgs...); - if (!mSerializedData.SetLength(bytesNeeded, fallible)) { - return QueueStatus::kOOMError; - } - - return SerializeArgs(view, aArgs...); - } - - QueueStatus SerializeArgs(mozilla::webgl::ProducerView& aView) { - return QueueStatus::kSuccess; - } - - template - QueueStatus SerializeArgs(mozilla::webgl::ProducerView& aView, - const Arg& aArg, const Args&... aArgs) { - QueueStatus status = SerializeArg(aView, aArg); - if (!IsSuccess(status)) { - return status; - } - return SerializeArgs(aView, aArgs...); - } - - template - QueueStatus SerializeArg(mozilla::webgl::ProducerView& aView, - const Arg& aArg) { - return mozilla::webgl::QueueParamTraits< - typename std::remove_volatile::type>::Write(aView, aArg); - } - - public: - template - QueueStatus WriteObject(size_t aRead, size_t* aWrite, const Arg& arg, - size_t aArgSize) { - // TODO: Queue needs one extra byte for PCQ (fixme). - return mozilla::webgl::Marshaller::WriteObject( - mSerializedData.Elements(), mSerializedData.Length() + 1, aRead, aWrite, - arg, aArgSize); - } - - inline bool NeedsSharedMemory(size_t aRequested) { - return aRequested >= kMaxIpdlQueueArgSize; - } - - base::ProcessId OtherPid() { return mActor ? mActor->OtherPid() : 0; } - - protected: - size_t MinSizeofArgs(mozilla::webgl::ProducerView& aView) { - return 0; - } - - template - size_t MinSizeofArgs(mozilla::webgl::ProducerView& aView, - const Arg& aArg, const Args&... aArgs) { - return aView.MinSizeParam(aArg) + MinSizeofArgs(aView, aArgs...); - } - - template - size_t MinSizeofArgs(mozilla::webgl::ProducerView& aView) { - return aView.template MinSizeParam() + MinSizeofArgs(aView); - } -}; - -template -class IpdlConsumer final : public SupportsWeakPtr> { - public: - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(IpdlConsumer<_Actor>) - using Actor = _Actor; - using SelfType = IpdlConsumer; - - // For IPDL - IpdlConsumer() : mId(kIllegalQueueId) {} - - /** - * Attempts to copy and remove aArgs from the queue. If the operation does - * not succeed then the queue is unchanged. If the operation returns - * kQueueNotReady then the consumer does not yet have enough data to satisfy - * the request. In this case, the IPDL MessageQueue should be given the - * opportunity to run, at which point TryRemove can be attempted again. - */ - template - QueueStatus TryRemove(Args&... aArgs) { - MOZ_ASSERT(mId != kIllegalQueueId); - if (!mActor) { - NS_WARNING("TryRemove with actor that was already freed."); - return QueueStatus::kFatalError; - } - mBuf.AppendElements(mActor->TakeIpdlQueueData(mId)); - return DeserializeAllArgs(aArgs...); - } - - /** - * Equivalent to TryRemove. Duration is ignored as it would need to - * allow the IPDL queue to run to be useful. - */ - template - QueueStatus TryWaitRemove(const Maybe&, Args&... aArgs) { - return TryRemove(aArgs...); - } - - protected: - template - friend class IpdlQueue; - friend struct mozilla::ipc::IPDLParamTraits; - - explicit IpdlConsumer(uint64_t aId, Actor* aActor = nullptr) - : mActor(aActor), mId(aId) {} - - template - QueueStatus DeserializeAllArgs(Args&... aArgs) { - size_t read = 0; - size_t write = mBuf.Length(); - mozilla::webgl::ConsumerView view(this, &read, write); - - QueueStatus status = DeserializeArgs(view, &aArgs...); - if (IsSuccess(status) && (read > 0)) { - mBuf.RemoveElementsAt(0, read); - } - return status; - } - - QueueStatus DeserializeArgs(mozilla::webgl::ConsumerView& aView) { - return QueueStatus::kSuccess; - } - - template - QueueStatus DeserializeArgs(mozilla::webgl::ConsumerView& aView, - Arg* aArg, Args*... aArgs) { - QueueStatus status = DeserializeArg(aView, aArg); - if (!IsSuccess(status)) { - return status; - } - return DeserializeArgs(aView, aArgs...); - } - - template - QueueStatus DeserializeArg(mozilla::webgl::ConsumerView& aView, - Arg* aArg) { - return mozilla::webgl:: - QueueParamTraits::Type>::Read( - aView, const_cast::type*>(aArg)); - } - - public: - template - QueueStatus ReadObject(size_t* aRead, size_t aWrite, Arg* arg, - size_t aArgSize) { - // TODO: Queue needs one extra byte for PCQ (fixme). - return mozilla::webgl::Marshaller::ReadObject( - mBuf.Elements(), mBuf.Length() + 1, aRead, aWrite, arg, aArgSize); - } - - static inline bool NeedsSharedMemory(size_t aRequested) { - return aRequested >= kMaxIpdlQueueArgSize; - } - - base::ProcessId OtherPid() { return mActor ? mActor->OtherPid() : 0; } - - protected: - WeakPtr mActor; - uint64_t mId; - nsTArray mBuf; -}; - -/** - * An IpdlQueue is a queue that uses an actor of type ActorP to send data and - * its reciprocal (i.e. child to its parent or vice-versa) to receive data. - * ActorP must derive from one of: - * AsyncProducerActor, SyncProducerActor - * ActorC must derive from one of: - * AsyncConsumerActor, SyncConsumerActor - */ -template -class IpdlQueue final { - public: - using ActorP = _ActorP; - using ActorC = _ActorC; - using Producer = IpdlProducer; - using Consumer = IpdlConsumer; - - UniquePtr TakeProducer() { return std::move(mProducer); } - UniquePtr TakeConsumer() { return std::move(mConsumer); } - - /** - * Create an IpdlQueue where the given actor is a producer and its - * reciprocal is the consumer. - * The reciprocal actor type must be typedefed in ActorC as OtherSideActor. - * For example, WebGLChild::OtherSideActor is WebGLParent. - */ - static UniquePtr> Create(ActorP* aProducerActor) { - static_assert(std::is_same::value, - "ActorP's reciprocal must be ActorC"); - static_assert(std::is_same::value, - "ActorC's reciprocal must be ActorP"); - - auto id = NewIpdlQueueId(); - return WrapUnique(new IpdlQueue( - std::move(WrapUnique(new Producer(id, aProducerActor))), - std::move(WrapUnique(new Consumer(id))))); - } - - /** - * Create an IpdlQueue where the given actor is a consumer and its - * reciprocal is the producer. - * The reciprocal actor type must be typedefed in ActorC as OtherSideActor. - * For example, WebGLChild::OtherSideActor is WebGLParent. - */ - static UniquePtr> Create(ActorC* aConsumerActor) { - static_assert(std::is_same::value, - "ActorP's reciprocal must be ActorC"); - static_assert(std::is_same::value, - "ActorC's reciprocal must be ActorP"); - - auto id = NewIpdlQueueId(); - return WrapUnique(new IpdlQueue( - std::move(WrapUnique(new Producer(id))), - std::move(WrapUnique(new Consumer(id, aConsumerActor))))); - } - - private: - IpdlQueue(UniquePtr&& aProducer, UniquePtr&& aConsumer) - : mProducer(std::move(aProducer)), mConsumer(std::move(aConsumer)) {} - - UniquePtr mProducer; - UniquePtr mConsumer; -}; - -} // namespace dom - -namespace ipc { - -template -struct IPDLParamTraits> { - typedef mozilla::dom::IpdlProducer paramType; - - static void Write(IPC::Message* aMsg, IProtocol* aActor, - const paramType& aParam) { - MOZ_ASSERT(aParam.mActor == nullptr); - WriteIPDLParam(aMsg, aActor, aParam.mId); - } - - static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, - IProtocol* aActor, paramType* aResult) { - aResult->mActor = static_cast(aActor); - return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mId); - } -}; - -template -struct IPDLParamTraits> { - typedef mozilla::dom::IpdlConsumer paramType; - - static void Write(IPC::Message* aMsg, IProtocol* aActor, - const paramType& aParam) { - MOZ_ASSERT(aParam.mActor == nullptr); - WriteIPDLParam(aMsg, aActor, aParam.mId); - WriteIPDLParam(aMsg, aActor, aParam.mBuf); - } - - static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, - IProtocol* aActor, paramType* aResult) { - aResult->mActor = static_cast(aActor); - return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mId) && - ReadIPDLParam(aMsg, aIter, aActor, &aResult->mBuf); - } -}; - -template <> -struct IPDLParamTraits { - typedef mozilla::dom::IpdlQueueBuffer paramType; - - static void Write(IPC::Message* aMsg, IProtocol* aActor, - const paramType& aParam) { - WriteParam(aMsg, aParam.id); - WriteParam(aMsg, aParam.data); - } - - static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, - IProtocol* aActor, paramType* aResult) { - return ReadParam(aMsg, aIter, &aResult->id) && - ReadParam(aMsg, aIter, &aResult->data); - } -}; - -} // namespace ipc -} // namespace mozilla - -#endif // IPDLQUEUE_H_ diff --git a/dom/canvas/PWebGL.ipdl b/dom/canvas/PWebGL.ipdl index 8319aa8f2540..c2598581615a 100644 --- a/dom/canvas/PWebGL.ipdl +++ b/dom/canvas/PWebGL.ipdl @@ -11,12 +11,6 @@ include protocol PLayerTransaction; using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h"; using mozilla::webgl::ContextLossReason from "mozilla/dom/WebGLIpdl.h"; using std::string from "ipc/IPCMessageUtils.h"; -using mozilla::webgl::InitContextDesc from "mozilla/dom/WebGLIpdl.h"; -using mozilla::webgl::InitContextResult from "mozilla/dom/WebGLIpdl.h"; -using mozilla::HostWebGLCommandSinkP from "mozilla/dom/WebGLCrossProcessCommandQueue.h"; -using mozilla::HostWebGLCommandSinkI from "mozilla/dom/WebGLCrossProcessCommandQueue.h"; -using mozilla::dom::IpdlQueueBuffer from "mozilla/dom/IpdlQueue.h"; -using mozilla::dom::IpdlQueueBuffers from "mozilla/dom/IpdlQueue.h"; namespace mozilla { namespace dom { @@ -31,26 +25,17 @@ sync refcounted protocol PWebGL manager PCompositorBridge; parent: - sync Initialize(InitContextDesc desc, UniquePtr sinkP, - UniquePtr sinkI) - returns (InitContextResult res); - async __delete__(); // DLP: TODO: Does this need to be sync? sync UpdateCompositableHandle(PLayerTransaction aLayerTrans, CompositableHandle aHandle); - sync ExchangeIpdlQueueData(IpdlQueueBuffer aMsg) returns (IpdlQueueBuffers aResponse); - child: async JsWarning(string text); // Tell client that this queue needs to be shut down async OnContextLoss(ContextLossReason aReason); - -both: - async TransmitIpdlQueueData(IpdlQueueBuffer aData); }; } // dom diff --git a/dom/canvas/Queue.cpp b/dom/canvas/ProducerConsumerQueue.cpp similarity index 89% rename from dom/canvas/Queue.cpp rename to dom/canvas/ProducerConsumerQueue.cpp index 30474408f2ac..a840fbc87e82 100644 --- a/dom/canvas/Queue.cpp +++ b/dom/canvas/ProducerConsumerQueue.cpp @@ -9,7 +9,6 @@ namespace mozilla { namespace webgl { mozilla::LazyLogModule gPCQLog("pcq"); -mozilla::LazyLogModule gIpdlQueueLog("ipdlqueue"); } // namespace webgl } // namespace mozilla diff --git a/dom/canvas/ProducerConsumerQueue.h b/dom/canvas/ProducerConsumerQueue.h index ea936af320ea..1c285b5d8880 100644 --- a/dom/canvas/ProducerConsumerQueue.h +++ b/dom/canvas/ProducerConsumerQueue.h @@ -13,12 +13,57 @@ #include #include #include -#include "mozilla/dom/QueueParamTraits.h" +#include "mozilla/ipc/SharedMemoryBasic.h" +#include "mozilla/Assertions.h" +#include "mozilla/ipc/Shmem.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/TimeStamp.h" +#include "nsString.h" #include "CrossProcessSemaphore.h" namespace IPC { + template struct ParamTraits; + +typedef uint32_t PcqTypeInfoID; +template +struct PcqTypeInfo; + +/** + * User-defined types can be added to TypeInfo 'locally', meaning in any + * context where they are defined. This is done with the MAKE_TYPEINFO + * macro, which associates a type with an integer ID. User-defined types + * should never use IDs below USER_TYPEINFO_START. They are reserved + * for the system. + * + * MAKE_TYPEINFO should only be used in the IPC namespace. + */ +#define MAKE_PCQTYPEINFO(__TYPENAME, __TYPEID) \ + template <> \ + struct PcqTypeInfo<__TYPENAME> { \ + static const PcqTypeInfoID ID = __TYPEID; \ + }; + +MAKE_PCQTYPEINFO(bool, 1) +MAKE_PCQTYPEINFO(int8_t, 2) +MAKE_PCQTYPEINFO(uint8_t, 3) +MAKE_PCQTYPEINFO(int16_t, 4) +MAKE_PCQTYPEINFO(uint16_t, 5) +MAKE_PCQTYPEINFO(int32_t, 6) +MAKE_PCQTYPEINFO(uint32_t, 7) +MAKE_PCQTYPEINFO(int64_t, 8) +MAKE_PCQTYPEINFO(uint64_t, 9) +MAKE_PCQTYPEINFO(float, 10) +MAKE_PCQTYPEINFO(double, 11) +MAKE_PCQTYPEINFO(nsresult, 20) +MAKE_PCQTYPEINFO(nsString, 21) +MAKE_PCQTYPEINFO(nsCString, 22) + +// TypeInfoIDs below this value are reserved for the system. +static const PcqTypeInfoID PcqTypeInfo_UserStart = 10000; + } // namespace IPC namespace mozilla { @@ -32,9 +77,259 @@ extern LazyLogModule gPCQLog; #define PCQ_LOGD(...) PCQ_LOG_(LogLevel::Debug, __VA_ARGS__) #define PCQ_LOGE(...) PCQ_LOG_(LogLevel::Error, __VA_ARGS__) -class ProducerConsumerQuue; -class PcqProducer; -class PcqConsumer; +struct PcqStatus { + enum EStatus { + // Operation was successful + Success, + // The operation failed because the queue isn't ready for it. + // Either the queue is too full for an insert or too empty for a remove. + // The operation may succeed if retried. + PcqNotReady, + // The operation was typed and the type check failed. + PcqTypeError, + // The operation required more room than the queue supports. + // It should not be retried -- it will always fail. + PcqTooSmall, + // The operation failed for some reason that is unrecoverable. + // All values below this value indicate a fata error. + PcqFatalError, + // Fatal error: Internal processing ran out of memory. This is likely e.g. + // during de-serialization. + PcqOOMError, + } mValue; + + MOZ_IMPLICIT PcqStatus(const EStatus status = Success) : mValue(status) {} + explicit operator bool() const { return mValue == Success; } + explicit operator int() const { return static_cast(mValue); } + bool operator==(const EStatus& o) const { return mValue == o; } + bool operator!=(const EStatus& o) const { return !(*this == o); } +}; + +inline bool IsSuccess(PcqStatus status) { return status == PcqStatus::Success; } + +template +struct RemoveCVR { + using Type = std::remove_reference_t>; +}; + +template +struct IsTriviallySerializable + : public std::integral_constant::value || + std::is_arithmetic::value> {}; + +struct ProducerConsumerQueue; +class Producer; +class Consumer; + +/** + * PcqParamTraits provide the user with a way to implement PCQ argument + * (de)serialization. It uses a PcqView, which permits the system to + * abandon all changes to the underlying PCQ if any operation fails. + * + * The transactional nature of PCQ operations make the ideal behavior a bit + * complex. Since the PCQ has a fixed amount of memory available to it, + * TryInsert operations operations are expected to sometimes fail and be + * re-issued later. We want these failures to be inexpensive. The same + * goes for TryPeek/TryRemove, which fail when there isn't enough data in + * the queue yet for them to complete. + * + * PcqParamTraits resolve this problem by allowing the Try... operations to + * use PcqParamTraits::Type>::MinSize() to get a + * lower-bound on the amount of room in the queue required for Arg. If the + * operation needs more than is available then the operation quickly fails. + * Otherwise, (de)serialization will commence, although it may still fail if + * MinSize() was too low. + * + * Their expected interface is: + * + * template<> struct PcqParamTraits::Type> { + * // Write data from aArg into the PCQ. It is an error to write less than + * // is reported by MinSize(aArg). + * * static PcqStatus Write(ProducerView& aProducerView, const Arg& aArg) + * {...}; + * + * // Read data from the PCQ into aArg, or just skip the data if aArg is null. + * // It is an error to read less than is reported by MinSize(aArg). + * * static PcqStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...} + * + * // The minimum number of bytes needed to represent this object in the + * queue. + * // It is intended to be a very fast estimate but most cases can easily + * // compute the exact value. + * // If aArg is null then this should be the minimum ever required (it is + * only + * // null when checking for deserialization, since the argument is obviously + * // not yet available). It is an error for the queue to require less room + * // than MinSize() reports. A MinSize of 0 is always valid (albeit + * wasteful). static size_t MinSize(const Arg* aArg) {...} + * }; + */ +template +struct PcqParamTraits; + +// Provides type-checking for PCQ parameters. +template +struct PcqTypedArg { + explicit PcqTypedArg(const Arg& aArg) : mWrite(&aArg), mRead(nullptr) {} + explicit PcqTypedArg(Arg* aArg) : mWrite(nullptr), mRead(aArg) {} + + private: + friend struct PcqParamTraits>; + const Arg* mWrite; + Arg* mRead; +}; + +/** + * Used to give PcqParamTraits a way to write to the Producer without + * actually altering it, in case the transaction fails. + * THis object maintains the error state of the transaction and + * discards commands issued after an error is encountered. + */ +class ProducerView { + public: + ProducerView(Producer* aProducer, size_t aRead, size_t* aWrite) + : mProducer(aProducer), + mRead(aRead), + mWrite(aWrite), + mStatus(PcqStatus::Success) {} + + /** + * Write bytes from aBuffer to the producer if there is enough room. + * aBufferSize must not be 0. + */ + inline PcqStatus Write(const void* aBuffer, size_t aBufferSize); + + template + inline PcqStatus Write(const T* src, size_t count) { + return Write(reinterpret_cast(src), count * sizeof(T)); + } + + /** + * Serialize aArg using Arg's PcqParamTraits. + */ + template + PcqStatus WriteParam(const Arg& aArg) { + return mozilla::webgl::PcqParamTraits::Type>::Write( + *this, aArg); + } + + /** + * Serialize aArg using Arg's PcqParamTraits and PcqTypeInfo. + */ + template + PcqStatus WriteTypedParam(const Arg& aArg) { + return mozilla::webgl::PcqParamTraits>::Write( + *this, PcqTypedArg(aArg)); + } + + /** + * MinSize of Arg using PcqParamTraits. + */ + template + size_t MinSizeParam(const Arg* aArg = nullptr) { + return mozilla::webgl::PcqParamTraits< + typename RemoveCVR::Type>::MinSize(*this, aArg); + } + + inline size_t MinSizeBytes(size_t aNBytes); + + PcqStatus Status() { return mStatus; } + + private: + Producer* mProducer; + size_t mRead; + size_t* mWrite; + PcqStatus mStatus; +}; + +/** + * Used to give PcqParamTraits a way to read from the Consumer without + * actually altering it, in case the transaction fails. + */ +class ConsumerView { + public: + ConsumerView(Consumer* aConsumer, size_t* aRead, size_t aWrite) + : mConsumer(aConsumer), + mRead(aRead), + mWrite(aWrite), + mStatus(PcqStatus::Success) {} + + // When reading raw memory blocks, we may get an error, a shared memory + // object that we take ownership of, or a pointer to a block of + // memory that is only guaranteed to exist as long as the ReadVariant + // call. + using PcqReadBytesVariant = + Variant, void*>; + + /** + * Read bytes from the consumer if there is enough data. aBuffer may + * be null (in which case the data is skipped) + */ + inline PcqStatus Read(void* aBuffer, size_t aBufferSize); + + template + inline PcqStatus Read(T* dest, size_t count) { + return Read(reinterpret_cast(dest), count * sizeof(T)); + } + + /** + * Calls a Matcher that returns a PcqStatus when told that the next bytes are + * in the queue or are in shared memory. + * + * The matcher looks like this: + * struct MyMatcher { + * PcqStatus operator()(RefPtr& x) { + * Read or copy x; take responsibility for closing x. + * } + * PcqStatus operator()() { Data is in queue. Use ConsumerView::Read. } + * }; + * + * The only reason to use this instead of Read is if it is important to + * get the data without copying "large" items. Few things are large + * enough to bother. + */ + template + inline PcqStatus ReadVariant(size_t aBufferSize, Matcher&& aMatcher); + + /** + * Deserialize aArg using Arg's PcqParamTraits. + * If the return value is not Success then aArg is not changed. + */ + template + PcqStatus ReadParam(Arg* aArg = nullptr) { + return mozilla::webgl::PcqParamTraits::Type>::Read( + *this, aArg); + } + + /** + * Deserialize aArg using Arg's PcqParamTraits and PcqTypeInfo. + * If the return value is not Success then aArg is not changed. + */ + template + PcqStatus ReadTypedParam(Arg* aArg = nullptr) { + return mozilla::webgl::PcqParamTraits>::Read( + *this, PcqTypedArg(aArg)); + } + + /** + * MinSize of Arg using PcqParamTraits. aArg may be null. + */ + template + size_t MinSizeParam(Arg* aArg = nullptr) { + return mozilla::webgl::PcqParamTraits< + typename RemoveCVR::Type>::MinSize(*this, aArg); + } + + inline size_t MinSizeBytes(size_t aNBytes); + + PcqStatus Status() { return mStatus; } + + private: + Consumer* mConsumer; + size_t* mRead; + size_t mWrite; + PcqStatus mStatus; +}; } // namespace webgl @@ -47,8 +342,8 @@ using IPC::PcqTypeInfoID; using mozilla::ipc::Shmem; using mozilla::webgl::IsSuccess; +using mozilla::webgl::PcqStatus; using mozilla::webgl::ProducerConsumerQueue; -using mozilla::webgl::QueueStatus; constexpr size_t GetCacheLineSize() { return 64; } @@ -72,6 +367,16 @@ constexpr size_t GetMaxHeaderSize() { return maxAlign1 + readAndAlign2 + writeAndAlign3; } +inline size_t UsedBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) { + return (aRead <= aWrite) ? aWrite - aRead + : (aQueueBufferSize - aRead) + aWrite; +} + +inline size_t FreeBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) { + // Remember, queueSize is queueBufferSize-1 + return (aQueueBufferSize - 1) - UsedBytes(aQueueBufferSize, aRead, aWrite); +} + template size_t MinSizeofArgs(View& aView, const Arg* aArg, const Args*... aArgs) { return aView.MinSizeParam(aArg) + MinSizeofArgs(aView, aArgs...); @@ -93,6 +398,63 @@ size_t MinSizeofArgs(View& aView) { return aView.MinSizeParam(nullptr); } +// Currently, require any parameters expected to need more than 1/16 the total +// number of bytes in the command queue to use their own SharedMemory. +// TODO: Base this heuristic on something. Also, if I made the PCQ size +// (aTotal) a template parameter then this could be a compile-time check +// in nearly all cases. +inline bool NeedsSharedMemory(size_t aRequested, size_t aTotal) { + return (aTotal / 16) < aRequested; +} + +/** + * The marshaller handles all data insertion into the queue. + */ +class Marshaller { + public: + static PcqStatus WriteObject(uint8_t* aQueue, size_t aQueueBufferSize, + size_t aRead, size_t* aWrite, const void* aArg, + size_t aArgLength) { + const uint8_t* buf = reinterpret_cast(aArg); + if (FreeBytes(aQueueBufferSize, aRead, *aWrite) < aArgLength) { + return PcqStatus::PcqNotReady; + } + + if (*aWrite + aArgLength <= aQueueBufferSize) { + memcpy(aQueue + *aWrite, buf, aArgLength); + } else { + size_t firstLen = aQueueBufferSize - *aWrite; + memcpy(aQueue + *aWrite, buf, firstLen); + memcpy(aQueue, &buf[firstLen], aArgLength - firstLen); + } + *aWrite = (*aWrite + aArgLength) % aQueueBufferSize; + return PcqStatus::Success; + } + + // The PcqBase must belong to a Consumer. + static PcqStatus ReadObject(const uint8_t* aQueue, size_t aQueueBufferSize, + size_t* aRead, size_t aWrite, void* aArg, + size_t aArgLength) { + if (UsedBytes(aQueueBufferSize, *aRead, aWrite) < aArgLength) { + return PcqStatus::PcqNotReady; + } + + if (aArg) { + uint8_t* buf = reinterpret_cast(aArg); + if (*aRead + aArgLength <= aQueueBufferSize) { + memcpy(buf, aQueue + *aRead, aArgLength); + } else { + size_t firstLen = aQueueBufferSize - *aRead; + memcpy(buf, aQueue + *aRead, firstLen); + memcpy(&buf[firstLen], aQueue, aArgLength - firstLen); + } + } + + *aRead = (*aRead + aArgLength) % aQueueBufferSize; + return PcqStatus::Success; + } +}; + class PcqRCSemaphore { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PcqRCSemaphore) @@ -117,7 +479,7 @@ class PcqRCSemaphore { }; /** - * Common base class for PcqProducer and Consumer. + * Common base class for Producer and Consumer. */ class PcqBase { public: @@ -126,7 +488,7 @@ class PcqBase { */ size_t UsedBytes(size_t aRead, size_t aWrite) { MOZ_ASSERT(ValidState(aRead, aWrite)); - return mozilla::webgl::UsedBytes(QueueBufferSize(), aRead, aWrite); + return detail::UsedBytes(QueueBufferSize(), aRead, aWrite); } /** @@ -134,7 +496,7 @@ class PcqBase { */ size_t FreeBytes(size_t aRead, size_t aWrite) { MOZ_ASSERT(ValidState(aRead, aWrite)); - return mozilla::webgl::FreeBytes(QueueBufferSize(), aRead, aWrite); + return detail::FreeBytes(QueueBufferSize(), aRead, aWrite); } /** @@ -161,7 +523,7 @@ class PcqBase { } // Cheaply get the used size of the current queue. This does no - // synchronization so the information may be stale. On the PcqProducer + // synchronization so the information may be stale. On the Producer // side, it will never underestimate the number of bytes used and, // on the Consumer side, it will never overestimate them. // (The reciprocal is true of FreeBytes.) @@ -339,14 +701,14 @@ namespace webgl { using mozilla::ipc::Shmem; /** - * The PcqProducer is the endpoint that inserts elements into the queue. It + * The Producer is the endpoint that inserts elements into the queue. It * should only be used from one thread at a time. */ -class PcqProducer : public detail::PcqBase { +class Producer : public detail::PcqBase { public: - PcqProducer(PcqProducer&& aOther) = default; - PcqProducer& operator=(PcqProducer&&) = default; - PcqProducer() = default; // for IPDL + Producer(Producer&& aOther) = default; + Producer& operator=(Producer&&) = default; + Producer() = default; // for IPDL /** * The number of bytes that the queue can hold. @@ -358,7 +720,7 @@ class PcqProducer : public detail::PcqBase { * succeed then the queue is unchanged. */ template - QueueStatus TryInsert(Args&&... aArgs) { + PcqStatus TryInsert(Args&&... aArgs) { size_t write = mWrite->load(std::memory_order_relaxed); const size_t initWrite = write; size_t read = mRead->load(std::memory_order_acquire); @@ -368,13 +730,13 @@ class PcqProducer : public detail::PcqBase { "Queue was found in an invalid state. Queue Size: %zu. " "Read: %zu. Write: %zu", Size(), read, write); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } ProducerView view(this, read, &write); // Check that the queue has enough unoccupied room for all Args types. - // This is based on the user's size estimate for args from QueueParamTraits. + // This is based on the user's size estimate for args from PcqParamTraits. size_t bytesNeeded = detail::MinSizeofArgs(view, &aArgs...); if (Size() < bytesNeeded) { @@ -382,7 +744,7 @@ class PcqProducer : public detail::PcqBase { "Queue is too small for objects. Queue Size: %zu. " "Needed: %zu", Size(), bytesNeeded); - return QueueStatus::kTooSmall; + return PcqStatus::PcqTooSmall; } if (FreeBytes(read, write) < bytesNeeded) { @@ -390,14 +752,14 @@ class PcqProducer : public detail::PcqBase { "Not enough room to insert. Has: %zu (%zu,%zu). " "Needed: %zu", FreeBytes(read, write), read, write, bytesNeeded); - return QueueStatus::kNotReady; + return PcqStatus::PcqNotReady; } // Try to insert args in sequence. Only update the queue if the // operation was successful. We already checked all normal means of // failure but we can expect occasional failure here if the user's - // QueueParamTraits::MinSize method was inexact. - QueueStatus status = TryInsertHelper(view, aArgs...); + // PcqParamTraits::MinSize method was inexact. + PcqStatus status = TryInsertHelper(view, aArgs...); if (!status) { PCQ_LOGD( "Failed to insert with error (%d). Has: %zu (%zu,%zu). " @@ -409,18 +771,18 @@ class PcqProducer : public detail::PcqBase { MOZ_ASSERT(ValidState(read, write)); // Check that at least bytesNeeded were produced. Failing this means - // that some QueueParamTraits::MinSize estimated too many bytes. + // that some PcqParamTraits::MinSize estimated too many bytes. bool enoughBytes = UsedBytes(read, write) >= UsedBytes(read, (initWrite + bytesNeeded) % QueueBufferSize()); MOZ_ASSERT(enoughBytes); if (!enoughBytes) { - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } // Commit the transaction. PCQ_LOGD( - "Successfully inserted. PcqProducer used %zu bytes total. " + "Successfully inserted. Producer used %zu bytes total. " "Write index: %zu -> %zu", bytesNeeded, initWrite, write); mWrite->store(write, std::memory_order_release); @@ -439,49 +801,47 @@ class PcqProducer : public detail::PcqBase { * succeed in the time allotted then the queue is unchanged. */ template - QueueStatus TryWaitInsert(const Maybe& aDuration, - Args&&... aArgs) { + PcqStatus TryWaitInsert(const Maybe& aDuration, + Args&&... aArgs) { return TryWaitInsertImpl(false, aDuration, std::forward(aArgs)...); } template - QueueStatus TryTypedInsert(Args&&... aArgs) { + PcqStatus TryTypedInsert(Args&&... aArgs) { return TryInsert(PcqTypedArg(aArgs)...); } protected: friend ProducerConsumerQueue; - friend ProducerView; + friend ProducerView; template - QueueStatus TryInsertHelper(ProducerView& aView, const Arg& aArg, - const Args&... aArgs) { - QueueStatus status = TryInsertItem(aView, aArg); + PcqStatus TryInsertHelper(ProducerView& aView, const Arg& aArg, + const Args&... aArgs) { + PcqStatus status = TryInsertItem(aView, aArg); return IsSuccess(status) ? TryInsertHelper(aView, aArgs...) : status; } - QueueStatus TryInsertHelper(ProducerView&) { - return QueueStatus::kSuccess; - } + PcqStatus TryInsertHelper(ProducerView&) { return PcqStatus::Success; } template - QueueStatus TryInsertItem(ProducerView& aView, const Arg& aArg) { - return QueueParamTraits::Type>::Write(aView, aArg); + PcqStatus TryInsertItem(ProducerView& aView, const Arg& aArg) { + return PcqParamTraits::Type>::Write(aView, aArg); } template - QueueStatus TryWaitInsertImpl(bool aRecursed, - const Maybe& aDuration, - Args&&... aArgs) { + PcqStatus TryWaitInsertImpl(bool aRecursed, + const Maybe& aDuration, + Args&&... aArgs) { // Wait up to aDuration for the not-full semaphore to be signaled. // If we run out of time then quit. TimeStamp start(TimeStamp::Now()); if (aRecursed && (!mMaybeNotFullSem->Wait(aDuration))) { - return QueueStatus::kNotReady; + return PcqStatus::PcqNotReady; } // Attempt to insert all args. No waiting is done here. - QueueStatus status = TryInsert(std::forward(aArgs)...); + PcqStatus status = TryInsert(std::forward(aArgs)...); TimeStamp now; if (aRecursed && IsSuccess(status)) { @@ -493,7 +853,7 @@ class PcqProducer : public detail::PcqBase { if ((!IsFull()) && (!mMaybeNotFullSem->IsAvailable())) { mMaybeNotFullSem->Signal(); } - } else if ((status == QueueStatus::kNotReady) && + } else if ((status == PcqStatus::PcqNotReady) && (aDuration.isNothing() || ((now = TimeStamp::Now()) - start) < aDuration.value())) { // We don't have enough room but still have time, e.g. because @@ -510,39 +870,32 @@ class PcqProducer : public detail::PcqBase { } template - QueueStatus WriteObject(size_t aRead, size_t* aWrite, const Arg& arg, - size_t aArgSize) { - return Marshaller::WriteObject(mQueue, QueueBufferSize(), aRead, aWrite, - arg, aArgSize); + PcqStatus WriteObject(size_t aRead, size_t* aWrite, const Arg& arg, + size_t aArgSize) { + return mozilla::detail::Marshaller::WriteObject( + mQueue, QueueBufferSize(), aRead, aWrite, arg, aArgSize); } - // Currently, the PCQ requires any parameters expected to need more than - // 1/16 the total number of bytes in the command queue to use their own - // SharedMemory. - bool NeedsSharedMemory(size_t aRequested) { - return (Size() / 16) < aRequested; - } - - PcqProducer(Shmem& aShmem, base::ProcessId aOtherPid, size_t aQueueSize, - RefPtr aMaybeNotEmptySem, - RefPtr aMaybeNotFullSem) + Producer(Shmem& aShmem, base::ProcessId aOtherPid, size_t aQueueSize, + RefPtr aMaybeNotEmptySem, + RefPtr aMaybeNotFullSem) : PcqBase(aShmem, aOtherPid, aQueueSize, aMaybeNotEmptySem, aMaybeNotFullSem) { - // Since they are shared, this initializes mRead/mWrite in the PcqConsumer + // Since they are shared, this initializes mRead/mWrite in the Consumer // as well. *mRead = 0; *mWrite = 0; } - PcqProducer(const PcqProducer&) = delete; - PcqProducer& operator=(const PcqProducer&) = delete; + Producer(const Producer&) = delete; + Producer& operator=(const Producer&) = delete; }; -class PcqConsumer : public detail::PcqBase { +class Consumer : public detail::PcqBase { public: - PcqConsumer(PcqConsumer&& aOther) = default; - PcqConsumer& operator=(PcqConsumer&&) = default; - PcqConsumer() = default; // for IPDL + Consumer(Consumer&& aOther) = default; + Consumer& operator=(Consumer&&) = default; + Consumer() = default; // for IPDL /** * The number of bytes that the queue can hold. @@ -553,15 +906,15 @@ class PcqConsumer : public detail::PcqBase { * Attempts to copy aArgs in the queue. The queue remains unchanged. */ template - QueueStatus TryPeek(Args&... aArgs) { + PcqStatus TryPeek(Args&... aArgs) { return TryPeekOrRemove( - [&](ConsumerView& aView) -> QueueStatus { + [&](ConsumerView& aView) -> PcqStatus { return TryPeekRemoveHelper(aView, &aArgs...); }); } template - QueueStatus TryTypedPeek(Args&... aArgs) { + PcqStatus TryTypedPeek(Args&... aArgs) { return TryPeek(PcqTypedArg(aArgs)...); } @@ -570,15 +923,15 @@ class PcqConsumer : public detail::PcqBase { * not succeed then the queue is unchanged. */ template - QueueStatus TryRemove(Args&... aArgs) { + PcqStatus TryRemove(Args&... aArgs) { return TryPeekOrRemove( - [&](ConsumerView& aView) -> QueueStatus { + [&](ConsumerView& aView) -> PcqStatus { return TryPeekRemoveHelper(aView, &aArgs...); }); } template - QueueStatus TryTypedRemove(Args&... aArgs) { + PcqStatus TryTypedRemove(Args&... aArgs) { return TryRemove(PcqTypedArg(&aArgs)...); } @@ -587,13 +940,13 @@ class PcqConsumer : public detail::PcqBase { * operation does not succeed then the queue is unchanged. */ template - QueueStatus TryRemove() { + PcqStatus TryRemove() { using seq = std::index_sequence_for; return TryRemove(seq{}); } template - QueueStatus TryTypedRemove() { + PcqStatus TryTypedRemove() { return TryRemove...>(); } @@ -603,8 +956,7 @@ class PcqConsumer : public detail::PcqBase { * unchanged. Pass Nothing to wait until peek succeeds. */ template - QueueStatus TryWaitPeek(const Maybe& aDuration, - Args&... aArgs) { + PcqStatus TryWaitPeek(const Maybe& aDuration, Args&... aArgs) { return TryWaitPeekOrRemove(aDuration, aArgs...); } @@ -613,8 +965,8 @@ class PcqConsumer : public detail::PcqBase { * Pass Nothing to wait until removal succeeds. */ template - QueueStatus TryWaitRemove(const Maybe& aDuration, - Args&... aArgs) { + PcqStatus TryWaitRemove(const Maybe& aDuration, + Args&... aArgs) { return TryWaitPeekOrRemove(aDuration, aArgs...); } @@ -625,16 +977,16 @@ class PcqConsumer : public detail::PcqBase { * Pass Nothing to wait until removal succeeds. */ template - QueueStatus TryWaitRemove(const Maybe& aDuration) { + PcqStatus TryWaitRemove(const Maybe& aDuration) { // Wait up to aDuration for the not-empty semaphore to be signaled. // If we run out of time then quit. TimeStamp start(TimeStamp::Now()); if (!mMaybeNotEmptySem->Wait(aDuration)) { - return QueueStatus::kNotReady; + return PcqStatus::PcqNotReady; } // Attempt to remove all args. No waiting is done here. - QueueStatus status = TryRemove(); + PcqStatus status = TryRemove(); TimeStamp now; if (IsSuccess(status)) { @@ -646,7 +998,7 @@ class PcqConsumer : public detail::PcqBase { if ((!IsEmpty()) && (!mMaybeNotEmptySem->IsAvailable())) { mMaybeNotEmptySem->Signal(); } - } else if ((status == QueueStatus::kNotReady) && + } else if ((status == PcqStatus::PcqNotReady) && (aDuration.isNothing() || ((now = TimeStamp::Now()) - start) < aDuration.value())) { // We don't have enough data but still have time, e.g. because @@ -663,14 +1015,13 @@ class PcqConsumer : public detail::PcqBase { protected: friend ProducerConsumerQueue; - friend ConsumerView; + friend ConsumerView; // PeekOrRemoveOperation takes a read pointer and a write index. - using PeekOrRemoveOperation = - std::function&)>; + using PeekOrRemoveOperation = std::function; template - QueueStatus TryPeekOrRemove(const PeekOrRemoveOperation& aOperation) { + PcqStatus TryPeekOrRemove(const PeekOrRemoveOperation& aOperation) { size_t write = mWrite->load(std::memory_order_acquire); size_t read = mRead->load(std::memory_order_relaxed); const size_t initRead = read; @@ -680,13 +1031,13 @@ class PcqConsumer : public detail::PcqBase { "Queue was found in an invalid state. Queue Size: %zu. " "Read: %zu. Write: %zu", Size(), read, write); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } - ConsumerView view(this, &read, write); + ConsumerView view(this, &read, write); // Check that the queue has enough unoccupied room for all Args types. - // This is based on the user's size estimate for Args from QueueParamTraits. + // This is based on the user's size estimate for Args from PcqParamTraits. size_t bytesNeeded = detail::MinSizeofArgs(view); if (Size() < bytesNeeded) { @@ -694,7 +1045,7 @@ class PcqConsumer : public detail::PcqBase { "Queue is too small for objects. Queue Size: %zu. " "Bytes needed: %zu.", Size(), bytesNeeded); - return QueueStatus::kTooSmall; + return PcqStatus::PcqTooSmall; } if (UsedBytes(read, write) < bytesNeeded) { @@ -702,30 +1053,30 @@ class PcqConsumer : public detail::PcqBase { "Not enough data in queue. Has: %zu (%zu,%zu). " "Bytes needed: %zu", UsedBytes(read, write), read, write, bytesNeeded); - return QueueStatus::kNotReady; + return PcqStatus::PcqNotReady; } // Only update the queue if the operation was successful and we aren't // peeking. - QueueStatus status = aOperation(view); + PcqStatus status = aOperation(view); if (!status) { return status; } // Check that at least bytesNeeded were consumed. Failing this means - // that some QueueParamTraits::MinSize estimated too many bytes. + // that some PcqParamTraits::MinSize estimated too many bytes. bool enoughBytes = FreeBytes(read, write) >= FreeBytes((initRead + bytesNeeded) % QueueBufferSize(), write); MOZ_ASSERT(enoughBytes); if (!enoughBytes) { - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } MOZ_ASSERT(ValidState(read, write)); PCQ_LOGD( - "Successfully %s. PcqConsumer used %zu bytes total. " + "Successfully %s. Consumer used %zu bytes total. " "Read index: %zu -> %zu", isRemove ? "removed" : "peeked", bytesNeeded, initRead, read); @@ -744,34 +1095,33 @@ class PcqConsumer : public detail::PcqBase { // Helper that passes nulls for all Args* template - QueueStatus TryRemove(std::index_sequence) { + PcqStatus TryRemove(std::index_sequence) { std::tuple nullArgs; - return TryPeekOrRemove( - [&](ConsumerView& aView) { - return TryPeekRemoveHelper(aView, std::get(nullArgs)...); - }); + return TryPeekOrRemove([&](ConsumerView& aView) { + return TryPeekRemoveHelper(aView, std::get(nullArgs)...); + }); } template - QueueStatus TryWaitPeekOrRemove(const Maybe& aDuration, - Args&&... aArgs) { + PcqStatus TryWaitPeekOrRemove(const Maybe& aDuration, + Args&&... aArgs) { return TryWaitPeekOrRemoveImpl(false, aDuration, std::forward(aArgs)...); } template - QueueStatus TryWaitPeekOrRemoveImpl(bool aRecursed, - const Maybe& aDuration, - Args&... aArgs) { + PcqStatus TryWaitPeekOrRemoveImpl(bool aRecursed, + const Maybe& aDuration, + Args&... aArgs) { // Wait up to aDuration for the not-empty semaphore to be signaled. // If we run out of time then quit. TimeStamp start(TimeStamp::Now()); if (aRecursed && (!mMaybeNotEmptySem->Wait(aDuration))) { - return QueueStatus::kNotReady; + return PcqStatus::PcqNotReady; } // Attempt to read all args. No waiting is done here. - QueueStatus status = isRemove ? TryRemove(aArgs...) : TryPeek(aArgs...); + PcqStatus status = isRemove ? TryRemove(aArgs...) : TryPeek(aArgs...); TimeStamp now; if (aRecursed && IsSuccess(status)) { @@ -783,7 +1133,7 @@ class PcqConsumer : public detail::PcqBase { if ((!IsEmpty()) && (!mMaybeNotEmptySem->IsAvailable())) { mMaybeNotEmptySem->Signal(); } - } else if ((status == QueueStatus::kNotReady) && + } else if ((status == PcqStatus::PcqNotReady) && (aDuration.isNothing() || ((now = TimeStamp::Now()) - start) < aDuration.value())) { // We don't have enough data but still have time, e.g. because @@ -801,54 +1151,147 @@ class PcqConsumer : public detail::PcqBase { // Version of the helper for copying values out of the queue. template - QueueStatus TryPeekRemoveHelper(ConsumerView& aView, - Args*... aArgs); + PcqStatus TryPeekRemoveHelper(ConsumerView& aView, Args*... aArgs); template - QueueStatus TryPeekRemoveHelper(ConsumerView& aView, Arg* aArg, - Args*... aArgs) { - QueueStatus status = TryCopyOrSkipItem(aView, aArg); + PcqStatus TryPeekRemoveHelper(ConsumerView& aView, Arg* aArg, + Args*... aArgs) { + PcqStatus status = TryCopyOrSkipItem(aView, aArg); return IsSuccess(status) ? TryPeekRemoveHelper(aView, aArgs...) : status; } - template <> - QueueStatus TryPeekRemoveHelper(ConsumerView&) { - return QueueStatus::kSuccess; - } + PcqStatus TryPeekRemoveHelper(ConsumerView&) { return PcqStatus::Success; } // If an item is available then it is copied into aArg. The item is skipped // over if aArg is null. template - QueueStatus TryCopyOrSkipItem(ConsumerView& aView, Arg* aArg) { - return QueueParamTraits::Type>::Read( + PcqStatus TryCopyOrSkipItem(ConsumerView& aView, Arg* aArg) { + return PcqParamTraits::Type>::Read( aView, const_cast*>(aArg)); } template - QueueStatus ReadObject(size_t* aRead, size_t aWrite, Arg* arg, - size_t aArgSize) { - return Marshaller::ReadObject(mQueue, QueueBufferSize(), aRead, aWrite, arg, - aArgSize); + PcqStatus ReadObject(size_t* aRead, size_t aWrite, Arg* arg, + size_t aArgSize) { + return mozilla::detail::Marshaller::ReadObject( + mQueue, QueueBufferSize(), aRead, aWrite, arg, aArgSize); } - // Currently, the PCQ requires any parameters expected to need more than - // 1/16 the total number of bytes in the command queue to use their own - // SharedMemory. - bool NeedsSharedMemory(size_t aRequested) { - return (Size() / 16) < aRequested; - } - - PcqConsumer(Shmem& aShmem, base::ProcessId aOtherPid, size_t aQueueSize, - RefPtr aMaybeNotEmptySem, - RefPtr aMaybeNotFullSem) + Consumer(Shmem& aShmem, base::ProcessId aOtherPid, size_t aQueueSize, + RefPtr aMaybeNotEmptySem, + RefPtr aMaybeNotFullSem) : PcqBase(aShmem, aOtherPid, aQueueSize, aMaybeNotEmptySem, aMaybeNotFullSem) {} - PcqConsumer(const PcqConsumer&) = delete; - PcqConsumer& operator=(const PcqConsumer&) = delete; + Consumer(const Consumer&) = delete; + Consumer& operator=(const Consumer&) = delete; }; +PcqStatus ProducerView::Write(const void* aBuffer, size_t aBufferSize) { + MOZ_ASSERT(aBuffer && (aBufferSize > 0)); + if (!mStatus) { + return mStatus; + } + + if (detail::NeedsSharedMemory(aBufferSize, mProducer->Size())) { + auto smem = MakeRefPtr(); + if (!smem->Create(aBufferSize) || !smem->Map(aBufferSize)) { + return PcqStatus::PcqFatalError; + } + mozilla::ipc::SharedMemoryBasic::Handle handle; + if (!smem->ShareToProcess(mProducer->mOtherPid, &handle)) { + return PcqStatus::PcqFatalError; + } + memcpy(smem->memory(), aBuffer, aBufferSize); + smem->CloseHandle(); + return WriteParam(handle); + } + + return mProducer->WriteObject(mRead, mWrite, aBuffer, aBufferSize); +} + +size_t ProducerView::MinSizeBytes(size_t aNBytes) { + return detail::NeedsSharedMemory(aNBytes, mProducer->Size()) + ? MinSizeParam((mozilla::ipc::SharedMemoryBasic::Handle*)nullptr) + : aNBytes; +} + +PcqStatus ConsumerView::Read(void* aBuffer, size_t aBufferSize) { + struct PcqReadBytesMatcher { + PcqStatus operator()(RefPtr& smem) { + MOZ_ASSERT(smem); + PcqStatus ret; + if (smem->memory()) { + if (mBuffer) { + memcpy(mBuffer, smem->memory(), mBufferSize); + } + ret = PcqStatus::Success; + } else { + ret = PcqStatus::PcqFatalError; + } + // TODO: Problem: CloseHandle should only be called on the remove/skip + // call. A peek should not CloseHandle! + smem->CloseHandle(); + return ret; + } + PcqStatus operator()() { + return mConsumer.ReadObject(mRead, mWrite, mBuffer, mBufferSize); + } + + Consumer& mConsumer; + size_t* mRead; + size_t mWrite; + void* mBuffer; + size_t mBufferSize; + }; + + MOZ_ASSERT(aBufferSize > 0); + if (!mStatus) { + return mStatus; + } + + return ReadVariant(aBufferSize, + PcqReadBytesMatcher{*(this->mConsumer), mRead, mWrite, + aBuffer, aBufferSize}); +} + +template +PcqStatus ConsumerView::ReadVariant(size_t aBufferSize, Matcher&& aMatcher) { + if (!mStatus) { + return mStatus; + } + + if (detail::NeedsSharedMemory(aBufferSize, mConsumer->Size())) { + // Always read shared-memory -- don't just skip. + mozilla::ipc::SharedMemoryBasic::Handle handle; + if (!ReadParam(&handle)) { + return Status(); + } + + // TODO: Find some way to MOZ_RELEASE_ASSERT that buffersize exactly matches + // what was in queue. This doesn't appear to be possible with the + // information available. + // TODO: This needs to return the same refptr even when peeking/during + // transactions that get aborted/rewound. So this is wrong. + auto sharedMem = MakeRefPtr(); + if (!sharedMem->IsHandleValid(handle) || + !sharedMem->SetHandle(handle, + mozilla::ipc::SharedMemory::RightsReadWrite) || + !sharedMem->Map(aBufferSize)) { + return PcqStatus::PcqFatalError; + } + return aMatcher(sharedMem); + } + return aMatcher(); +} + +size_t ConsumerView::MinSizeBytes(size_t aNBytes) { + return detail::NeedsSharedMemory(aNBytes, mConsumer->Size()) + ? MinSizeParam((mozilla::ipc::SharedMemoryBasic::Handle*)nullptr) + : aNBytes; +} + using mozilla::detail::GetCacheLineSize; using mozilla::detail::GetMaxHeaderSize; @@ -857,11 +1300,46 @@ using mozilla::detail::GetMaxHeaderSize; * circular queue. The object is backed with a Shmem, which allows * it to be used across processes. * + * To work with this queue: + * 1. In some process (typically either the producer or consumer process), + * create a ProducerConsumerQueue: + * `UniquePtr pcq(ProducerConsumerQueue::Create())` + * 2. Grab either the Producer or the Consumer from the ProducerConsumerQueue + * with e.g.: + * UniquePtr my_consumer(std::move(pcq.mConsumer)) + * 3. (If using cross-process:) Create an IPDL message in an actor that runs + * in both processes. This message will send the other endpoint from your + * ProducerConsumerQueue. It needs to provide a `using` statement for the + * endpoint that specifies it as a shmemholder, as well as a message to send + * it: + * using shmemholder Producer from "mozilla/dom/ProducerConsumerQueue.h"; + * // ... + * async MyIPDLMessage(UniquePtr aProducer); + * If you don't label the type as a shmemholder then you will get a runtime + * error whenever you attempt to send it. + * 4. Either send the other endpoint (producer or consumer) to the remote + * process and Recv it there: + * SendMyIPDLMessage(std::move(pcq.mProducer)); + * --- + * IPCResult RecvMyIPDLMessage(UniquePtr&& aProducer) { + * // ... + * } + * or grab the other endpoint for use in the same process without an IPDL + * message, as in step 2. + * + * The ProducerConsumerQueue object is then empty and can be freed. + * + * With endpoints in their proper processes, the producer can begin producing + * entries and the consumer consuming them, with synchronization being handled + * by this class. + * * This is a single-producer/single-consumer queue. Another way of saying that - * is to say that the PcqProducer and PcqConsumer objects are not thread-safe. + * is to say that the Producer and Consumer objects are not thread-safe. */ -class ProducerConsumerQueue { - public: +struct ProducerConsumerQueue { + UniquePtr mProducer; + UniquePtr mConsumer; + /** * Create a queue whose endpoints are the same as those of aProtocol. * In choosing a queueSize, be aware that both the queue and the Shmem will @@ -950,12 +1428,6 @@ class ProducerConsumerQueue { return mozilla::detail::GetCacheLineSize(); } - using Producer = PcqProducer; - using Consumer = PcqConsumer; - - UniquePtr TakeProducer() { return std::move(mProducer); } - UniquePtr TakeConsumer() { return std::move(mConsumer); } - private: ProducerConsumerQueue(Shmem& aShmem, base::ProcessId aOtherPid, size_t aQueueSize, @@ -972,9 +1444,6 @@ class ProducerConsumerQueue { "Other process ID: %08x.", this, aShmem.Size(), aQueueSize, (uint32_t)aOtherPid); } - - UniquePtr mProducer; - UniquePtr mConsumer; }; } // namespace webgl @@ -989,8 +1458,8 @@ struct IPDLParamTraits { WriteIPDLParam(aMsg, aActor, aParam.QueueSize()); WriteIPDLParam(aMsg, aActor, std::move(aParam.mShmem)); - // May not currently share a PcqProducer or PcqConsumer with a process that - // it's Shmem is not related to. + // May not currently share a Producer or Consumer with a process that it's + // Shmem is not related to. MOZ_ASSERT(aActor->OtherPid() == aParam.mOtherPid); WriteIPDLParam( aMsg, aActor, @@ -1029,18 +1498,699 @@ struct IPDLParamTraits { }; template <> -struct IPDLParamTraits +struct IPDLParamTraits : public IPDLParamTraits { - typedef mozilla::webgl::PcqProducer paramType; + typedef mozilla::webgl::Producer paramType; }; template <> -struct IPDLParamTraits +struct IPDLParamTraits : public IPDLParamTraits { - typedef mozilla::webgl::PcqConsumer paramType; + typedef mozilla::webgl::Consumer paramType; }; } // namespace ipc + +// --------------------------------------------------------------- + +namespace webgl { + +template +struct PcqParamTraits> { + using ParamType = PcqTypedArg; + + template ::ID> + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + MOZ_ASSERT(aArg.mWrite); + aProducerView.WriteParam(ArgTypeId); + return aProducerView.WriteParam(*aArg.mWrite); + } + + template ::ID> + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + MOZ_ASSERT(aArg->mRead); + PcqTypeInfoID typeId; + if (!aConsumerView.ReadParam(&typeId)) { + return aConsumerView.Status(); + } + return (typeId == ArgTypeId) ? aConsumerView.ReadParam(aArg) + : PcqStatus::PcqTypeError; + } + + template + static constexpr size_t MinSize(View& aView, const ParamType* aArg) { + return sizeof(PcqTypeInfoID) + + aView.MinSize(aArg->mWrite ? aArg->mWrite : aArg->mRead); + } +}; + +// --------------------------------------------------------------- + +/** + * True for types that can be (de)serialized by memcpy. + */ +template +struct PcqParamTraits { + static PcqStatus Write(ProducerView& aProducerView, const Arg& aArg) { + static_assert(mozilla::webgl::template IsTriviallySerializable::value, + "No PcqParamTraits specialization was found for this type " + "and it does not satisfy IsTriviallySerializable."); + // Write self as binary + return aProducerView.Write(&aArg, sizeof(Arg)); + } + + static PcqStatus Read(ConsumerView& aConsumerView, Arg* aArg) { + static_assert(mozilla::webgl::template IsTriviallySerializable::value, + "No PcqParamTraits specialization was found for this type " + "and it does not satisfy IsTriviallySerializable."); + // Read self as binary + return aConsumerView.Read(aArg, sizeof(Arg)); + } + + template + static constexpr size_t MinSize(View& aView, const Arg* aArg) { + static_assert(mozilla::webgl::template IsTriviallySerializable::value, + "No PcqParamTraits specialization was found for this type " + "and it does not satisfy IsTriviallySerializable."); + return sizeof(Arg); + } +}; + +// --------------------------------------------------------------- + +template <> +struct IsTriviallySerializable : std::true_type {}; + +// --------------------------------------------------------------- + +template <> +struct PcqParamTraits { + using ParamType = nsACString; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + if ((!aProducerView.WriteParam(aArg.IsVoid())) || aArg.IsVoid()) { + return aProducerView.Status(); + } + + uint32_t len = aArg.Length(); + if ((!aProducerView.WriteParam(len)) || (len == 0)) { + return aProducerView.Status(); + } + + return aProducerView.Write(aArg.BeginReading(), len); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isVoid = false; + if (!aConsumerView.ReadParam(&isVoid)) { + return aConsumerView.Status(); + } + if (aArg) { + aArg->SetIsVoid(isVoid); + } + if (isVoid) { + return PcqStatus::Success; + } + + uint32_t len = 0; + if (!aConsumerView.ReadParam(&len)) { + return aConsumerView.Status(); + } + + if (len == 0) { + if (aArg) { + *aArg = ""; + } + return PcqStatus::Success; + } + + char* buf = aArg ? new char[len + 1] : nullptr; + if (aArg && (!buf)) { + return PcqStatus::PcqOOMError; + } + if (!aConsumerView.Read(buf, len)) { + return aConsumerView.Status(); + } + buf[len] = '\0'; + if (aArg) { + aArg->Adopt(buf, len); + } + return PcqStatus::Success; + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + size_t minSize = aView.template MinSizeParam(nullptr); + if ((!aArg) || aArg->IsVoid()) { + return minSize; + } + minSize += aView.template MinSizeParam(nullptr) + + aView.MinSizeBytes(aArg->Length()); + return minSize; + } +}; + +template <> +struct PcqParamTraits { + using ParamType = nsAString; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + if ((!aProducerView.WriteParam(aArg.IsVoid())) || (aArg.IsVoid())) { + return aProducerView.Status(); + } + // DLP: No idea if this includes null terminator + uint32_t len = aArg.Length(); + if ((!aProducerView.WriteParam(len)) || (len == 0)) { + return aProducerView.Status(); + } + constexpr const uint32_t sizeofchar = sizeof(typename ParamType::char_type); + return aProducerView.Write(aArg.BeginReading(), len * sizeofchar); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isVoid = false; + if (!aConsumerView.ReadParam(&isVoid)) { + return aConsumerView.Status(); + } + if (aArg) { + aArg->SetIsVoid(isVoid); + } + if (isVoid) { + return PcqStatus::Success; + } + + // DLP: No idea if this includes null terminator + uint32_t len = 0; + if (!aConsumerView.ReadParam(&len)) { + return aConsumerView.Status(); + } + + if (len == 0) { + if (aArg) { + *aArg = nsString(); + } + return PcqStatus::Success; + } + + uint32_t sizeofchar = sizeof(typename ParamType::char_type); + typename ParamType::char_type* buf = nullptr; + if (aArg) { + buf = static_cast( + malloc((len + 1) * sizeofchar)); + if (!buf) { + return PcqStatus::PcqOOMError; + } + } + + if (!aConsumerView.Read(buf, len * sizeofchar)) { + return aConsumerView.Status(); + } + + buf[len] = L'\0'; + if (aArg) { + aArg->Adopt(buf, len); + } + + return PcqStatus::Success; + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + size_t minSize = aView.template MinSizeParam(nullptr); + if ((!aArg) || aArg->IsVoid()) { + return minSize; + } + uint32_t sizeofchar = sizeof(typename ParamType::char_type); + minSize += aView.template MinSizeParam(nullptr) + + aView.MinSizeBytes(aArg->Length() * sizeofchar); + return minSize; + } +}; + +template <> +struct PcqParamTraits : public PcqParamTraits { + using ParamType = nsCString; +}; + +template <> +struct PcqParamTraits : public PcqParamTraits { + using ParamType = nsString; +}; + +// --------------------------------------------------------------- + +template ::value> +struct NSArrayPcqParamTraits; + +// For ElementTypes that are !IsTriviallySerializable +template +struct NSArrayPcqParamTraits, false> { + using ElementType = _ElementType; + using ParamType = nsTArray; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + size_t arrayLen = aArg.Length(); + aProducerView.WriteParam(arrayLen); + for (size_t i = 0; i < aArg.Length(); ++i) { + aProducerView.WriteParam(aArg[i]); + } + return aProducerView.Status(); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + size_t arrayLen; + if (!aConsumerView.ReadParam(&arrayLen)) { + return aConsumerView.Status(); + } + + if (aArg && !aArg->AppendElements(arrayLen, fallible)) { + return PcqStatus::PcqOOMError; + } + + for (size_t i = 0; i < arrayLen; ++i) { + ElementType* elt = aArg ? (&aArg->ElementAt(i)) : nullptr; + aConsumerView.ReadParam(elt); + } + return aConsumerView.Status(); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + size_t ret = aView.template MinSizeParam(nullptr); + if (!aArg) { + return ret; + } + + size_t arrayLen = aArg->Length(); + for (size_t i = 0; i < arrayLen; ++i) { + ret += aView.MinSizeParam(&aArg[i]); + } + return ret; + } +}; + +// For ElementTypes that are IsTriviallySerializable +template +struct NSArrayPcqParamTraits, true> { + using ElementType = _ElementType; + using ParamType = nsTArray; + + // TODO: Are there alignment issues? + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + size_t arrayLen = aArg.Length(); + aProducerView.WriteParam(arrayLen); + return aProducerView.Write(&aArg[0], aArg.Length() * sizeof(ElementType)); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + size_t arrayLen; + if (!aConsumerView.ReadParam(&arrayLen)) { + return aConsumerView.Status(); + } + + if (aArg && !aArg->AppendElements(arrayLen, fallible)) { + return PcqStatus::PcqOOMError; + } + + return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType)); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + size_t ret = aView.template MinSizeParam(nullptr); + if (!aArg) { + return ret; + } + + ret += aView.MinSizeBytes(aArg->Length() * sizeof(ElementType)); + return ret; + } +}; + +template +struct PcqParamTraits> + : public NSArrayPcqParamTraits> { + using ParamType = nsTArray; +}; + +// --------------------------------------------------------------- + +template ::value> +struct ArrayPcqParamTraits; + +// For ElementTypes that are !IsTriviallySerializable +template +struct ArrayPcqParamTraits, false> { + using ElementType = _ElementType; + using ParamType = Array; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + for (size_t i = 0; i < Length; ++i) { + aProducerView.WriteParam(aArg[i]); + } + return aProducerView.Status(); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + for (size_t i = 0; i < Length; ++i) { + ElementType* elt = aArg ? (&((*aArg)[i])) : nullptr; + aConsumerView.ReadParam(elt); + } + return aConsumerView.Status(); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + size_t ret = 0; + for (size_t i = 0; i < Length; ++i) { + ret += aView.MinSizeParam(&((*aArg)[i])); + } + return ret; + } +}; + +// For ElementTypes that are IsTriviallySerializable +template +struct ArrayPcqParamTraits, true> { + using ElementType = _ElementType; + using ParamType = Array; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + return aProducerView.Write(aArg.begin(), sizeof(ElementType[Length])); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length])); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + return aView.MinSizeBytes(sizeof(ElementType[Length])); + } +}; + +template +struct PcqParamTraits> + : public ArrayPcqParamTraits> { + using ParamType = Array; +}; + +// --------------------------------------------------------------- + +template +struct PcqParamTraits> { + using ParamType = Maybe; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(aArg.mIsSome); + return (aArg.mIsSome) ? aProducerView.WriteParam(aArg.ref()) + : aProducerView.Status(); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isSome; + if (!aConsumerView.ReadParam(&isSome)) { + return aConsumerView.Status(); + } + + if (!isSome) { + if (aArg) { + aArg->reset(); + } + return PcqStatus::Success; + } + + if (!aArg) { + return aConsumerView.ReadParam(nullptr); + } + + aArg->emplace(); + return aConsumerView.ReadParam(aArg->ptr()); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + return aView.template MinSizeParam(nullptr) + + ((aArg && aArg->isSome()) ? aView.MinSizeParam(&aArg->ref()) : 0); + } +}; + +// --------------------------------------------------------------- + +// Maybe needs special behavior since Variant is not default +// constructable. The Variant's first type must be default constructible. +template +struct PcqParamTraits>> { + using ParamType = Maybe>; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(aArg.mIsSome); + return (aArg.mIsSome) ? aProducerView.WriteParam(aArg.ref()) + : aProducerView.Status(); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isSome; + if (!aConsumerView.ReadParam(&isSome)) { + return aConsumerView.Status(); + } + + if (!isSome) { + if (aArg) { + aArg->reset(); + } + return PcqStatus::Success; + } + + if (!aArg) { + return aConsumerView.ReadParam>(nullptr); + } + + aArg->emplace(VariantType()); + return aConsumerView.ReadParam(aArg->ptr()); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + return aView.template MinSizeParam(nullptr) + + ((aArg && aArg->isSome()) ? aView.MinSizeParam(&aArg->ref()) : 0); + } +}; + +// --------------------------------------------------------------- + +template +struct PcqParamTraits> { + using ParamType = std::pair; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(aArg.first); + return aProducerView.WriteParam(aArg.second); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + aConsumerView.ReadParam(aArg ? (&aArg->first) : nullptr); + return aConsumerView.ReadParam(aArg ? (&aArg->second) : nullptr); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + return aView.MinSizeParam(aArg ? aArg->first : nullptr) + + aView.MinSizeParam(aArg ? aArg->second : nullptr); + } +}; + +// --------------------------------------------------------------- + +template +struct PcqParamTraits> { + using ParamType = UniquePtr; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + // TODO: Clean up move with PCQ + aProducerView.WriteParam(!static_cast(aArg)); + if (aArg && aProducerView.WriteParam(*aArg.get())) { + const_cast(aArg).reset(); + } + return aProducerView.Status(); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isNull; + if (!aConsumerView.ReadParam(&isNull)) { + return aConsumerView.Status(); + } + if (isNull) { + if (aArg) { + aArg->reset(nullptr); + } + return PcqStatus::Success; + } + + T* obj = nullptr; + if (aArg) { + obj = new T(); + if (!obj) { + return PcqStatus::PcqOOMError; + } + aArg->reset(obj); + } + return aConsumerView.ReadParam(obj); + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + if ((!aArg) || (!aArg->get())) { + return aView.template MinSizeParam(nullptr); + } + return aView.template MinSizeParam(nullptr) + + aView.MinSizeParam(aArg->get()); + } +}; + +// --------------------------------------------------------------- + +// Both the Producer and the Consumer are required to maintain (i.e. close) +// the FileDescriptor themselves. The PCQ does not do this for you, nor does +// it use FileDescriptor::auto_close. +#if defined(OS_WIN) +template <> +struct IsTriviallySerializable : std::true_type {}; +#elif defined(OS_POSIX) +// SharedMemoryHandle is typedefed to base::FileDescriptor +template <> +struct PcqParamTraits { + using ParamType = base::FileDescriptor; + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + // PCQs don't use auto-close. + // Convert any negative (i.e. invalid) fds to -1, as done with ParamTraits + // (TODO: why?) + return aProducerView.WriteParam(aArg.fd > 0 ? aArg.fd : -1); + } + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + int fd; + if (!aConsumerView.ReadParam(aArg ? &fd : nullptr)) { + return aConsumerView.Status(); + } + + if (aArg) { + aArg->fd = fd; + aArg->auto_close = false; // PCQs don't use auto-close. + } + return PcqStatus::Success; + } + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + return aView.MinSizeParam(aArg ? &aArg->fd : nullptr); + } +}; +#endif + +// --------------------------------------------------------------- + +// C++ does not allow this struct with a templated method to be local to +// another struct (PcqParamTraits>) so we put it here. +struct PcqVariantWriter { + ProducerView& mView; + template + PcqStatus match(const T& x) { + return mView.WriteParam(x); + } +}; + +template +struct PcqParamTraits> { + using ParamType = Variant; + using Tag = typename mozilla::detail::VariantTag::Type; + + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(aArg.tag); + return aArg.match(PcqVariantWriter{aProducerView}); + } + + // Check the N-1th tag. See ParamTraits for details. + template + struct VariantReader { + using Next = VariantReader; + static PcqStatus Read(ConsumerView& aView, Tag aTag, ParamType* aArg) { + if (aTag == N - 1) { + using EntryType = typename mozilla::detail::Nth::Type; + if (aArg) { + return aView.ReadParam(static_cast(aArg->ptr())); + } + return aView.ReadParam(); + } + return Next::Read(aView, aTag, aArg); + } + }; + + template + struct VariantReader<0, dummy> { + static PcqStatus Read(ConsumerView& aView, Tag aTag, ParamType* aArg) { + MOZ_ASSERT_UNREACHABLE("Tag wasn't for an entry in this Variant"); + return PcqStatus::PcqFatalError; + } + }; + + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + Tag tag; + if (!aConsumerView.ReadParam(&tag)) { + return aConsumerView.Status(); + } + if (aArg) { + aArg->tag = tag; + } + return VariantReader::Read(aConsumerView, tag, aArg); + } + + // Get the min size of the given variant or get the min size of all of the + // variant's types. + template + struct MinSizeVariant { + using Next = MinSizeVariant; + static size_t MinSize(View& aView, const Tag* aTag, const ParamType* aArg) { + using EntryType = typename mozilla::detail::Nth::Type; + if (!aArg) { + return std::min(aView.template MinSizeParam(), + Next::MinSize(aView, aTag, aArg)); + } + MOZ_ASSERT(aTag); + if (*aTag == N - 1) { + return aView.MinSizeParam(&aArg->template as()); + } + return Next::MinSize(aView, aTag, aArg); + } + }; + + template + struct MinSizeVariant<0, View> { + // We've reached the end of the type list. We will legitimately get here + // when calculating MinSize for a null Variant. + static size_t MinSize(View& aView, const Tag* aTag, const ParamType* aArg) { + if (!aArg) { + return 0; + } + MOZ_ASSERT_UNREACHABLE("Tag wasn't for an entry in this Variant"); + return 0; + } + }; + + template + static size_t MinSize(View& aView, const ParamType* aArg) { + const Tag* tag = aArg ? &aArg->tag : nullptr; + return aView.MinSizeParam(tag) + + MinSizeVariant::MinSize(aView, tag, aArg); + } +}; + +} // namespace webgl } // namespace mozilla #endif // mozilla_ipc_ProducerConsumerQueue_h diff --git a/dom/canvas/QueueParamTraits.h b/dom/canvas/QueueParamTraits.h deleted file mode 100644 index 153045ee1a7f..000000000000 --- a/dom/canvas/QueueParamTraits.h +++ /dev/null @@ -1,1183 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: sw=2 ts=4 et : - */ -/* 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 _QUEUEPARAMTRAITS_H_ -#define _QUEUEPARAMTRAITS_H_ 1 - -#include "mozilla/ipc/SharedMemoryBasic.h" -#include "mozilla/Assertions.h" -#include "mozilla/ipc/Shmem.h" -#include "mozilla/ipc/ProtocolUtils.h" -#include "mozilla/Logging.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/TypeTraits.h" -#include "nsString.h" - -namespace IPC { -typedef uint32_t PcqTypeInfoID; -template -struct PcqTypeInfo; -} // namespace IPC - -namespace mozilla { -namespace webgl { - -using IPC::PcqTypeInfo; -using IPC::PcqTypeInfoID; - -struct QueueStatus { - enum EStatus { - // Operation was successful - kSuccess, - // The operation failed because the queue isn't ready for it. - // Either the queue is too full for an insert or too empty for a remove. - // The operation may succeed if retried. - kNotReady, - // The operation was typed and the type check failed. - kTypeError, - // The operation required more room than the queue supports. - // It should not be retried -- it will always fail. - kTooSmall, - // The operation failed for some reason that is unrecoverable. - // All values below this value indicate a fata error. - kFatalError, - // Fatal error: Internal processing ran out of memory. This is likely e.g. - // during de-serialization. - kOOMError, - } mValue; - - MOZ_IMPLICIT QueueStatus(const EStatus status = kSuccess) : mValue(status) {} - explicit operator bool() const { return mValue == kSuccess; } - explicit operator int() const { return static_cast(mValue); } - bool operator==(const EStatus& o) const { return mValue == o; } - bool operator!=(const EStatus& o) const { return !(*this == o); } -}; - -inline bool IsSuccess(QueueStatus status) { - return status == QueueStatus::kSuccess; -} - -template -struct RemoveCVR { - typedef typename std::remove_reference::type>::type - Type; -}; - -inline size_t UsedBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) { - return (aRead <= aWrite) ? aWrite - aRead - : (aQueueBufferSize - aRead) + aWrite; -} - -inline size_t FreeBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) { - // Remember, queueSize is queueBufferSize-1 - return (aQueueBufferSize - 1) - UsedBytes(aQueueBufferSize, aRead, aWrite); -} - -template -struct IsTriviallySerializable - : public std::integral_constant::value || - std::is_arithmetic::value> {}; - -class ProducerConsumerQueue; -class PcqProducer; -class PcqConsumer; - -/** - * QueueParamTraits provide the user with a way to implement PCQ argument - * (de)serialization. It uses a PcqView, which permits the system to - * abandon all changes to the underlying PCQ if any operation fails. - * - * The transactional nature of PCQ operations make the ideal behavior a bit - * complex. Since the PCQ has a fixed amount of memory available to it, - * TryInsert operations operations are expected to sometimes fail and be - * re-issued later. We want these failures to be inexpensive. The same - * goes for TryPeek/TryRemove, which fail when there isn't enough data in - * the queue yet for them to complete. - * - * QueueParamTraits resolve this problem by allowing the Try... operations to - * use QueueParamTraits::Type>::MinSize() to get a - * lower-bound on the amount of room in the queue required for Arg. If the - * operation needs more than is available then the operation quickly fails. - * Otherwise, (de)serialization will commence, although it may still fail if - * MinSize() was too low. - * - * Their expected interface is: - * - * template<> struct QueueParamTraits::Type> { - * // Write data from aArg into the PCQ. It is an error to write less than - * // is reported by MinSize(aArg). - * * static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg) - * {...}; - * - * // Read data from the PCQ into aArg, or just skip the data if aArg is null. - * // It is an error to read less than is reported by MinSize(aArg). - * * static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...} - * - * // The minimum number of bytes needed to represent this object in the - * queue. - * // It is intended to be a very fast estimate but most cases can easily - * // compute the exact value. - * // If aArg is null then this should be the minimum ever required (it is - * only - * // null when checking for deserialization, since the argument is obviously - * // not yet available). It is an error for the queue to require less room - * // than MinSize() reports. A MinSize of 0 is always valid (albeit - * wasteful). static size_t MinSize(const Arg* aArg) {...} - * }; - */ -template -struct QueueParamTraits; - -// Provides type-checking for queue parameters. -template -struct PcqTypedArg { - explicit PcqTypedArg(const Arg& aArg) : mWrite(&aArg), mRead(nullptr) {} - explicit PcqTypedArg(Arg* aArg) : mWrite(nullptr), mRead(aArg) {} - - private: - friend struct QueueParamTraits>; - const Arg* mWrite; - Arg* mRead; -}; - -/** - * The marshaller handles all data insertion into the queue. - */ -class Marshaller { - public: - static QueueStatus WriteObject(uint8_t* aQueue, size_t aQueueBufferSize, - size_t aRead, size_t* aWrite, const void* aArg, - size_t aArgLength) { - const uint8_t* buf = reinterpret_cast(aArg); - if (FreeBytes(aQueueBufferSize, aRead, *aWrite) < aArgLength) { - return QueueStatus::kNotReady; - } - - if (*aWrite + aArgLength <= aQueueBufferSize) { - memcpy(aQueue + *aWrite, buf, aArgLength); - } else { - size_t firstLen = aQueueBufferSize - *aWrite; - memcpy(aQueue + *aWrite, buf, firstLen); - memcpy(aQueue, &buf[firstLen], aArgLength - firstLen); - } - *aWrite = (*aWrite + aArgLength) % aQueueBufferSize; - return QueueStatus::kSuccess; - } - - // The PcqBase must belong to a Consumer. - static QueueStatus ReadObject(const uint8_t* aQueue, size_t aQueueBufferSize, - size_t* aRead, size_t aWrite, void* aArg, - size_t aArgLength) { - if (UsedBytes(aQueueBufferSize, *aRead, aWrite) < aArgLength) { - return QueueStatus::kNotReady; - } - - if (aArg) { - uint8_t* buf = reinterpret_cast(aArg); - if (*aRead + aArgLength <= aQueueBufferSize) { - memcpy(buf, aQueue + *aRead, aArgLength); - } else { - size_t firstLen = aQueueBufferSize - *aRead; - memcpy(buf, aQueue + *aRead, firstLen); - memcpy(&buf[firstLen], aQueue, aArgLength - firstLen); - } - } - - *aRead = (*aRead + aArgLength) % aQueueBufferSize; - return QueueStatus::kSuccess; - } -}; - -/** - * Used to give QueueParamTraits a way to write to the Producer without - * actually altering it, in case the transaction fails. - * THis object maintains the error state of the transaction and - * discards commands issued after an error is encountered. - */ -template -class ProducerView { - public: - using Producer = _Producer; - - ProducerView(Producer* aProducer, size_t aRead, size_t* aWrite) - : mProducer(aProducer), - mRead(aRead), - mWrite(aWrite), - mStatus(QueueStatus::kSuccess) {} - - /** - * Write bytes from aBuffer to the producer if there is enough room. - * aBufferSize must not be 0. - */ - inline QueueStatus Write(const void* aBuffer, size_t aBufferSize); - - template - inline QueueStatus Write(const T* src, size_t count) { - return Write(reinterpret_cast(src), count * sizeof(T)); - } - - /** - * Serialize aArg using Arg's QueueParamTraits. - */ - template - QueueStatus WriteParam(const Arg& aArg) { - return mozilla::webgl::QueueParamTraits< - typename RemoveCVR::Type>::Write(*this, aArg); - } - - /** - * Serialize aArg using Arg's QueueParamTraits and PcqTypeInfo. - */ - template - QueueStatus WriteTypedParam(const Arg& aArg) { - return mozilla::webgl::QueueParamTraits>::Write( - *this, PcqTypedArg(aArg)); - } - - /** - * MinSize of Arg using QueueParamTraits. - */ - template - size_t MinSizeParam(const Arg* aArg = nullptr) { - return mozilla::webgl::QueueParamTraits< - typename RemoveCVR::Type>::MinSize(*this, aArg); - } - - inline size_t MinSizeBytes(size_t aNBytes); - - QueueStatus GetStatus() { return mStatus; } - - private: - Producer* mProducer; - size_t mRead; - size_t* mWrite; - QueueStatus mStatus; -}; - -/** - * Used to give QueueParamTraits a way to read from the Consumer without - * actually altering it, in case the transaction fails. - */ -template -class ConsumerView { - public: - using Consumer = _Consumer; - - ConsumerView(Consumer* aConsumer, size_t* aRead, size_t aWrite) - : mConsumer(aConsumer), - mRead(aRead), - mWrite(aWrite), - mStatus(QueueStatus::kSuccess) {} - - // When reading raw memory blocks, we may get an error, a shared memory - // object that we take ownership of, or a pointer to a block of - // memory that is only guaranteed to exist as long as the ReadVariant - // call. - using PcqReadBytesVariant = - Variant, void*>; - - /** - * Read bytes from the consumer if there is enough data. aBuffer may - * be null (in which case the data is skipped) - */ - inline QueueStatus Read(void* aBuffer, size_t aBufferSize); - - template - inline QueueStatus Read(T* dest, size_t count) { - return Read(reinterpret_cast(dest), count * sizeof(T)); - } - - /** - * Calls a Matcher that returns a QueueStatus when told that the next bytes - * are in the queue or are in shared memory. - * - * The matcher looks like this: - * struct MyMatcher { - * QueueStatus operator()(RefPtr& x) { - * Read or copy x; take responsibility for closing x. - * } - * QueueStatus operator()() { Data is in queue. Use ConsumerView::Read. } - * }; - * - * The only reason to use this instead of Read is if it is important to - * get the data without copying "large" items. Few things are large - * enough to bother. - */ - template - inline QueueStatus ReadVariant(size_t aBufferSize, Matcher&& aMatcher); - - /** - * Deserialize aArg using Arg's QueueParamTraits. - * If the return value is not Success then aArg is not changed. - */ - template - QueueStatus ReadParam(Arg* aArg = nullptr) { - return mozilla::webgl::QueueParamTraits< - typename RemoveCVR::Type>::Read(*this, aArg); - } - - /** - * Deserialize aArg using Arg's QueueParamTraits and PcqTypeInfo. - * If the return value is not Success then aArg is not changed. - */ - template - QueueStatus ReadTypedParam(Arg* aArg = nullptr) { - return mozilla::webgl::QueueParamTraits>::Read( - *this, PcqTypedArg(aArg)); - } - - /** - * MinSize of Arg using QueueParamTraits. aArg may be null. - */ - template - size_t MinSizeParam(Arg* aArg = nullptr) { - return mozilla::webgl::QueueParamTraits< - typename RemoveCVR::Type>::MinSize(*this, aArg); - } - - inline size_t MinSizeBytes(size_t aNBytes); - - QueueStatus GetStatus() { return mStatus; } - - private: - Consumer* mConsumer; - size_t* mRead; - size_t mWrite; - QueueStatus mStatus; -}; - -template -QueueStatus ProducerView::Write(const void* aBuffer, size_t aBufferSize) { - MOZ_ASSERT(aBuffer && (aBufferSize > 0)); - if (!mStatus) { - return mStatus; - } - - if (NeedsSharedMemory(aBufferSize, mProducer->Size())) { - auto smem = MakeRefPtr(); - if (!smem->Create(aBufferSize) || !smem->Map(aBufferSize)) { - return QueueStatus::kFatalError; - } - mozilla::ipc::SharedMemoryBasic::Handle handle; - if (!smem->ShareToProcess(mProducer->mOtherPid, &handle)) { - return QueueStatus::kFatalError; - } - memcpy(smem->memory(), aBuffer, aBufferSize); - smem->CloseHandle(); - return WriteParam(handle); - } - - return mProducer->WriteObject(mRead, mWrite, aBuffer, aBufferSize); -} - -template -size_t ProducerView::MinSizeBytes(size_t aNBytes) { - return NeedsSharedMemory(aNBytes, mProducer->Size()) - ? MinSizeParam((mozilla::ipc::SharedMemoryBasic::Handle*)nullptr) - : aNBytes; -} - -template -QueueStatus ConsumerView::Read(void* aBuffer, size_t aBufferSize) { - struct PcqReadBytesMatcher { - QueueStatus operator()(RefPtr& smem) { - MOZ_ASSERT(smem); - QueueStatus ret; - if (smem->memory()) { - if (mBuffer) { - memcpy(mBuffer, smem->memory(), mBufferSize); - } - ret = QueueStatus::kSuccess; - } else { - ret = QueueStatus::kFatalError; - } - // TODO: Problem: CloseHandle should only be called on the remove/skip - // call. A peek should not CloseHandle! - smem->CloseHandle(); - return ret; - } - QueueStatus operator()() { - return mConsumer.ReadObject(mRead, mWrite, mBuffer, mBufferSize); - } - - Consumer& mConsumer; - size_t* mRead; - size_t mWrite; - void* mBuffer; - size_t mBufferSize; - }; - - MOZ_ASSERT(aBufferSize > 0); - if (!mStatus) { - return mStatus; - } - - return ReadVariant(aBufferSize, - PcqReadBytesMatcher{*(this->mConsumer), mRead, mWrite, - aBuffer, aBufferSize}); -} - -template -template -QueueStatus ConsumerView::ReadVariant(size_t aBufferSize, - Matcher&& aMatcher) { - if (!mStatus) { - return mStatus; - } - - if (NeedsSharedMemory(aBufferSize, mConsumer->Size())) { - // Always read shared-memory -- don't just skip. - mozilla::ipc::SharedMemoryBasic::Handle handle; - if (!ReadParam(&handle)) { - return GetStatus(); - } - - // TODO: Find some way to MOZ_RELEASE_ASSERT that buffersize exactly matches - // what was in queue. This doesn't appear to be possible with the - // information available. - // TODO: This needs to return the same refptr even when peeking/during - // transactions that get aborted/rewound. So this is wrong. - auto sharedMem = MakeRefPtr(); - if (!sharedMem->IsHandleValid(handle) || - !sharedMem->SetHandle(handle, - mozilla::ipc::SharedMemory::RightsReadWrite) || - !sharedMem->Map(aBufferSize)) { - return QueueStatus::kFatalError; - } - return aMatcher(sharedMem); - } - return aMatcher(); -} - -template -size_t ConsumerView::MinSizeBytes(size_t aNBytes) { - return NeedsSharedMemory(aNBytes, mConsumer->Size()) - ? MinSizeParam((mozilla::ipc::SharedMemoryBasic::Handle*)nullptr) - : aNBytes; -} - -// --------------------------------------------------------------- - -template -struct QueueParamTraits> { - using ParamType = PcqTypedArg; - - template ::ID> - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - MOZ_ASSERT(aArg.mWrite); - aProducerView.WriteParam(ArgTypeId); - return aProducerView.WriteParam(*aArg.mWrite); - } - - template ::ID> - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - MOZ_ASSERT(aArg->mRead); - PcqTypeInfoID typeId; - if (!aConsumerView.ReadParam(&typeId)) { - return aConsumerView.GetStatus(); - } - return (typeId == ArgTypeId) ? aConsumerView.ReadParam(aArg) - : QueueStatus::kTypeError; - } - - template - static constexpr size_t MinSize(View& aView, const ParamType* aArg) { - return sizeof(PcqTypeInfoID) + - aView.MinSize(aArg->mWrite ? aArg->mWrite : aArg->mRead); - } -}; - -// --------------------------------------------------------------- - -/** - * True for types that can be (de)serialized by memcpy. - */ -template -struct QueueParamTraits { - template - static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg) { - static_assert(mozilla::webgl::template IsTriviallySerializable::value, - "No QueueParamTraits specialization was found for this type " - "and it does not satisfy IsTriviallySerializable."); - // Write self as binary - return aProducerView.Write(&aArg, sizeof(Arg)); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) { - static_assert(mozilla::webgl::template IsTriviallySerializable::value, - "No QueueParamTraits specialization was found for this type " - "and it does not satisfy IsTriviallySerializable."); - // Read self as binary - return aConsumerView.Read(aArg, sizeof(Arg)); - } - - template - static constexpr size_t MinSize(View& aView, const Arg* aArg) { - static_assert(mozilla::webgl::template IsTriviallySerializable::value, - "No QueueParamTraits specialization was found for this type " - "and it does not satisfy IsTriviallySerializable."); - return sizeof(Arg); - } -}; - -// --------------------------------------------------------------- - -template <> -struct IsTriviallySerializable : std::true_type {}; - -// --------------------------------------------------------------- - -template <> -struct QueueParamTraits { - using ParamType = nsACString; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - if ((!aProducerView.WriteParam(aArg.IsVoid())) || aArg.IsVoid()) { - return aProducerView.GetStatus(); - } - - uint32_t len = aArg.Length(); - if ((!aProducerView.WriteParam(len)) || (len == 0)) { - return aProducerView.GetStatus(); - } - - return aProducerView.Write(aArg.BeginReading(), len); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - bool isVoid = false; - if (!aConsumerView.ReadParam(&isVoid)) { - return aConsumerView.GetStatus(); - } - if (aArg) { - aArg->SetIsVoid(isVoid); - } - if (isVoid) { - return QueueStatus::kSuccess; - } - - uint32_t len = 0; - if (!aConsumerView.ReadParam(&len)) { - return aConsumerView.GetStatus(); - } - - if (len == 0) { - if (aArg) { - *aArg = ""; - } - return QueueStatus::kSuccess; - } - - char* buf = aArg ? new char[len + 1] : nullptr; - if (aArg && (!buf)) { - return QueueStatus::kOOMError; - } - if (!aConsumerView.Read(buf, len)) { - return aConsumerView.GetStatus(); - } - buf[len] = '\0'; - if (aArg) { - aArg->Adopt(buf, len); - } - return QueueStatus::kSuccess; - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - size_t minSize = aView.template MinSizeParam(nullptr); - if ((!aArg) || aArg->IsVoid()) { - return minSize; - } - minSize += aView.template MinSizeParam(nullptr) + - aView.MinSizeBytes(aArg->Length()); - return minSize; - } -}; - -template <> -struct QueueParamTraits { - using ParamType = nsAString; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - if ((!aProducerView.WriteParam(aArg.IsVoid())) || (aArg.IsVoid())) { - return aProducerView.GetStatus(); - } - // DLP: No idea if this includes null terminator - uint32_t len = aArg.Length(); - if ((!aProducerView.WriteParam(len)) || (len == 0)) { - return aProducerView.GetStatus(); - } - constexpr const uint32_t sizeofchar = sizeof(typename ParamType::char_type); - return aProducerView.Write(aArg.BeginReading(), len * sizeofchar); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - bool isVoid = false; - if (!aConsumerView.ReadParam(&isVoid)) { - return aConsumerView.GetStatus(); - } - if (aArg) { - aArg->SetIsVoid(isVoid); - } - if (isVoid) { - return QueueStatus::kSuccess; - } - - // DLP: No idea if this includes null terminator - uint32_t len = 0; - if (!aConsumerView.ReadParam(&len)) { - return aConsumerView.GetStatus(); - } - - if (len == 0) { - if (aArg) { - *aArg = nsString(); - } - return QueueStatus::kSuccess; - } - - uint32_t sizeofchar = sizeof(typename ParamType::char_type); - typename ParamType::char_type* buf = nullptr; - if (aArg) { - buf = static_cast( - malloc((len + 1) * sizeofchar)); - if (!buf) { - return QueueStatus::kOOMError; - } - } - - if (!aConsumerView.Read(buf, len * sizeofchar)) { - return aConsumerView.GetStatus(); - } - - buf[len] = L'\0'; - if (aArg) { - aArg->Adopt(buf, len); - } - - return QueueStatus::kSuccess; - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - size_t minSize = aView.template MinSizeParam(nullptr); - if ((!aArg) || aArg->IsVoid()) { - return minSize; - } - uint32_t sizeofchar = sizeof(typename ParamType::char_type); - minSize += aView.template MinSizeParam(nullptr) + - aView.MinSizeBytes(aArg->Length() * sizeofchar); - return minSize; - } -}; - -template <> -struct QueueParamTraits : public QueueParamTraits { - using ParamType = nsCString; -}; - -template <> -struct QueueParamTraits : public QueueParamTraits { - using ParamType = nsString; -}; - -// --------------------------------------------------------------- - -template ::value> -struct NSArrayQueueParamTraits; - -// For ElementTypes that are !IsTriviallySerializable -template -struct NSArrayQueueParamTraits, false> { - using ElementType = _ElementType; - using ParamType = nsTArray; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - aProducerView.WriteParam(aArg.Length()); - for (auto& elt : aArg) { - aProducerView.WriteParam(elt); - } - return aProducerView.GetStatus(); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - size_t arrayLen; - if (!aConsumerView.ReadParam(&arrayLen)) { - return aConsumerView.GetStatus(); - } - - if (aArg && !aArg->AppendElements(arrayLen, fallible)) { - return QueueStatus::kOOMError; - } - - for (auto i : IntegerRange(arrayLen)) { - ElementType* elt = aArg ? (&aArg->ElementAt(i)) : nullptr; - aConsumerView.ReadParam(elt); - } - return aConsumerView.GetStatus(); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - size_t ret = aView.template MinSizeParam(nullptr); - if (!aArg) { - return ret; - } - - for (auto& elt : aArg) { - ret += aView.MinSizeParam(&elt); - } - return ret; - } -}; - -// For ElementTypes that are IsTriviallySerializable -template -struct NSArrayQueueParamTraits, true> { - using ElementType = _ElementType; - using ParamType = nsTArray; - - // TODO: Are there alignment issues? - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - size_t arrayLen = aArg.Length(); - aProducerView.WriteParam(arrayLen); - return aProducerView.Write(&aArg[0], aArg.Length() * sizeof(ElementType)); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - size_t arrayLen; - if (!aConsumerView.ReadParam(&arrayLen)) { - return aConsumerView.GetStatus(); - } - - if (aArg && !aArg->AppendElements(arrayLen, fallible)) { - return QueueStatus::kOOMError; - } - - return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType)); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - size_t ret = aView.template MinSizeParam(nullptr); - if (!aArg) { - return ret; - } - - ret += aView.MinSizeBytes(aArg->Length() * sizeof(ElementType)); - return ret; - } -}; - -template -struct QueueParamTraits> - : public NSArrayQueueParamTraits> { - using ParamType = nsTArray; -}; - -// --------------------------------------------------------------- - -template ::value> -struct ArrayQueueParamTraits; - -// For ElementTypes that are !IsTriviallySerializable -template -struct ArrayQueueParamTraits, false> { - using ElementType = _ElementType; - using ParamType = Array; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - for (size_t i = 0; i < Length; ++i) { - aProducerView.WriteParam(aArg[i]); - } - return aProducerView.GetStatus(); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - for (size_t i = 0; i < Length; ++i) { - ElementType* elt = aArg ? (&((*aArg)[i])) : nullptr; - aConsumerView.ReadParam(elt); - } - return aConsumerView.GetStatus(); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - size_t ret = 0; - for (size_t i = 0; i < Length; ++i) { - ret += aView.MinSizeParam(&((*aArg)[i])); - } - return ret; - } -}; - -// For ElementTypes that are IsTriviallySerializable -template -struct ArrayQueueParamTraits, true> { - using ElementType = _ElementType; - using ParamType = Array; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - return aProducerView.Write(aArg.begin(), sizeof(ElementType[Length])); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length])); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - return aView.MinSizeBytes(sizeof(ElementType[Length])); - } -}; - -template -struct QueueParamTraits> - : public ArrayQueueParamTraits> { - using ParamType = Array; -}; - -// --------------------------------------------------------------- - -template -struct QueueParamTraits> { - using ParamType = Maybe; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - aProducerView.WriteParam(static_cast(aArg)); - return aArg ? aProducerView.WriteParam(aArg.ref()) - : aProducerView.GetStatus(); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - bool isSome; - if (!aConsumerView.ReadParam(&isSome)) { - return aConsumerView.GetStatus(); - } - - if (!isSome) { - if (aArg) { - aArg->reset(); - } - return QueueStatus::kSuccess; - } - - if (!aArg) { - return aConsumerView.template ReadParam(nullptr); - } - - aArg->emplace(); - return aConsumerView.ReadParam(aArg->ptr()); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - return aView.template MinSizeParam(nullptr) + - ((aArg && aArg->isSome()) ? aView.MinSizeParam(&aArg->ref()) : 0); - } -}; - -// --------------------------------------------------------------- - -// Maybe needs special behavior since Variant is not default -// constructable. The Variant's first type must be default constructible. -template -struct QueueParamTraits>> { - using ParamType = Maybe>; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - aProducerView.WriteParam(aArg.mIsSome); - return (aArg.mIsSome) ? aProducerView.WriteParam(aArg.ref()) - : aProducerView.GetStatus(); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - bool isSome; - if (!aConsumerView.ReadParam(&isSome)) { - return aConsumerView.GetStatus(); - } - - if (!isSome) { - if (aArg) { - aArg->reset(); - } - return QueueStatus::kSuccess; - } - - if (!aArg) { - return aConsumerView.template ReadParam>(nullptr); - } - - aArg->emplace(VariantType()); - return aConsumerView.ReadParam(aArg->ptr()); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - return aView.template MinSizeParam(nullptr) + - ((aArg && aArg->isSome()) ? aView.MinSizeParam(&aArg->ref()) : 0); - } -}; - -// --------------------------------------------------------------- - -template -struct QueueParamTraits> { - using ParamType = std::pair; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - aProducerView.WriteParam(aArg.first()); - return aProducerView.WriteParam(aArg.second()); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - aConsumerView.ReadParam(aArg ? (&aArg->first()) : nullptr); - return aConsumerView.ReadParam(aArg ? (&aArg->second()) : nullptr); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - return aView.MinSizeParam(aArg ? aArg->first() : nullptr) + - aView.MinSizeParam(aArg ? aArg->second() : nullptr); - } -}; - -// --------------------------------------------------------------- - -template -struct QueueParamTraits> { - using ParamType = UniquePtr; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - // TODO: Clean up move with PCQ - aProducerView.WriteParam(!static_cast(aArg)); - if (aArg && aProducerView.WriteParam(*aArg.get())) { - const_cast(aArg).reset(); - } - return aProducerView.GetStatus(); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - bool isNull; - if (!aConsumerView.ReadParam(&isNull)) { - return aConsumerView.GetStatus(); - } - if (isNull) { - if (aArg) { - aArg->reset(nullptr); - } - return QueueStatus::kSuccess; - } - - T* obj = nullptr; - if (aArg) { - obj = new T(); - if (!obj) { - return QueueStatus::kOOMError; - } - aArg->reset(obj); - } - return aConsumerView.ReadParam(obj); - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - if ((!aArg) || (!aArg->get())) { - return aView.template MinSizeParam(nullptr); - } - return aView.template MinSizeParam(nullptr) + - aView.MinSizeParam(aArg->get()); - } -}; - -// --------------------------------------------------------------- - -// Both the Producer and the Consumer are required to maintain (i.e. close) -// the FileDescriptor themselves. The PCQ does not do this for you, nor does -// it use FileDescriptor::auto_close. -#if defined(OS_WIN) -template <> -struct IsTriviallySerializable : std::true_type {}; -#elif defined(OS_POSIX) -// SharedMemoryHandle is typedefed to base::FileDescriptor -template <> -struct QueueParamTraits { - using ParamType = base::FileDescriptor; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - // PCQs don't use auto-close. - // Convert any negative (i.e. invalid) fds to -1, as done with ParamTraits - // (TODO: why?) - return aProducerView.WriteParam(aArg.fd > 0 ? aArg.fd : -1); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - int fd; - if (!aConsumerView.ReadParam(aArg ? &fd : nullptr)) { - return aConsumerView.GetStatus(); - } - - if (aArg) { - aArg->fd = fd; - aArg->auto_close = false; // PCQs don't use auto-close. - } - return QueueStatus::kSuccess; - } - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - return aView.MinSizeParam(aArg ? &aArg->fd : nullptr); - } -}; -#endif - -// --------------------------------------------------------------- - -// C++ does not allow this struct with a templated method to be local to -// another struct (QueueParamTraits>) so we put it here. -template -struct PcqVariantWriter { - ProducerView& mView; - template - QueueStatus match(const T& x) { - return mView.WriteParam(x); - } -}; - -template -struct QueueParamTraits> { - using ParamType = Variant; - using Tag = typename mozilla::detail::VariantTag::Type; - - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { - aProducerView.WriteParam(aArg.tag); - return aArg.match(PcqVariantWriter{aProducerView}); - } - - // Check the N-1th tag. See ParamTraits for details. - template - struct VariantReader { - using Next = VariantReader; - template - static QueueStatus Read(ConsumerView& aView, Tag aTag, ParamType* aArg) { - if (aTag == N - 1) { - using EntryType = typename mozilla::detail::Nth::Type; - if (aArg) { - return aView.ReadParam(static_cast(aArg->ptr())); - } - return aView.template ReadParam(); - } - return Next::Read(aView, aTag, aArg); - } - }; - - template - struct VariantReader<0, dummy> { - template - static QueueStatus Read(ConsumerView& aView, Tag aTag, ParamType* aArg) { - MOZ_ASSERT_UNREACHABLE("Tag wasn't for an entry in this Variant"); - return QueueStatus::kFatalError; - } - }; - - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { - Tag tag; - if (!aConsumerView.ReadParam(&tag)) { - return aConsumerView.GetStatus(); - } - if (aArg) { - aArg->tag = tag; - } - return VariantReader::Read(aConsumerView, tag, aArg); - } - - // Get the min size of the given variant or get the min size of all of the - // variant's types. - template - struct MinSizeVariant { - using Next = MinSizeVariant; - static size_t MinSize(View& aView, const Tag* aTag, const ParamType* aArg) { - using EntryType = typename mozilla::detail::Nth::Type; - if (!aArg) { - return std::min(aView.template MinSizeParam(), - Next::MinSize(aView, aTag, aArg)); - } - MOZ_ASSERT(aTag); - if (*aTag == N - 1) { - return aView.MinSizeParam(&aArg->template as()); - } - return Next::MinSize(aView, aTag, aArg); - } - }; - - template - struct MinSizeVariant<0, View> { - // We've reached the end of the type list. We will legitimately get here - // when calculating MinSize for a null Variant. - static size_t MinSize(View& aView, const Tag* aTag, const ParamType* aArg) { - if (!aArg) { - return 0; - } - MOZ_ASSERT_UNREACHABLE("Tag wasn't for an entry in this Variant"); - return 0; - } - }; - - template - static size_t MinSize(View& aView, const ParamType* aArg) { - const Tag* tag = aArg ? &aArg->tag : nullptr; - return aView.MinSizeParam(tag) + - MinSizeVariant::MinSize(aView, tag, aArg); - } -}; - -} // namespace webgl -} // namespace mozilla - -#endif // _QUEUEPARAMTRAITS_H_ diff --git a/dom/canvas/WebGLChild.cpp b/dom/canvas/WebGLChild.cpp index 23b408cd5b2c..22971168aef1 100644 --- a/dom/canvas/WebGLChild.cpp +++ b/dom/canvas/WebGLChild.cpp @@ -6,7 +6,6 @@ #include "WebGLChild.h" #include "ClientWebGLContext.h" -#include "WebGLMethodDispatcher.h" namespace mozilla { namespace dom { @@ -27,10 +26,5 @@ mozilla::ipc::IPCResult WebGLChild::RecvOnContextLoss( return IPC_OK(); } -/* static */ -bool WebGLChild::ShouldSendSync(size_t aCmd, ...) { - return WebGLMethodDispatcher::SyncType(aCmd) == CommandSyncType::SYNC; -} - } // namespace dom } // namespace mozilla diff --git a/dom/canvas/WebGLChild.h b/dom/canvas/WebGLChild.h index c85f0df7c5be..78342ae118d6 100644 --- a/dom/canvas/WebGLChild.h +++ b/dom/canvas/WebGLChild.h @@ -9,7 +9,6 @@ #include #include "mozilla/dom/PWebGLChild.h" -#include "mozilla/dom/IpdlQueue.h" namespace mozilla { @@ -17,24 +16,15 @@ class ClientWebGLContext; namespace dom { -class WebGLChild final : public PWebGLChild, - public SyncProducerActor, - public AsyncConsumerActor, - public SupportsWeakPtr { +class WebGLChild final : public PWebGLChild { public: - MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLChild) NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebGLChild, override); - using OtherSideActor = WebGLParent; ClientWebGLContext& mContext; explicit WebGLChild(ClientWebGLContext&); - // For SyncProducerActor: - static bool ShouldSendSync(size_t aCmd, ...); - private: - friend PWebGLChild; virtual ~WebGLChild(); public: diff --git a/dom/canvas/WebGLCommandQueue.h b/dom/canvas/WebGLCommandQueue.h index 6bb82cb83f11..e831e6b6c13b 100644 --- a/dom/canvas/WebGLCommandQueue.h +++ b/dom/canvas/WebGLCommandQueue.h @@ -21,12 +21,50 @@ namespace mozilla { using mozilla::ipc::IPDLParamTraits; -using mozilla::webgl::QueueStatus; +using mozilla::webgl::Consumer; +using mozilla::webgl::PcqStatus; +using mozilla::webgl::Producer; +using mozilla::webgl::ProducerConsumerQueue; -enum CommandResult { kSuccess, kTimeExpired, kQueueEmpty, kError }; +template +struct MethodDispatcher; + +enum CommandResult { Success, TimeExpired, QueueEmpty, Error }; enum CommandSyncType { ASYNC, SYNC }; +class BasicSource { + public: + explicit BasicSource(UniquePtr&& aProducer) + : mProducer(std::move(aProducer)) { + MOZ_ASSERT(mProducer); + } + virtual ~BasicSource() = default; + + // For IPDL: + BasicSource() = default; + friend struct mozilla::ipc::IPDLParamTraits; + + protected: + UniquePtr mProducer; +}; + +class BasicSink { + public: + explicit BasicSink(UniquePtr&& aConsumer) + : mConsumer(std::move(aConsumer)) { + MOZ_ASSERT(mConsumer); + } + virtual ~BasicSink() = default; + + // For IPDL: + BasicSink() = default; + friend struct mozilla::ipc::IPDLParamTraits; + + protected: + UniquePtr mConsumer; +}; + /** * A CommandSource is obtained from a CommandQueue. Use it by inserting a * command (represented by type Command) using InsertCommand, which also @@ -34,38 +72,30 @@ enum CommandSyncType { ASYNC, SYNC }; * to the CommandSink, which must understand the Command+Args combination * to execute it. */ -template -class CommandSource { - using Source = _Source; - +template +class CommandSource : public BasicSource { public: - explicit CommandSource(UniquePtr&& aSource) - : mSource(std::move(aSource)) { - MOZ_ASSERT(mSource); + explicit CommandSource(UniquePtr&& aProducer) + : BasicSource(std::move(aProducer)) {} + + template + PcqStatus InsertCommand(Command aCommand, Args&&... aArgs) { + return this->mProducer->TryWaitInsert(Nothing() /* wait forever */, + aCommand, aArgs...); + } + + PcqStatus InsertCommand(Command aCommand) { + return this->mProducer->TryWaitInsert(Nothing() /* wait forever */, + aCommand); } template - QueueStatus InsertCommand(Command aCommand, Args&&... aArgs) { - return this->mSource->TryWaitInsert(Nothing() /* wait forever */, aCommand, - aArgs...); - } - - QueueStatus InsertCommand(Command aCommand) { - return this->mSource->TryWaitInsert(Nothing() /* wait forever */, aCommand); - } - - template - QueueStatus RunCommand(Command aCommand, Args&&... aArgs) { + PcqStatus RunCommand(Command aCommand, Args&&... aArgs) { return InsertCommand(aCommand, std::forward(aArgs)...); } // For IPDL: CommandSource() = default; - - protected: - friend struct IPDLParamTraits>; - - UniquePtr mSource; }; /** @@ -78,39 +108,36 @@ class CommandSource { * easily run functions and methods using arguments taken from the command * queue by calling the Dispatch methods in this class. */ -template -class CommandSink { - using Sink = _Sink; - +template +class CommandSink : public BasicSink { public: - explicit CommandSink(UniquePtr&& aSink) : mSink(std::move(aSink)) { - MOZ_ASSERT(mSink); - } + explicit CommandSink(UniquePtr&& aConsumer) + : BasicSink(std::move(aConsumer)) {} /** * Attempts to process the next command in the queue, if one is available. */ CommandResult ProcessOne(const Maybe& aTimeout) { Command command; - QueueStatus status = (aTimeout.isNothing() || aTimeout.value()) - ? this->mSink->TryWaitRemove(aTimeout, command) - : this->mSink->TryRemove(command); + PcqStatus status = (aTimeout.isNothing() || aTimeout.value()) + ? this->mConsumer->TryWaitRemove(aTimeout, command) + : this->mConsumer->TryRemove(command); - if (status == QueueStatus::kSuccess) { + if (status == PcqStatus::Success) { if (DispatchCommand(command)) { - return CommandResult::kSuccess; + return CommandResult::Success; } - return CommandResult::kError; + return CommandResult::Error; } - if (status == QueueStatus::kNotReady) { - return CommandResult::kQueueEmpty; + if (status == PcqStatus::PcqNotReady) { + return CommandResult::QueueEmpty; } - if (status == QueueStatus::kOOMError) { + if (status == PcqStatus::PcqOOMError) { ReportOOM(); } - return CommandResult::kError; + return CommandResult::Error; } CommandResult ProcessOneNow() { return ProcessOne(Some(TimeDuration(0))); } @@ -125,7 +152,7 @@ class CommandSink { CommandResult result; do { result = ProcessOneNow(); - } while (result == CommandResult::kSuccess); + } while (result == CommandResult::Success); return result; } @@ -142,8 +169,7 @@ class CommandSink { do { result = ProcessOne(Some(aDuration - (now - start))); now = TimeStamp::Now(); - } while ((result == CommandResult::kSuccess) && - ((now - start) < aDuration)); + } while ((result == CommandResult::Success) && ((now - start) < aDuration)); return result; } @@ -198,8 +224,6 @@ class CommandSink { } protected: - friend struct IPDLParamTraits>; - /** * Implementations will usually be something like a big switch statement * that calls one of the Dispatch methods in this class. @@ -213,27 +237,26 @@ class CommandSink { virtual void ReportOOM() {} template - QueueStatus CallTryRemove(std::tuple& aArgs, - std::index_sequence) { - QueueStatus status = mSink->TryRemove(std::get(aArgs)...); + PcqStatus CallTryRemove(std::tuple& aArgs, + std::index_sequence) { + PcqStatus status = mConsumer->TryRemove(std::get(aArgs)...); // The CommandQueue inserts the command and the args together as an atomic // operation. We already read the command so the args must also be // available. - MOZ_ASSERT(status != QueueStatus::kNotReady); + MOZ_ASSERT(status != PcqStatus::PcqNotReady); return status; } - QueueStatus CallTryRemove(std::tuple<>& aArgs, - std::make_integer_sequence) { - return QueueStatus::kSuccess; + PcqStatus CallTryRemove(std::tuple<>& aArgs, + std::make_integer_sequence) { + return PcqStatus::Success; } template ::ReturnType> - ReturnType CallMethod(T& aObj, MethodType aMethod, std::tuple& aArgs, - std::index_sequence) { + size_t... Indices, typename ReturnType> + std::result_of CallMethod(T& aObj, MethodType aMethod, + std::tuple& aArgs, + std::index_sequence) { return (aObj.*aMethod)(std::forward(std::get(aArgs))...); } @@ -246,110 +269,96 @@ class CommandSink { template bool ReadArgs(std::tuple& aArgs) { - QueueStatus status = - CallTryRemove(aArgs, std::index_sequence_for{}); + PcqStatus status = CallTryRemove(aArgs, std::index_sequence_for{}); return IsSuccess(status); } - - UniquePtr mSink; }; enum SyncResponse : uint8_t { RESPONSE_NAK, RESPONSE_ACK }; /** - * This is the Source for a SyncCommandSink. It takes an extra queue, - * the ResponseQueue, and uses it to receive synchronous responses from - * the sink. The ResponseQueue is a regular queue, not a CommandQueue. + * This is the Source for a SyncCommandSink. It takes an extra PCQ, + * the ResponsePcq, and uses it to receive synchronous responses from + * the sink. The ResponsePcq is a regular ProducerConsumerQueue, + * not a CommandQueue. */ -template -class SyncCommandSource : public CommandSource { +template +class SyncCommandSource : public CommandSource { public: - using BaseType = CommandSource; - using Source = _Source; - using ResponseQueue = _ResponseQueue; - using ResponseSink = typename ResponseQueue::Consumer; - - SyncCommandSource(UniquePtr&& aSource, - UniquePtr&& aResponseSink) - : CommandSource(std::move(aSource)), - mResponseSink(std::move(aResponseSink)) {} + using BaseType = CommandSource; + SyncCommandSource(UniquePtr&& aProducer, + UniquePtr&& aResponseConsumer) + : CommandSource(std::move(aProducer)), + mConsumer(std::move(aResponseConsumer)) {} template - QueueStatus RunAsyncCommand(Command aCommand, Args&&... aArgs) { + PcqStatus RunAsyncCommand(Command aCommand, Args&&... aArgs) { return this->RunCommand(aCommand, std::forward(aArgs)...); } template - QueueStatus RunVoidSyncCommand(Command aCommand, Args&&... aArgs) { - QueueStatus status = - RunAsyncCommand(aCommand, std::forward(aArgs)...); + PcqStatus RunVoidSyncCommand(Command aCommand, Args&&... aArgs) { + PcqStatus status = RunAsyncCommand(aCommand, std::forward(aArgs)...); return IsSuccess(status) ? this->ReadSyncResponse() : status; } template - QueueStatus RunSyncCommand(Command aCommand, ResultType& aReturn, - Args&&... aArgs) { - QueueStatus status = + PcqStatus RunSyncCommand(Command aCommand, ResultType& aReturn, + Args&&... aArgs) { + PcqStatus status = RunVoidSyncCommand(aCommand, std::forward(aArgs)...); return IsSuccess(status) ? this->ReadResult(aReturn) : status; } // for IPDL: SyncCommandSource() = default; - friend struct mozilla::ipc::IPDLParamTraits< - SyncCommandSource>; + friend struct mozilla::ipc::IPDLParamTraits>; protected: - QueueStatus ReadSyncResponse() { + PcqStatus ReadSyncResponse() { SyncResponse response; - QueueStatus status = - mResponseSink->TryWaitRemove(Nothing() /* wait forever */, response); - MOZ_ASSERT(status != QueueStatus::kNotReady); + PcqStatus status = + mConsumer->TryWaitRemove(Nothing() /* wait forever */, response); + MOZ_ASSERT(status != PcqStatus::PcqNotReady); if (IsSuccess(status) && response != RESPONSE_ACK) { - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } return status; } template - QueueStatus ReadResult(T& aResult) { - QueueStatus status = mResponseSink->TryRemove(aResult); + PcqStatus ReadResult(T& aResult) { + PcqStatus status = mConsumer->TryRemove(aResult); // The Sink posts the response code and result as an atomic transaction. We // already read the response code so the result must be available. - MOZ_ASSERT(status != QueueStatus::kNotReady); + MOZ_ASSERT(status != PcqStatus::PcqNotReady); return status; } - UniquePtr mResponseSink; + UniquePtr mConsumer; }; /** - * This is the Sink for a SyncCommandSource. It takes an extra queue, the - * ResponseQueue, and uses it to issue synchronous responses to the client. + * This is the Sink for a SyncCommandSource. It takes an extra PCQ, the + * ResponsePcq, and uses it to issue synchronous responses to the client. * Subclasses can use the DispatchSync methods in this class in their * DispatchCommand implementations. - * The ResponseQueue is not a CommandQueue. + * The ResponsePcq is not a CommandQueue. */ -template -class SyncCommandSink : public CommandSink { - using BaseType = CommandSink; - using ResponseQueue = _ResponseQueue; - using Sink = _Sink; - using ResponseSource = typename ResponseQueue::Producer; +template +class SyncCommandSink : public CommandSink { + using BaseType = CommandSink; public: - SyncCommandSink(UniquePtr&& aSink, - UniquePtr&& aResponseSource) - : CommandSink(std::move(aSink)), - mResponseSource(std::move(aResponseSource)) { - MOZ_ASSERT(mResponseSource); - } + SyncCommandSink(UniquePtr&& aConsumer, + UniquePtr&& aResponseProducer) + : CommandSink(std::move(aConsumer)), + mProducer(std::move(aResponseProducer)) {} // for IPDL: SyncCommandSink() = default; - friend struct mozilla::ipc::IPDLParamTraits< - SyncCommandSink>; + friend struct mozilla::ipc::IPDLParamTraits>; // Places RESPONSE_ACK and the typed return value, or RESPONSE_NAK, in // the response queue, @@ -482,7 +491,7 @@ class SyncCommandSink : public CommandSink { protected: template bool WriteArgs(const Args&... aArgs) { - return IsSuccess(mResponseSource->TryInsert(aArgs...)); + return IsSuccess(mProducer->TryInsert(aArgs...)); } template @@ -496,7 +505,25 @@ class SyncCommandSink : public CommandSink { return WriteArgs(nak); } - UniquePtr mResponseSource; + UniquePtr mProducer; +}; + +/** + * Can be used by a sink to find and execute the handler for a given commandId. + */ +template +struct CommandDispatchDriver { + /** + * Find and run the command. + */ + template + static MOZ_ALWAYS_INLINE bool DispatchCommandHelper(size_t aId, + Args&... aArgs) { + if (commandId == aId) { + return Derived::template Dispatch(aArgs...); + } + return Derived::template DispatchCommand(aId, aArgs...); + } }; /** @@ -504,61 +531,48 @@ class SyncCommandSink : public CommandSink { * use to dispatch sync/async commands to a method via a CommandSink. * See DECLARE_METHOD_DISPATCHER and DEFINE_METHOD_DISPATCHER. */ -template +template struct MethodDispatcher { + using SinkType = _SinkType; template struct DispatchMethod; + /* + // Specialization for dispatching asynchronous methods + template + struct DispatchMethod { + template + static MOZ_ALWAYS_INLINE bool Run(SinkType& aSink, MethodType mMethod, + ObjectType& aObj) { + return aSink.DispatchMethod(aObj, mMethod); + } + }; - // Specialization for dispatching asynchronous methods - template <> - struct DispatchMethod { - template - static MOZ_ALWAYS_INLINE bool Run(SinkType& aSink, MethodType mMethod, - ObjectType& aObj) { - return aSink.DispatchAsyncMethod(aObj, mMethod); - } - }; - - // Specialization for dispatching synchronous methods - template <> - struct DispatchMethod { - template - static MOZ_ALWAYS_INLINE bool Run(SinkType& aSink, MethodType aMethod, - ObjectType& aObj) { - return aSink.DispatchSyncMethod(aObj, aMethod); - } - }; + // Specialization for dispatching synchronous methods + template <> + struct DispatchMethod { + template + static MOZ_ALWAYS_INLINE bool Run(SinkType& aSink, MethodType aMethod, + ObjectType& aObj) { + return aSink.DispatchSyncMethod(aObj, aMethod); + } + }; + */ }; // Declares a MethodDispatcher with the given name and CommandSink type. // The ObjectType is the type of the object this class will dispatch methods to. -#define DECLARE_METHOD_DISPATCHER(_DISPATCHER, _OBJECTTYPE) \ - struct _DISPATCHER : public MethodDispatcher<_DISPATCHER> { \ +#define DECLARE_METHOD_DISPATCHER(_DISPATCHER, _SINKTYPE, _OBJECTTYPE) \ + struct _DISPATCHER : public MethodDispatcher<_DISPATCHER, _SINKTYPE> { \ using ObjectType = _OBJECTTYPE; \ - template \ - struct IdDispatcher { \ - template \ - static MOZ_ALWAYS_INLINE bool DispatchCommand(size_t aId, \ - SinkType& aSink, \ - ObjectType& aObj) { \ - MOZ_CRASH("Impossible -- Unhandled command ID"); \ - return false; \ - } \ - static MOZ_ALWAYS_INLINE CommandSyncType SyncType(size_t aId) { \ - MOZ_ASSERT_UNREACHABLE("Impossible -- Unhandled command ID"); \ - return CommandSyncType::ASYNC; \ - } \ - }; \ - template \ + template \ static MOZ_ALWAYS_INLINE bool DispatchCommand(size_t aId, SinkType& aSink, \ ObjectType& aObj) { \ - return IdDispatcher::DispatchCommand(aId, aSink, aObj); \ - } \ - template \ - static MOZ_ALWAYS_INLINE CommandSyncType SyncType(size_t aId) { \ - return IdDispatcher::SyncType(aId); \ + MOZ_ASSERT_UNREACHABLE("Unhandled command ID"); \ + return false; \ } \ template \ + static MOZ_ALWAYS_INLINE bool Dispatch(SinkType& aSink, ObjectType& aObj); \ + template \ struct MethodInfo; \ template \ static constexpr CommandSyncType SyncType(); \ @@ -570,111 +584,127 @@ struct MethodDispatcher { // id. The handler uses a CommandSink to read parameters, call the // given method using the given synchronization protocol, and provide // compile-time lookup of the ID by class method. -#define DEFINE_METHOD_DISPATCHER(_DISPATCHER, _ID, _METHOD, _SYNC) \ - template <> \ - struct _DISPATCHER::MethodInfo<_ID> { \ - using MethodType = decltype(&_METHOD); \ - }; \ - template <> \ - constexpr CommandSyncType _DISPATCHER::SyncType<_ID>() { \ - return _SYNC; \ - } \ - template <> \ - constexpr size_t _DISPATCHER::Id() { \ - return _ID; \ - } \ - template <> \ - struct _DISPATCHER::IdDispatcher<_ID> { \ - template \ - static MOZ_ALWAYS_INLINE bool DispatchCommand(size_t aId, SinkType& aSink, \ - ObjectType& aObj) { \ - return (_ID == aId) ? DispatchMethod<_SYNC>::Run(aSink, &_METHOD, aObj) \ - : _DISPATCHER::DispatchCommand( \ - aId, aSink, aObj); \ - } \ - static MOZ_ALWAYS_INLINE CommandSyncType SyncType(size_t aId) { \ - return (_ID == aId) ? _DISPATCHER::SyncType<_ID>() \ - : _DISPATCHER::SyncType<_ID + 1>(aId); \ - } \ - }; +#define DEFINE_METHOD_DISPATCHER(_DISPATCHER, _ID, _METHOD, _SYNC) \ + /* template <> \ + bool _DISPATCHER::DispatchCommand<_ID>(size_t aId, SinkType & aSink, \ + ObjectType & aObj) { \ + return CommandDispatchDriver<_DISPATCHER>::DispatchCommandHelper<_ID>( \ + aId, aSink, aObj); \ + } \ + template <> \ + bool _DISPATCHER::Dispatch<_ID>(SinkType & aSink, ObjectType & aObj) { \ + return DispatchMethod<_SYNC>::Run(aSink, &_METHOD, aObj); \ + } */ \ + template <> \ + struct _DISPATCHER::MethodInfo<_ID> { \ + using MethodType = decltype(&_METHOD); \ + }; \ + template <> \ + constexpr CommandSyncType _DISPATCHER::SyncType<_ID>() { \ + return _SYNC; \ + } \ + template <> \ + constexpr size_t _DISPATCHER::Id() { \ + return _ID; \ + } namespace ipc { template struct IPDLParamTraits; -template -struct IPDLParamTraits> { +template <> +struct IPDLParamTraits { public: - typedef mozilla::CommandSource paramType; + typedef mozilla::BasicSource paramType; static void Write(IPC::Message* aMsg, IProtocol* aActor, const paramType& aParam) { - WriteIPDLParam(aMsg, aActor, aParam.mSource); + MOZ_ASSERT(aParam.mProducer); + WriteIPDLParam(aMsg, aActor, *aParam.mProducer.get()); } static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor, paramType* aResult) { - return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSource); + Producer* producer = new Producer; + bool ret = ReadIPDLParam(aMsg, aIter, aActor, producer); + aResult->mProducer.reset(producer); + return ret; } }; -template -struct IPDLParamTraits> { +template <> +struct IPDLParamTraits { public: - typedef mozilla::CommandSink paramType; + typedef mozilla::BasicSink paramType; static void Write(IPC::Message* aMsg, IProtocol* aActor, const paramType& aParam) { - WriteIPDLParam(aMsg, aActor, aParam.mSink); + MOZ_ASSERT(aParam.mConsumer); + WriteIPDLParam(aMsg, aActor, *aParam.mConsumer.get()); } static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor, paramType* aResult) { - return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSink); + Consumer* consumer = new Consumer; + bool ret = ReadIPDLParam(aMsg, aIter, aActor, consumer); + aResult->mConsumer.reset(consumer); + return ret; } }; -template -struct IPDLParamTraits< - mozilla::SyncCommandSource> - : public IPDLParamTraits> { +template +struct IPDLParamTraits> + : public IPDLParamTraits { public: - typedef mozilla::SyncCommandSource paramType; + typedef mozilla::CommandSource paramType; +}; + +template +struct IPDLParamTraits> + : public IPDLParamTraits { + public: + typedef mozilla::CommandSink paramType; +}; + +template +struct IPDLParamTraits> + : public IPDLParamTraits> { + public: + typedef mozilla::SyncCommandSource paramType; typedef typename paramType::BaseType paramBaseType; static void Write(IPC::Message* aMsg, IProtocol* aActor, const paramType& aParam) { WriteIPDLParam(aMsg, aActor, static_cast(aParam)); - WriteIPDLParam(aMsg, aActor, aParam.mResponseSink); + WriteIPDLParam(aMsg, aActor, aParam.mConsumer); } static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor, paramType* aParam) { bool result = ReadIPDLParam(aMsg, aIter, aActor, static_cast(aParam)); - return result && ReadIPDLParam(aMsg, aIter, aActor, &aParam->mResponseSink); + return result && ReadIPDLParam(aMsg, aIter, aActor, &aParam->mConsumer); } }; -template -struct IPDLParamTraits> - : public IPDLParamTraits> { +template +struct IPDLParamTraits> + : public IPDLParamTraits> { public: - typedef mozilla::SyncCommandSink paramType; + typedef mozilla::SyncCommandSink paramType; typedef typename paramType::BaseType paramBaseType; static void Write(IPC::Message* aMsg, IProtocol* aActor, const paramType& aParam) { WriteIPDLParam(aMsg, aActor, static_cast(aParam)); - WriteIPDLParam(aMsg, aActor, aParam.mResponseSource); + WriteIPDLParam(aMsg, aActor, aParam.mProducer); } static bool Read(const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor, paramType* aParam) { bool result = ReadIPDLParam(aMsg, aIter, aActor, static_cast(aParam)); - return result && - ReadIPDLParam(aMsg, aIter, aActor, &aParam->mResponseSource); + return result && ReadIPDLParam(aMsg, aIter, aActor, &aParam->mProducer); } }; diff --git a/dom/canvas/WebGLCrossProcessCommandQueue.cpp b/dom/canvas/WebGLCrossProcessCommandQueue.cpp index a4491302f2ef..f88ccf84ac9d 100644 --- a/dom/canvas/WebGLCrossProcessCommandQueue.cpp +++ b/dom/canvas/WebGLCrossProcessCommandQueue.cpp @@ -8,14 +8,12 @@ namespace mozilla { -#if 0 -bool HostWebGLCommandSink::DispatchCommand(size_t command) { +bool HostWebGLCommandSink::DispatchCommand(size_t command) { if (!mHostContext) { return false; } return WebGLMethodDispatcher::DispatchCommand(command, *this, *mHostContext); } -#endif } // namespace mozilla diff --git a/dom/canvas/WebGLCrossProcessCommandQueue.h b/dom/canvas/WebGLCrossProcessCommandQueue.h index fef72ea752da..428cc1227139 100644 --- a/dom/canvas/WebGLCrossProcessCommandQueue.h +++ b/dom/canvas/WebGLCrossProcessCommandQueue.h @@ -7,16 +7,9 @@ #define WEBGLCROSSPROCESSCOMMANDQUEUE_H_ #include "mozilla/dom/WebGLCommandQueue.h" -#include "ProducerConsumerQueue.h" -#include "IpdlQueue.h" namespace mozilla { -namespace dom { -class WebGLParent; -class WebGLChild; -} // namespace dom - namespace layers { class PCompositorBridgeParent; } @@ -28,9 +21,7 @@ using mozilla::webgl::ProducerConsumerQueue; /** * The source for the WebGL Command Queue. */ -using ClientWebGLCommandSourceP = - SyncCommandSource; +using ClientWebGLCommandSource = SyncCommandSource; /** * The sink for the WebGL Command Queue. This object is created in the client @@ -38,56 +29,30 @@ using ClientWebGLCommandSourceP = * then uses for executing methods. Add new commands to DispatchCommand using * the WEBGL_SYNC_COMMAND and WEBGL_ASYNC_COMMAND macros. */ -template -class HostWebGLCommandSink final - : public SyncCommandSink { +class HostWebGLCommandSink : public SyncCommandSink { public: HostWebGLCommandSink(UniquePtr&& aConsumer, - UniquePtr&& aResponseProducer) - : SyncCommandSink( - std::move(aConsumer), std::move(aResponseProducer)) {} + UniquePtr&& aResponseProducer) + : SyncCommandSink(std::move(aConsumer), std::move(aResponseProducer)) {} HostWebGLContext* mHostContext = nullptr; - // For IPDL: - HostWebGLCommandSink() = default; - protected: + // For IPDL: friend struct mozilla::ipc::IPDLParamTraits; friend class mozilla::layers::PCompositorBridgeParent; + HostWebGLCommandSink() = default; - bool DispatchCommand(size_t command) override { - MOZ_CRASH("TODO:"); - return false; - } + bool DispatchCommand(size_t command) override; }; -using HostWebGLCommandSinkP = - HostWebGLCommandSink; - -using IpdlWebGLCommandQueue = - mozilla::dom::IpdlQueue; -using IpdlWebGLResponseQueue = - mozilla::dom::IpdlQueue; - -using ClientWebGLCommandSourceI = - SyncCommandSource; -using HostWebGLCommandSinkI = - HostWebGLCommandSink; - namespace ipc { -template -struct IPDLParamTraits> - : public IPDLParamTraits< - mozilla::SyncCommandSink> { +template <> +struct IPDLParamTraits + : public IPDLParamTraits> { public: - typedef mozilla::HostWebGLCommandSink paramType; + typedef mozilla::HostWebGLCommandSink paramType; }; } // namespace ipc diff --git a/dom/canvas/WebGLMethodDispatcher.h b/dom/canvas/WebGLMethodDispatcher.h index f5e2da8f4ff1..a0620a868935 100644 --- a/dom/canvas/WebGLMethodDispatcher.h +++ b/dom/canvas/WebGLMethodDispatcher.h @@ -9,13 +9,14 @@ #include "TexUnpackBlob.h" #include "WebGLCrossProcessCommandQueue.h" #include "HostWebGLContext.h" -#include "WebGLQueueParamTraits.h" +#include "WebGLPcqParamTraits.h" namespace mozilla { // The WebGLMethodDispatcher will dispatch commands read from the // HostWebGLCommandSink and issue them to a given HostWebGLContext. -DECLARE_METHOD_DISPATCHER(WebGLMethodDispatcher, HostWebGLContext) +DECLARE_METHOD_DISPATCHER(WebGLMethodDispatcher, HostWebGLCommandSink, + HostWebGLContext) // Defines each method the WebGLMethodDispatcher handles. The COUNTER value // is used as a cross-process ID for each of the methods. @@ -52,14 +53,14 @@ DEFINE_ASYNC(HostWebGLContext::DeleteTransformFeedback) DEFINE_ASYNC(HostWebGLContext::DeleteVertexArray) DEFINE_ASYNC(HostWebGLContext::ClearVRFrame) -// DEFINE_SYNC(HostWebGLContext::GetVRFrame) +DEFINE_SYNC(HostWebGLContext::GetVRFrame) DEFINE_ASYNC(HostWebGLContext::Disable) DEFINE_ASYNC(HostWebGLContext::Enable) DEFINE_ASYNC(HostWebGLContext::GenerateError) DEFINE_SYNC(HostWebGLContext::GetCompileResult) DEFINE_SYNC(HostWebGLContext::GetFragDataLocation) -// DEFINE_SYNC(HostWebGLContext::GetLinkResult) +DEFINE_SYNC(HostWebGLContext::GetLinkResult) DEFINE_SYNC(HostWebGLContext::InitializeCanvasRenderer) DEFINE_SYNC(HostWebGLContext::IsEnabled) DEFINE_ASYNC(HostWebGLContext::Resize) @@ -113,21 +114,21 @@ DEFINE_ASYNC(HostWebGLContext::Viewport) DEFINE_ASYNC(HostWebGLContext::BindBuffer) DEFINE_ASYNC(HostWebGLContext::BindBufferRange) DEFINE_ASYNC(HostWebGLContext::CopyBufferSubData) -// DEFINE_ASYNC(HostWebGLContext::GetBufferSubData) +DEFINE_ASYNC(HostWebGLContext::GetBufferSubData) DEFINE_ASYNC(HostWebGLContext::BufferData) DEFINE_ASYNC(HostWebGLContext::BufferSubData) DEFINE_ASYNC(HostWebGLContext::BlitFramebuffer) DEFINE_ASYNC(HostWebGLContext::InvalidateFramebuffer) DEFINE_ASYNC(HostWebGLContext::InvalidateSubFramebuffer) DEFINE_ASYNC(HostWebGLContext::ReadBuffer) -// DEFINE_SYNC(HostWebGLContext::GetInternalformatParameter) +DEFINE_SYNC(HostWebGLContext::GetInternalformatParameter) DEFINE_ASYNC(HostWebGLContext::RenderbufferStorageMultisample) DEFINE_ASYNC(HostWebGLContext::ActiveTexture) DEFINE_ASYNC(HostWebGLContext::BindTexture) DEFINE_ASYNC(HostWebGLContext::GenerateMipmap) DEFINE_ASYNC(HostWebGLContext::CopyTexImage) DEFINE_ASYNC(HostWebGLContext::TexStorage) -// DEFINE_ASYNC(HostWebGLContext::TexImage) +DEFINE_ASYNC(HostWebGLContext::TexImage) DEFINE_ASYNC(HostWebGLContext::CompressedTexImage) DEFINE_SYNC(HostWebGLContext::GetTexParameter) DEFINE_ASYNC(HostWebGLContext::TexParameter_base) @@ -144,7 +145,7 @@ DEFINE_SYNC(HostWebGLContext::GetVertexAttrib) DEFINE_ASYNC(HostWebGLContext::VertexAttribPointer) DEFINE_ASYNC(HostWebGLContext::ClearBufferTv) DEFINE_ASYNC(HostWebGLContext::ClearBufferfi) -// DEFINE_SYNC(HostWebGLContext::ReadPixels) +DEFINE_SYNC(HostWebGLContext::ReadPixels) DEFINE_ASYNC(HostWebGLContext::ReadPixelsPbo) DEFINE_ASYNC(HostWebGLContext::BindSampler) DEFINE_ASYNC(HostWebGLContext::SamplerParameteri) diff --git a/dom/canvas/WebGLParent.cpp b/dom/canvas/WebGLParent.cpp index ec9dee7c01b1..acf92a422a68 100644 --- a/dom/canvas/WebGLParent.cpp +++ b/dom/canvas/WebGLParent.cpp @@ -5,7 +5,6 @@ #include "WebGLParent.h" -#include "WebGLChild.h" #include "mozilla/dom/WebGLCrossProcessCommandQueue.h" #include "mozilla/layers/LayerTransactionParent.h" #include "mozilla/layers/TextureClientSharedSurface.h" @@ -15,31 +14,30 @@ namespace mozilla { namespace dom { -mozilla::ipc::IPCResult WebGLParent::RecvInitialize( - const webgl::InitContextDesc& desc, - UniquePtr&& aSinkP, - UniquePtr&& aSinkI, - webgl::InitContextResult* const out) { +/* static */ +RefPtr WebGLParent::Create(const webgl::InitContextDesc& desc, + webgl::InitContextResult* const out) { + RefPtr parent = new WebGLParent; auto remotingData = Some(HostWebGLContext::RemotingData{ - *this, {}, // std::move(commandSink), + *parent, {}, // std::move(commandSink), }); - mHost = HostWebGLContext::Create( + parent->mHost = HostWebGLContext::Create( { {}, std::move(remotingData), }, desc, out); - if (!mHost) { - return IPC_FAIL(this, "Failed to create HostWebGLContext"); + if (!parent->mHost) { + WEBGL_BRIDGE_LOGE("Failed to create HostWebGLContext"); + return nullptr; } - - if (!BeginCommandQueueDrain()) { - return IPC_FAIL(this, "Failed to start WebGL command queue drain"); + if (!parent->BeginCommandQueueDrain()) { + WEBGL_BRIDGE_LOGE("Failed to start WebGL command queue drain"); + return nullptr; } - - return IPC_OK(); + return parent; } WebGLParent::WebGLParent() = default; diff --git a/dom/canvas/WebGLParent.h b/dom/canvas/WebGLParent.h index cb52110c77dc..b7232ce1b9ed 100644 --- a/dom/canvas/WebGLParent.h +++ b/dom/canvas/WebGLParent.h @@ -9,10 +9,10 @@ #include "mozilla/GfxMessageUtils.h" #include "mozilla/dom/PWebGLParent.h" #include "mozilla/WeakPtr.h" -#include "mozilla/dom/IpdlQueue.h" namespace mozilla { +class HostWebGLCommandSink; class HostWebGLContext; namespace layers { @@ -21,30 +21,21 @@ class SharedSurfaceTextureClient; namespace dom { -class WebGLParent : public PWebGLParent, - public AsyncProducerActor, - public SyncConsumerActor, - public SupportsWeakPtr { +class WebGLParent final : public PWebGLParent, + public SupportsWeakPtr { friend PWebGLParent; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebGLParent, override); MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebGLParent) - using OtherSideActor = WebGLChild; - mozilla::ipc::IPCResult RecvInitialize( - const webgl::InitContextDesc&, UniquePtr&& aSinkP, - UniquePtr&& aSinkI, webgl::InitContextResult* out); + static RefPtr Create(const webgl::InitContextDesc&, + webgl::InitContextResult* out); RefPtr GetVRFrame(webgl::ObjectId); - // Drain the command queue now. Used by synchronous IpdlQueue consumers. - bool RunQueue(uint64_t) { return RunCommandQueue(); } - - // For IPDL: - WebGLParent(); - private: + WebGLParent(); ~WebGLParent(); bool BeginCommandQueueDrain(); diff --git a/dom/canvas/WebGLQueueParamTraits.h b/dom/canvas/WebGLPcqParamTraits.h similarity index 65% rename from dom/canvas/WebGLQueueParamTraits.h rename to dom/canvas/WebGLPcqParamTraits.h index 3191fa5b2680..e5405f84e8d2 100644 --- a/dom/canvas/WebGLQueueParamTraits.h +++ b/dom/canvas/WebGLPcqParamTraits.h @@ -3,8 +3,8 @@ * 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 WEBGLQUEUEPARAMTRAITS_H_ -#define WEBGLQUEUEPARAMTRAITS_H_ +#ifndef WEBGLPCQPARAMTRAITS_H_ +#define WEBGLPCQPARAMTRAITS_H_ #include @@ -17,7 +17,7 @@ namespace mozilla { namespace webgl { template -struct QueueParamTraits; +struct PcqParamTraits; template <> struct IsTriviallySerializable : std::true_type {}; @@ -56,21 +56,12 @@ struct IsTriviallySerializable> : std::true_type {}; template <> struct IsTriviallySerializable : std::true_type {}; - -template <> -struct IsTriviallySerializable : std::true_type {}; -template <> -struct IsTriviallySerializable : std::true_type { -}; -template <> -struct IsTriviallySerializable : std::true_type {}; - /* template <> -struct QueueParamTraits { +struct PcqParamTraits { using ParamType = WebGLActiveInfo; - static QueueStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { aProducerView.WriteParam(aArg.mElemCount); aProducerView.WriteParam(aArg.mElemType); aProducerView.WriteParam(aArg.mBaseUserName); @@ -80,7 +71,7 @@ struct QueueParamTraits { return aProducerView.WriteParam(aArg.mBaseType); } - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { aConsumerView.ReadParam(aArg ? &aArg->mElemCount : nullptr); aConsumerView.ReadParam(aArg ? &aArg->mElemType : nullptr); aConsumerView.ReadParam(aArg ? &aArg->mBaseUserName : nullptr); @@ -103,23 +94,21 @@ struct QueueParamTraits { }; */ template -struct QueueParamTraits> { +struct PcqParamTraits> { using ParamType = RawBuffer; - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { aProducerView.WriteParam(aArg.mLength); return (aArg.mLength > 0) ? aProducerView.Write(aArg.mData, aArg.mLength * sizeof(T)) - : aProducerView.GetStatus(); + : aProducerView.Status(); } - template > - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + template < + typename ElementType = std::remove_cv_t> + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { size_t len; - QueueStatus status = aConsumerView.ReadParam(&len); + PcqStatus status = aConsumerView.ReadParam(&len); if (!status) { return status; } @@ -129,7 +118,7 @@ struct QueueParamTraits> { aArg->mLength = 0; aArg->mData = nullptr; } - return QueueStatus::kSuccess; + return PcqStatus::Success; } if (!aArg) { @@ -137,17 +126,17 @@ struct QueueParamTraits> { } struct RawBufferReadMatcher { - QueueStatus operator()(RefPtr& smem) { + PcqStatus operator()(RefPtr& smem) { if (!smem) { - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } mArg->mSmem = smem; mArg->mData = static_cast(smem->memory()); mArg->mLength = mLength; mArg->mOwnsData = false; - return QueueStatus::kSuccess; + return PcqStatus::Success; } - QueueStatus operator()() { + PcqStatus operator()() { mArg->mSmem = nullptr; ElementType* buf = new ElementType[mLength]; mArg->mData = buf; @@ -156,7 +145,7 @@ struct QueueParamTraits> { return mConsumerView.Read(buf, mLength * sizeof(T)); } - ConsumerView& mConsumerView; + ConsumerView& mConsumerView; ParamType* mArg; size_t mLength; }; @@ -176,16 +165,16 @@ struct QueueParamTraits> { enum TexUnpackTypes : uint8_t { Bytes, Surface, Image, Pbo }; template <> -struct QueueParamTraits { +struct PcqParamTraits { using ParamType = webgl::TexUnpackBytes; - static QueueStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { // Write TexUnpackBlob base class, then the RawBuffer. aProducerView.WriteParam(static_cast(aArg)); return aProducerView.WriteParam(aArg.mPtr); } - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { // Read TexUnpackBlob base class, then the RawBuffer. aConsumerView.ReadParam(static_cast(aArg)); return aConsumerView.ReadParam(aArg ? &aArg->mPtr : nullptr); @@ -199,10 +188,10 @@ struct QueueParamTraits { }; template <> -struct QueueParamTraits { +struct PcqParamTraits { using ParamType = webgl::TexUnpackSurface; - static QueueStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { aProducerView.WriteParam(static_cast(aArg)); aProducerView.WriteParam(aArg.mSize); aProducerView.WriteParam(aArg.mFormat); @@ -210,7 +199,7 @@ struct QueueParamTraits { return aProducerView.WriteParam(aArg.mStride); } - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { aConsumerView.ReadParam(static_cast(aArg)); aConsumerView.ReadParam(aArg ? &aArg->mSize : nullptr); aConsumerView.ReadParam(aArg ? &aArg->mFormat : nullptr); @@ -228,28 +217,28 @@ struct QueueParamTraits { } }; -// Specialization of QueueParamTraits that adapts the TexUnpack type in order to +// Specialization of PcqParamTraits that adapts the TexUnpack type in order to // efficiently convert types. For example, a TexUnpackSurface may deserialize // as a TexUnpackBytes. template <> -struct QueueParamTraits { +struct PcqParamTraits { using ParamType = WebGLTexUnpackVariant; - static QueueStatus Write(ProducerView& aProducerView, const ParamType& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { struct TexUnpackWriteMatcher { - QueueStatus operator()(const UniquePtr& x) { + PcqStatus operator()(const UniquePtr& x) { mProducerView.WriteParam(TexUnpackTypes::Bytes); return mProducerView.WriteParam(x); } - QueueStatus operator()(const UniquePtr& x) { + PcqStatus operator()(const UniquePtr& x) { mProducerView.WriteParam(TexUnpackTypes::Surface); return mProducerView.WriteParam(x); } - QueueStatus operator()(const UniquePtr& x) { + PcqStatus operator()(const UniquePtr& x) { MOZ_ASSERT_UNREACHABLE("TODO:"); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } - QueueStatus operator()(const WebGLTexPboOffset& x) { + PcqStatus operator()(const WebGLTexPboOffset& x) { mProducerView.WriteParam(TexUnpackTypes::Pbo); return mProducerView.WriteParam(x); } @@ -258,14 +247,14 @@ struct QueueParamTraits { return aArg.match(TexUnpackWriteMatcher{aProducerView}); } - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { if (!aArg) { // Not a great estimate but we can't do much better. return aConsumerView.template ReadParam(); } TexUnpackTypes unpackType; if (!aConsumerView.ReadParam(&unpackType)) { - return aConsumerView.GetStatus(); + return aConsumerView.Status(); } switch (unpackType) { case TexUnpackTypes::Bytes: @@ -278,13 +267,13 @@ struct QueueParamTraits { &aArg->as>()); case TexUnpackTypes::Image: MOZ_ASSERT_UNREACHABLE("TODO:"); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; case TexUnpackTypes::Pbo: *aArg = AsVariant(WebGLTexPboOffset()); return aConsumerView.ReadParam(&aArg->as()); } MOZ_ASSERT_UNREACHABLE("Illegal texture unpack type"); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } template @@ -315,23 +304,20 @@ struct QueueParamTraits { }; */ template <> -struct QueueParamTraits { +struct PcqParamTraits { using ParamType = webgl::ContextLossReason; - template - static QueueStatus Write(ProducerView& aProducerView, - const ParamType& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const ParamType& aArg) { return aProducerView.WriteParam(static_cast(aArg)); } - template - static QueueStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, ParamType* aArg) { uint8_t val; auto status = aConsumerView.ReadParam(&val); if (!status) return status; if (!ReadContextLossReason(val, aArg)) { MOZ_ASSERT_UNREACHABLE("Invalid ContextLossReason"); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } return status; } @@ -343,11 +329,10 @@ struct QueueParamTraits { }; template -struct QueueParamTraits> { +struct PcqParamTraits> { using T = Result; - template - static QueueStatus Write(ProducerView& aProducerView, const T& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const T& aArg) { const auto ok = aArg.isOk(); auto status = aProducerView.WriteParam(ok); if (!status) return status; @@ -359,8 +344,7 @@ struct QueueParamTraits> { return status; } - template - static QueueStatus Read(ConsumerView& aConsumerView, T* const aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, T* const aArg) { bool ok; auto status = aConsumerView.ReadParam(&ok); if (!status) return status; @@ -393,26 +377,24 @@ struct QueueParamTraits> { }; template <> -struct QueueParamTraits { +struct PcqParamTraits { using T = std::string; - template - static QueueStatus Write(ProducerView& aProducerView, const T& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const T& aArg) { auto status = aProducerView.WriteParam(aArg.size()); if (!status) return status; status = aProducerView.Write(aArg.data(), aArg.size()); return status; } - template - static QueueStatus Read(ConsumerView& aConsumerView, T* const aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, T* const aArg) { size_t size; auto status = aConsumerView.ReadParam(&size); if (!status) return status; const UniqueBuffer temp = malloc(size); const auto dest = static_cast(temp.get()); - if (!dest) return QueueStatus::kFatalError; + if (!dest) return PcqStatus::PcqFatalError; status = aConsumerView.Read(dest, size); if (aArg) { @@ -432,19 +414,17 @@ struct QueueParamTraits { }; template -struct QueueParamTraits> { - using T = std::vector; +struct PcqParamTraits> { + using T = std::string; - template - static QueueStatus Write(ProducerView& aProducerView, const T& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const T& aArg) { auto status = aProducerView.WriteParam(aArg.size()); if (!status) return status; status = aProducerView.Write(aArg.data(), aArg.size()); return status; } - template - static QueueStatus Read(ConsumerView& aConsumerView, T* const aArg) { + static PcqStatus Read(ConsumerView& aConsumerView, T* const aArg) { MOZ_CRASH("no way to fallibly resize vectors without exceptions"); size_t size; auto status = aConsumerView.ReadParam(&size); @@ -466,23 +446,21 @@ struct QueueParamTraits> { }; template <> -struct QueueParamTraits { +struct PcqParamTraits { using T = WebGLExtensionID; - template - static QueueStatus Write(ProducerView& aProducerView, const T& aArg) { + static PcqStatus Write(ProducerView& aProducerView, const T& aArg) { return aProducerView.WriteParam(mozilla::EnumValue(aArg)); } - template - static QueueStatus Read(ConsumerView& aConsumerView, T* const aArg) { - QueueStatus status = QueueStatus::kSuccess; + static PcqStatus Read(ConsumerView& aConsumerView, T* const aArg) { + PcqStatus status = PcqStatus::Success; if (aArg) { status = aConsumerView.ReadParam( reinterpret_cast::type*>(aArg)); if (*aArg >= WebGLExtensionID::Max) { MOZ_ASSERT(false); - return QueueStatus::kFatalError; + return PcqStatus::PcqFatalError; } } return status; @@ -494,71 +472,7 @@ struct QueueParamTraits { } }; -template <> -struct QueueParamTraits { - using T = CompileResult; - - template - static QueueStatus Write(ProducerView& aProducerView, const T& aArg) { - aProducerView.WriteParam(aArg.pending); - aProducerView.WriteParam(aArg.log); - aProducerView.WriteParam(aArg.translatedSource); - return aProducerView.WriteParam(aArg.success); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, T* const aArg) { - aConsumerView.ReadParam(aArg ? &aArg->pending : nullptr); - aConsumerView.ReadParam(aArg ? &aArg->log : nullptr); - aConsumerView.ReadParam(aArg ? &aArg->translatedSource : nullptr); - return aConsumerView.ReadParam(aArg ? &aArg->success : nullptr); - } - - template - static size_t MinSize(View& aView, const T* const aArg) { - return aView.MinSizeParam(aArg ? &aArg->pending : nullptr) + - aView.MinSizeParam(aArg ? &aArg->log : nullptr) + - aView.MinSizeParam(aArg ? &aArg->translatedSource : nullptr) + - aView.MinSizeParam(aArg ? &aArg->success : nullptr); - } -}; - -#if 0 -// TODO: QueueParamTraits for LinkActiveResult and friends. -template <> -struct QueueParamTraits { - using T = LinkResult; - - template - static QueueStatus Write(ProducerView& aProducerView, const T& aArg) { - aProducerView.WriteParam(aArg.pending); - aProducerView.WriteParam(aArg.log); - aProducerView.WriteParam(aArg.active); - aProducerView.WriteParam(aArg.tfBufferMode); - return aProducerView.WriteParam(aArg.success); - } - - template - static QueueStatus Read(ConsumerView& aConsumerView, T* const aArg) { - aConsumerView.ReadParam(aArg ? &aArg->pending : nullptr); - aConsumerView.ReadParam(aArg ? &aArg->log : nullptr); - aConsumerView.ReadParam(aArg ? &aArg->active : nullptr); - aConsumerView.ReadParam(aArg ? &aArg->tfBufferMode : nullptr); - return aConsumerView.ReadParam(aArg ? &aArg->success : nullptr); - } - - template - static size_t MinSize(View& aView, const T* const aArg) { - return aView.MinSizeParam(aArg ? &aArg->pending : nullptr) + - aView.MinSizeParam(aArg ? &aArg->log : nullptr) + - aView.MinSizeParam(aArg ? &aArg->active : nullptr) + - aView.MinSizeParam(aArg ? &aArg->tfBufferMode : nullptr) + - aView.MinSizeParam(aArg ? &aArg->success : nullptr); - } -}; -#endif - } // namespace webgl } // namespace mozilla -#endif // WEBGLQUEUEPARAMTRAITS_H_ +#endif // WEBGLPCQPARAMTRAITS_H_ diff --git a/dom/canvas/WebGLTypes.h b/dom/canvas/WebGLTypes.h index 7aeca93585e7..561884603412 100644 --- a/dom/canvas/WebGLTypes.h +++ b/dom/canvas/WebGLTypes.h @@ -94,9 +94,12 @@ inline auto AutoAssertCast(const From val) { return detail::AutoAssertCastT(val); } -namespace webgl { +namespace ipc { template -struct QueueParamTraits; +struct PcqParamTraits; +} + +namespace webgl { class TexUnpackBytes; class TexUnpackImage; class TexUnpackSurface; @@ -730,7 +733,7 @@ class RawBuffer { // true if we should delete[] the mData on destruction bool mOwnsData = false; - friend struct mozilla::webgl::QueueParamTraits>; + friend mozilla::ipc::PcqParamTraits; public: using ElementType = T; diff --git a/dom/canvas/moz.build b/dom/canvas/moz.build index 65e41a003bee..267c9f248439 100644 --- a/dom/canvas/moz.build +++ b/dom/canvas/moz.build @@ -58,10 +58,8 @@ EXPORTS.mozilla.dom += [ 'ImageBitmapSource.h', 'ImageData.h', 'ImageUtils.h', - 'IpdlQueue.h', 'OffscreenCanvas.h', 'ProducerConsumerQueue.h', - 'QueueParamTraits.h', 'TextMetrics.h', 'WebGLChild.h', 'WebGLCommandQueue.h', @@ -96,7 +94,7 @@ UNIFIED_SOURCES += [ 'ClientWebGLContext.cpp', 'ClientWebGLExtensions.cpp', 'HostWebGLContext.cpp', - 'Queue.cpp', + 'ProducerConsumerQueue.cpp', 'TexUnpackBlob.cpp', 'WebGL2Context.cpp', 'WebGL2ContextBuffers.cpp', diff --git a/gfx/layers/ipc/CompositorBridgeChild.cpp b/gfx/layers/ipc/CompositorBridgeChild.cpp index a0dcff5cb9a8..9c563d4d342e 100644 --- a/gfx/layers/ipc/CompositorBridgeChild.cpp +++ b/gfx/layers/ipc/CompositorBridgeChild.cpp @@ -12,6 +12,7 @@ #include "base/message_loop.h" // for MessageLoop #include "base/task.h" // for NewRunnableMethod, etc #include "mozilla/StaticPrefs_layers.h" +#include "mozilla/dom/WebGLChild.h" #include "mozilla/layers/CompositorManagerChild.h" #include "mozilla/layers/ImageBridgeChild.h" #include "mozilla/layers/APZChild.h" diff --git a/gfx/layers/ipc/CompositorBridgeParent.h b/gfx/layers/ipc/CompositorBridgeParent.h index 44adbfa7150b..2e4f592be40c 100644 --- a/gfx/layers/ipc/CompositorBridgeParent.h +++ b/gfx/layers/ipc/CompositorBridgeParent.h @@ -53,6 +53,7 @@ class nsIWidget; namespace mozilla { class CancelableRunnable; +class HostWebGLCommandSink; namespace dom { class WebGLParent; @@ -302,7 +303,8 @@ class CompositorBridgeParentBase : public PCompositorBridgeParent, return IPC_FAIL_NO_REASON(this); } - virtual already_AddRefed AllocPWebGLParent() = 0; + virtual already_AddRefed AllocPWebGLParent( + const webgl::InitContextDesc&, webgl::InitContextResult* out) = 0; bool mCanSend; @@ -692,7 +694,8 @@ class CompositorBridgeParent final : public CompositorBridgeParentBase, WebRenderBridgeParent* GetWrBridge() { return mWrBridge; } webgpu::WebGPUParent* GetWebGPUBridge() { return mWebGPUBridge; } - already_AddRefed AllocPWebGLParent() override { + already_AddRefed AllocPWebGLParent( + const webgl::InitContextDesc&, webgl::InitContextResult*) override { MOZ_ASSERT_UNREACHABLE( "This message is CrossProcessCompositorBridgeParent only"); return nullptr; diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp index bfd79e1746e7..7669fffdec1d 100644 --- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp @@ -766,9 +766,11 @@ mozilla::ipc::IPCResult ContentCompositorBridgeParent::RecvPreferredDXGIAdapter( } already_AddRefed -ContentCompositorBridgeParent::AllocPWebGLParent() { - RefPtr parent = new dom::WebGLParent(); - return parent.forget(); +ContentCompositorBridgeParent::AllocPWebGLParent( + const webgl::InitContextDesc& aInitDesc, + webgl::InitContextResult* const out) { + RefPtr ret = dom::WebGLParent::Create(aInitDesc, out); + return ret.forget(); } } // namespace layers diff --git a/gfx/layers/ipc/ContentCompositorBridgeParent.h b/gfx/layers/ipc/ContentCompositorBridgeParent.h index 03574bf947a2..77cd9e5170fb 100644 --- a/gfx/layers/ipc/ContentCompositorBridgeParent.h +++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h @@ -161,7 +161,8 @@ class ContentCompositorBridgeParent final : public CompositorBridgeParentBase { return IPC_FAIL_NO_REASON(this); } - already_AddRefed AllocPWebGLParent() override; + already_AddRefed AllocPWebGLParent( + const webgl::InitContextDesc&, webgl::InitContextResult* out) override; // Use DidCompositeLocked if you already hold a lock on // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request diff --git a/gfx/layers/ipc/PCompositorBridge.ipdl b/gfx/layers/ipc/PCompositorBridge.ipdl index 2a730d8a65c7..83dfbaa8056e 100644 --- a/gfx/layers/ipc/PCompositorBridge.ipdl +++ b/gfx/layers/ipc/PCompositorBridge.ipdl @@ -48,6 +48,8 @@ using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h"; using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h"; using struct DxgiAdapterDesc from "mozilla/D3DMessageUtils.h"; +using mozilla::webgl::InitContextDesc from "mozilla/dom/WebGLIpdl.h"; +using mozilla::webgl::InitContextResult from "mozilla/dom/WebGLIpdl.h"; namespace mozilla { namespace layers { @@ -298,7 +300,9 @@ parent: async RequestFxrOutput(); // Actor that represents one WebGL context. - async PWebGL(); + // (modeled after `sync PBackgroundLSSnapshot`) + sync PWebGL(InitContextDesc desc) + returns (InitContextResult res); child: // Send back Compositor Frame Metrics from APZCs so tiled layers can diff --git a/ipc/ipdl/sync-messages.ini b/ipc/ipdl/sync-messages.ini index f9be462025aa..34b10d338e82 100644 --- a/ipc/ipdl/sync-messages.ini +++ b/ipc/ipdl/sync-messages.ini @@ -1213,10 +1213,8 @@ description = Synchronous ping allowing worker thread to confirm actor is create description = Synchronous launch of a child process that in turn launches and sandboxes another process. Called on a dedicated thread and targets a dedicated process, so this shouldn't block anything. [PWebGL::UpdateCompositableHandle] description = Compositing WebGL is synchronous by spec. Updates to WebGL canvas contents must be updated in lockstep with other DOM updates. -[PWebGL::Initialize] -description = Initialization of WebGL contexts is synchronous by spec. -[PWebGL::ExchangeIpdlQueueData] -description = Synchronous RPC to allow WebGL to run graphics commands in compositor process and return results to be used in JS return values. +[PCompositorBridge::PWebGL] +description = Creation of WebGL contexts is synchronous by spec. [PSocketProcess::GetTLSClientCert] description = Synchronously get client certificate and key from parent process. Once bug 696976 has been fixed, this can be removed. diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 5e1b95434ec9..24b6250043c2 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -8952,12 +8952,6 @@ value: true mirror: always -# value is 0 for PCQ, 1 for Ipdl -- name: webgl.prototype.ipc-pcq - type: uint32_t - value: 0 - mirror: always - #--------------------------------------------------------------------------- # Prefs starting with "widget." #---------------------------------------------------------------------------