/* -*- 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_