1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
14 #include <unordered_map>
15 #include "ipc/IPCMessageUtilsSpecializations.h"
16 #include "mozilla/dom/QueueParamTraits.h"
17 #include "mozilla/ipc/SharedMemoryBasic.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/ipc/Shmem.h"
20 #include "mozilla/ipc/ProtocolUtils.h"
21 #include "mozilla/Logging.h"
22 #include "mozilla/ScopeExit.h"
23 #include "mozilla/TimeStamp.h"
24 #include "mozilla/TypeTraits.h"
26 #include "mozilla/WeakPtr.h"
36 using mozilla::webgl::QueueStatus
;
38 extern LazyLogModule gIpdlQueueLog
;
39 #define IPDLQUEUE_LOG_(lvl, ...) \
40 MOZ_LOG(mozilla::dom::gIpdlQueueLog, lvl, (__VA_ARGS__))
41 #define IPDLQUEUE_LOGD(...) IPDLQUEUE_LOG_(LogLevel::Debug, __VA_ARGS__)
42 #define IPDLQUEUE_LOGE(...) IPDLQUEUE_LOG_(LogLevel::Error, __VA_ARGS__)
44 template <typename ActorP
, typename ActorC
>
46 template <typename Derived
>
47 class SyncConsumerActor
;
49 enum IpdlQueueProtocol
{
51 * Sends the message immediately. Does not wait for a response.
55 * Sends the message immediately or caches it for a later batch
56 * send. Messages may be sent at any point in the future but
57 * will always be processed in order. kSync messages always force
58 * a flush of the cache but other mechanisms (e.g. periodic tasks)
59 * can do this as well.
63 * Sends the message immediately. Waits for any response message,
64 * which can immediately be read upon completion of the send.
69 constexpr uint64_t kIllegalQueueId
= 0;
70 inline uint64_t NewIpdlQueueId() {
71 static std::atomic
<uint64_t> sNextIpdlQueueId
= 1;
72 return sNextIpdlQueueId
++;
75 struct IpdlQueueBuffer
{
76 uint64_t id
= kIllegalQueueId
;
77 nsTArray
<uint8_t> data
;
79 IpdlQueueBuffer() = default;
80 IpdlQueueBuffer(const IpdlQueueBuffer
&) = delete;
81 IpdlQueueBuffer(IpdlQueueBuffer
&&) = default;
82 IpdlQueueBuffer(uint64_t aId
, nsTArray
<uint8_t>&& aData
)
83 : id(aId
), data(std::move(aData
)) {}
86 using IpdlQueueBuffers
= nsTArray
<IpdlQueueBuffer
>;
88 static constexpr uint32_t kAsyncFlushWaitMs
= 4; // 4ms
90 template <typename Derived
>
91 class AsyncProducerActor
{
93 virtual bool TransmitIpdlQueueData(IpdlQueueProtocol aProtocol
,
94 IpdlQueueBuffer
&& aData
) {
95 MOZ_ASSERT((aProtocol
== IpdlQueueProtocol::kAsync
) ||
96 (aProtocol
== IpdlQueueProtocol::kBufferedAsync
));
98 if (mResponseBuffers
|| (aProtocol
== IpdlQueueProtocol::kBufferedAsync
)) {
99 // Always use response buffer if set.
100 auto& buffers
= mResponseBuffers
? *mResponseBuffers
: mAsyncBuffers
;
102 // We are in the middle of a sync transaction. Store the data so
103 // that we can return it with the response.
104 const uint64_t id
= aData
.id
;
105 for (auto& elt
: buffers
) {
107 elt
.data
.AppendElements(aData
.data
);
111 buffers
.AppendElement(std::move(aData
));
113 if (!mResponseBuffers
) {
114 PostFlushAsyncCache(kAsyncFlushWaitMs
);
119 // We are not inside of a transaction. Send normally, but first send any
123 Derived
* self
= static_cast<Derived
*>(this);
124 return self
->SendTransmitIpdlQueueData(std::move(aData
));
127 // This can be called at any time to flush all queued async messages.
128 bool FlushAsyncCache() {
129 Derived
* self
= static_cast<Derived
*>(this);
130 for (auto& elt
: mAsyncBuffers
) {
131 if (!elt
.data
.IsEmpty()) {
132 if (!self
->SendTransmitIpdlQueueData(std::move(elt
))) {
137 mAsyncBuffers
.Clear();
141 bool PostFlushAsyncCache(uint32_t aEstWaitTimeMs
) {
142 if (mPostedFlushRunnable
) {
143 // Already scheduled a flush for later.
147 MOZ_ASSERT(GetCurrentSerialEventTarget(),
148 "No message loop for IpdlQueue flush task");
150 Derived
* self
= static_cast<Derived
*>(this);
151 // IpdlProducer/IpdlConsumer guarantees the actor supports WeakPtr.
152 auto weak
= WeakPtr
<Derived
>(self
);
153 already_AddRefed
<mozilla::Runnable
> flushRunnable
=
154 NS_NewRunnableFunction("FlushAsyncCache", [weak
] {
155 auto strong
= RefPtr
<Derived
>(weak
);
159 strong
->FlushAsyncCache();
160 strong
->ClearFlushRunnable();
163 NS_ENSURE_SUCCESS(GetCurrentSerialEventTarget()->DelayedDispatch(
164 std::move(flushRunnable
), aEstWaitTimeMs
),
166 mPostedFlushRunnable
= true;
170 void ClearFlushRunnable() { mPostedFlushRunnable
= false; }
172 template <typename
... Args
>
173 IpdlQueueProtocol
GetIpdlQueueProtocol(const Args
&...) {
174 return IpdlQueueProtocol::kAsync
;
178 friend SyncConsumerActor
<Derived
>;
180 void SetResponseBuffers(IpdlQueueBuffers
* aResponse
) {
181 MOZ_ASSERT(!mResponseBuffers
);
182 mResponseBuffers
= aResponse
;
184 // Response should include any cached async transmissions.
185 *mResponseBuffers
= std::move(mAsyncBuffers
);
188 void ClearResponseBuffers() {
189 MOZ_ASSERT(mResponseBuffers
);
190 mResponseBuffers
= nullptr;
193 // Stores response when inside of a kSync transaction.
194 IpdlQueueBuffers
* mResponseBuffers
= nullptr;
195 // For kBufferedAsync transmissions that occur outside of a response to a
197 IpdlQueueBuffers mAsyncBuffers
;
199 bool mPostedFlushRunnable
= false;
202 template <typename Derived
>
203 class SyncProducerActor
: public AsyncProducerActor
<Derived
> {
205 bool TransmitIpdlQueueData(IpdlQueueProtocol aProtocol
,
206 IpdlQueueBuffer
&& aData
) override
{
207 Derived
* self
= static_cast<Derived
*>(this);
208 if (mResponseBuffers
|| (aProtocol
!= IpdlQueueProtocol::kSync
)) {
209 return AsyncProducerActor
<Derived
>::TransmitIpdlQueueData(
210 aProtocol
, std::forward
<IpdlQueueBuffer
>(aData
));
213 IpdlQueueBuffers responses
;
214 if (!self
->SendExchangeIpdlQueueData(std::forward
<IpdlQueueBuffer
>(aData
),
219 for (auto& buf
: responses
) {
220 if (!self
->StoreIpdlQueueData(std::move(buf
))) {
228 using AsyncProducerActor
<Derived
>::mResponseBuffers
;
231 template <typename Derived
>
232 class AsyncConsumerActor
{
234 // Returns the ipdlQueue contents that were Recv'ed in a prior IPDL
235 // transmission. No new data is received via IPDL during this operation.
236 nsTArray
<uint8_t> TakeIpdlQueueData(uint64_t aId
) {
237 auto it
= mIpdlQueueBuffers
.find(aId
);
238 if (it
!= mIpdlQueueBuffers
.end()) {
239 return std::move(it
->second
.data
);
241 return nsTArray
<uint8_t>();
245 friend SyncProducerActor
<Derived
>;
247 // Store data received from the producer, to be read by local IpdlConsumers.
248 bool StoreIpdlQueueData(IpdlQueueBuffer
&& aBuffer
) {
249 auto it
= mIpdlQueueBuffers
.find(aBuffer
.id
);
250 if (it
== mIpdlQueueBuffers
.end()) {
251 return mIpdlQueueBuffers
.insert({aBuffer
.id
, std::move(aBuffer
)}).second
;
253 return it
->second
.data
.AppendElements(aBuffer
.data
, fallible
);
256 mozilla::ipc::IPCResult
RecvTransmitIpdlQueueData(IpdlQueueBuffer
&& aBuffer
) {
257 if (StoreIpdlQueueData(std::forward
<IpdlQueueBuffer
>(aBuffer
))) {
260 return IPC_FAIL_NO_REASON(static_cast<Derived
*>(this));
263 std::unordered_map
<uint64_t, IpdlQueueBuffer
> mIpdlQueueBuffers
;
266 template <typename Derived
>
267 class SyncConsumerActor
: public AsyncConsumerActor
<Derived
> {
269 using AsyncConsumerActor
<Derived
>::StoreIpdlQueueData
;
271 mozilla::ipc::IPCResult
RecvExchangeIpdlQueueData(
272 IpdlQueueBuffer
&& aBuffer
, IpdlQueueBuffers
* aResponse
) {
273 uint64_t id
= aBuffer
.id
;
274 if (!StoreIpdlQueueData(std::forward
<IpdlQueueBuffer
>(aBuffer
))) {
275 return IPC_FAIL_NO_REASON(static_cast<Derived
*>(this));
278 // Mark the actor as in a sync operation, then calls handler.
279 // During handler, if actor is used as producer (for ALL queues)
280 // then instead of immediately sending, it writes the data into
281 // aResponse. When handler is done, we unmark the actor.
282 // Note that we must buffer for _all_ queues associated with the
283 // actor as the intended response queue is indistinguishable from
284 // the rest from our vantage point.
285 Derived
* actor
= static_cast<Derived
*>(this);
286 actor
->SetResponseBuffers(aResponse
);
287 auto clearResponseBuffer
=
288 MakeScopeExit([&] { actor
->ClearResponseBuffers(); });
291 // Response now includes any cached async transmissions. It is
292 // illegal to have a response queue also used for other purposes
293 // so the cache for that queue must be empty.
294 DebugOnly
<bool> responseBufferIsEmpty
= [&] {
295 for (auto& elt
: *aResponse
) {
297 return elt
.data
.IsEmpty();
302 MOZ_ASSERT(responseBufferIsEmpty
);
305 return actor
->RunQueue(id
) ? IPC_OK() : IPC_FAIL_NO_REASON(actor
);
309 template <typename _Actor
>
310 class IpdlProducer final
: public SupportsWeakPtr
{
311 nsTArray
<uint8_t> mSerializedData
;
312 WeakPtr
<_Actor
> mActor
;
316 using Actor
= _Actor
;
317 using SelfType
= IpdlProducer
<Actor
>;
320 IpdlProducer() : mId(kIllegalQueueId
) {}
323 * Insert aArgs into the queue. If the operation does not succeed then
324 * the queue is unchanged.
326 template <typename
... Args
>
327 QueueStatus
TryInsert(Args
&&... aArgs
) {
328 MOZ_ASSERT(mId
!= kIllegalQueueId
);
330 NS_WARNING("TryInsert with actor that was already freed.");
331 return QueueStatus::kFatalError
;
334 // Fill mSerializedData with the data to send. Clear it when done.
335 MOZ_ASSERT(mSerializedData
.IsEmpty());
337 auto clearData
= MakeScopeExit([&] { self
.mSerializedData
.Clear(); });
338 const IpdlQueueProtocol protocol
= mActor
->GetIpdlQueueProtocol(aArgs
...);
339 QueueStatus status
= SerializeAllArgs(std::forward
<Args
>(aArgs
)...);
340 if (status
!= QueueStatus::kSuccess
) {
343 return mActor
->TransmitIpdlQueueData(
344 protocol
, IpdlQueueBuffer(mId
, std::move(mSerializedData
)))
345 ? QueueStatus::kSuccess
346 : QueueStatus::kFatalError
;
350 * Same as TryInsert. IPDL send failures are considered fatal to the
353 template <typename
... Args
>
354 QueueStatus
TryWaitInsert(const Maybe
<TimeDuration
>&, Args
&&... aArgs
) {
355 return TryInsert(std::forward
<Args
>(aArgs
)...);
358 QueueStatus
AllocShmem(mozilla::ipc::Shmem
* aShmem
, size_t aBufferSize
,
359 const void* aBuffer
= nullptr) {
361 return QueueStatus::kFatalError
;
364 if (!mActor
->AllocShmem(
366 mozilla::ipc::SharedMemory::SharedMemoryType::TYPE_BASIC
, aShmem
)) {
367 return QueueStatus::kOOMError
;
371 memcpy(aShmem
->get
<uint8_t>(), aBuffer
, aBufferSize
);
373 return QueueStatus::kSuccess
;
377 template <typename T1
, typename T2
>
378 friend class IpdlQueue
;
379 friend struct mozilla::ipc::IPDLParamTraits
<SelfType
>;
381 explicit IpdlProducer(uint64_t aId
, Actor
* aActor
= nullptr)
382 : mActor(aActor
), mId(aId
) {}
384 template <typename
... Args
>
385 QueueStatus
SerializeAllArgs(Args
&&... aArgs
) {
388 mozilla::webgl::ProducerView
<SelfType
> view(this, read
, &write
);
389 size_t bytesNeeded
= MinSizeofArgs(view
, aArgs
...);
390 if (!mSerializedData
.SetLength(bytesNeeded
, fallible
)) {
391 return QueueStatus::kOOMError
;
394 return SerializeArgs(view
, aArgs
...);
397 QueueStatus
SerializeArgs(mozilla::webgl::ProducerView
<SelfType
>& aView
) {
398 return QueueStatus::kSuccess
;
401 template <typename Arg
, typename
... Args
>
402 QueueStatus
SerializeArgs(mozilla::webgl::ProducerView
<SelfType
>& aView
,
403 const Arg
& aArg
, const Args
&... aArgs
) {
404 QueueStatus status
= SerializeArg(aView
, aArg
);
405 if (!IsSuccess(status
)) {
408 return SerializeArgs(aView
, aArgs
...);
411 template <typename Arg
>
412 QueueStatus
SerializeArg(mozilla::webgl::ProducerView
<SelfType
>& aView
,
414 return mozilla::webgl::QueueParamTraits
<
415 typename
std::remove_volatile
<Arg
>::type
>::Write(aView
, aArg
);
419 template <typename Arg
>
420 QueueStatus
WriteObject(size_t aRead
, size_t* aWrite
, const Arg
& arg
,
422 if (mSerializedData
.Length() < (*aWrite
) + aArgSize
) {
423 // Previous MinSizeOfArgs estimate was insufficient. Resize the
424 // buffer to accomodate our real needs.
425 mSerializedData
.SetLength(*aWrite
+ aArgSize
);
427 return mozilla::webgl::Marshaller::WriteObject(
428 mSerializedData
.Elements(), mSerializedData
.Length() + 1, aRead
, aWrite
,
432 base::ProcessId
OtherPid() { return mActor
? mActor
->OtherPid() : 0; }
435 size_t MinSizeofArgs(mozilla::webgl::ProducerView
<SelfType
>& aView
) {
439 template <typename Arg
, typename
... Args
>
440 size_t MinSizeofArgs(mozilla::webgl::ProducerView
<SelfType
>& aView
,
441 const Arg
& aArg
, const Args
&... aArgs
) {
442 return aView
.MinSizeParam(aArg
) + MinSizeofArgs(aView
, aArgs
...);
446 template <typename _Actor
>
447 class IpdlConsumer final
: public SupportsWeakPtr
{
449 using Actor
= _Actor
;
450 using SelfType
= IpdlConsumer
<Actor
>;
453 IpdlConsumer() : mId(kIllegalQueueId
) {}
456 * Attempts to copy and remove aArgs from the queue. If the operation does
457 * not succeed then the queue is unchanged. If the operation returns
458 * kQueueNotReady then the consumer does not yet have enough data to satisfy
459 * the request. In this case, the IPDL MessageQueue should be given the
460 * opportunity to run, at which point TryRemove can be attempted again.
462 template <typename
... Args
>
463 QueueStatus
TryRemove(Args
&... aArgs
) {
464 MOZ_ASSERT(mId
!= kIllegalQueueId
);
466 NS_WARNING("TryRemove with actor that was already freed.");
467 return QueueStatus::kFatalError
;
469 mBuf
.AppendElements(mActor
->TakeIpdlQueueData(mId
));
470 return DeserializeAllArgs(aArgs
...);
474 * Equivalent to TryRemove. Duration is ignored as it would need to
475 * allow the IPDL queue to run to be useful.
477 template <typename
... Args
>
478 QueueStatus
TryWaitRemove(const Maybe
<TimeDuration
>&, Args
&... aArgs
) {
479 return TryRemove(aArgs
...);
482 mozilla::ipc::Shmem::SharedMemory
* LookupSharedMemory(uint32_t aId
) {
483 return mActor
? mActor
->LookupSharedMemory(aId
) : nullptr;
487 template <typename T1
, typename T2
>
488 friend class IpdlQueue
;
489 friend struct mozilla::ipc::IPDLParamTraits
<SelfType
>;
491 explicit IpdlConsumer(uint64_t aId
, Actor
* aActor
= nullptr)
492 : mActor(aActor
), mId(aId
) {}
494 template <typename
... Args
>
495 QueueStatus
DeserializeAllArgs(Args
&... aArgs
) {
497 size_t write
= mBuf
.Length();
498 mozilla::webgl::ConsumerView
<SelfType
> view(this, &read
, write
);
500 QueueStatus status
= DeserializeArgs(view
, aArgs
...);
501 if (IsSuccess(status
) && (read
> 0)) {
502 mBuf
.RemoveElementsAt(0, read
);
507 QueueStatus
DeserializeArgs(mozilla::webgl::ConsumerView
<SelfType
>& aView
) {
508 return QueueStatus::kSuccess
;
511 template <typename Arg
, typename
... Args
>
512 QueueStatus
DeserializeArgs(mozilla::webgl::ConsumerView
<SelfType
>& aView
,
513 Arg
& aArg
, Args
&... aArgs
) {
514 QueueStatus status
= DeserializeArg(aView
, aArg
);
515 if (!IsSuccess(status
)) {
518 return DeserializeArgs(aView
, aArgs
...);
521 template <typename Arg
>
522 QueueStatus
DeserializeArg(mozilla::webgl::ConsumerView
<SelfType
>& aView
,
524 return mozilla::webgl::
525 QueueParamTraits
<typename
mozilla::webgl::RemoveCVR
<Arg
>::Type
>::Read(
526 aView
, const_cast<std::remove_cv_t
<Arg
>*>(&aArg
));
530 template <typename Arg
>
531 QueueStatus
ReadObject(size_t* aRead
, size_t aWrite
, Arg
* arg
,
533 // TODO: Queue needs one extra byte for PCQ (fixme).
534 return mozilla::webgl::Marshaller::ReadObject(
535 mBuf
.Elements(), mBuf
.Length() + 1, aRead
, aWrite
, arg
, aArgSize
);
538 base::ProcessId
OtherPid() { return mActor
? mActor
->OtherPid() : 0; }
541 WeakPtr
<Actor
> mActor
;
543 nsTArray
<uint8_t> mBuf
;
547 * An IpdlQueue is a queue that uses an actor of type ActorP to send data and
548 * its reciprocal (i.e. child to its parent or vice-versa) to receive data.
549 * ActorP must derive from one of:
550 * AsyncProducerActor, SyncProducerActor
551 * ActorC must derive from one of:
552 * AsyncConsumerActor, SyncConsumerActor
554 template <typename _ActorP
, typename _ActorC
>
555 class IpdlQueue final
{
557 using ActorP
= _ActorP
;
558 using ActorC
= _ActorC
;
559 using Producer
= IpdlProducer
<ActorP
>;
560 using Consumer
= IpdlConsumer
<ActorC
>;
562 UniquePtr
<Producer
> TakeProducer() { return std::move(mProducer
); }
563 UniquePtr
<Consumer
> TakeConsumer() { return std::move(mConsumer
); }
566 * Create an IpdlQueue where the given actor is a producer and its
567 * reciprocal is the consumer.
568 * The reciprocal actor type must be typedefed in ActorC as OtherSideActor.
569 * For example, WebGLChild::OtherSideActor is WebGLParent.
571 static UniquePtr
<IpdlQueue
<ActorP
, ActorC
>> Create(ActorP
* aProducerActor
) {
572 static_assert(std::is_same
<typename
ActorP::OtherSideActor
, ActorC
>::value
,
573 "ActorP's reciprocal must be ActorC");
574 static_assert(std::is_same
<typename
ActorC::OtherSideActor
, ActorP
>::value
,
575 "ActorC's reciprocal must be ActorP");
577 auto id
= NewIpdlQueueId();
578 return WrapUnique(new IpdlQueue
<ActorP
, ActorC
>(
579 std::move(WrapUnique(new Producer(id
, aProducerActor
))),
580 std::move(WrapUnique(new Consumer(id
)))));
584 * Create an IpdlQueue where the given actor is a consumer and its
585 * reciprocal is the producer.
586 * The reciprocal actor type must be typedefed in ActorC as OtherSideActor.
587 * For example, WebGLChild::OtherSideActor is WebGLParent.
589 static UniquePtr
<IpdlQueue
<ActorP
, ActorC
>> Create(ActorC
* aConsumerActor
) {
590 static_assert(std::is_same
<typename
ActorP::OtherSideActor
, ActorC
>::value
,
591 "ActorP's reciprocal must be ActorC");
592 static_assert(std::is_same
<typename
ActorC::OtherSideActor
, ActorP
>::value
,
593 "ActorC's reciprocal must be ActorP");
595 auto id
= NewIpdlQueueId();
596 return WrapUnique(new IpdlQueue
<ActorP
, ActorC
>(
597 std::move(WrapUnique(new Producer(id
))),
598 std::move(WrapUnique(new Consumer(id
, aConsumerActor
)))));
602 IpdlQueue(UniquePtr
<Producer
>&& aProducer
, UniquePtr
<Consumer
>&& aConsumer
)
603 : mProducer(std::move(aProducer
)), mConsumer(std::move(aConsumer
)) {}
605 UniquePtr
<Producer
> mProducer
;
606 UniquePtr
<Consumer
> mConsumer
;
613 template <typename Actor
>
614 struct IPDLParamTraits
<mozilla::dom::IpdlProducer
<Actor
>> {
615 typedef mozilla::dom::IpdlProducer
<Actor
> paramType
;
617 static void Write(IPC::Message
* aMsg
, IProtocol
* aActor
,
618 const paramType
& aParam
) {
619 MOZ_ASSERT(aParam
.mActor
== nullptr);
620 WriteIPDLParam(aMsg
, aActor
, aParam
.mId
);
623 static bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
624 IProtocol
* aActor
, paramType
* aResult
) {
625 aResult
->mActor
= static_cast<Actor
*>(aActor
);
626 return ReadIPDLParam(aMsg
, aIter
, aActor
, &aResult
->mId
);
630 template <typename Actor
>
631 struct IPDLParamTraits
<mozilla::dom::IpdlConsumer
<Actor
>> {
632 typedef mozilla::dom::IpdlConsumer
<Actor
> paramType
;
634 static void Write(IPC::Message
* aMsg
, IProtocol
* aActor
,
635 const paramType
& aParam
) {
636 MOZ_ASSERT(aParam
.mActor
== nullptr);
637 WriteIPDLParam(aMsg
, aActor
, aParam
.mId
);
638 WriteIPDLParam(aMsg
, aActor
, aParam
.mBuf
);
641 static bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
642 IProtocol
* aActor
, paramType
* aResult
) {
643 aResult
->mActor
= static_cast<Actor
*>(aActor
);
644 return ReadIPDLParam(aMsg
, aIter
, aActor
, &aResult
->mId
) &&
645 ReadIPDLParam(aMsg
, aIter
, aActor
, &aResult
->mBuf
);
650 struct IPDLParamTraits
<mozilla::dom::IpdlQueueBuffer
> {
651 typedef mozilla::dom::IpdlQueueBuffer paramType
;
653 static void Write(IPC::Message
* aMsg
, IProtocol
* aActor
,
654 const paramType
& aParam
) {
655 WriteParam(aMsg
, aParam
.id
);
656 WriteParam(aMsg
, aParam
.data
);
659 static bool Read(const IPC::Message
* aMsg
, PickleIterator
* aIter
,
660 IProtocol
* aActor
, paramType
* aResult
) {
661 return ReadParam(aMsg
, aIter
, &aResult
->id
) &&
662 ReadParam(aMsg
, aIter
, &aResult
->data
);
667 } // namespace mozilla
669 #endif // IPDLQUEUE_H_