Files
tubestation/ipc/glue/InputStreamUtils.cpp
Nika Layzell d1b9f58acb Bug 1754004 - Part 1: Switch IPCStream to use DataPipe instead of P{ChildToParent,ParentToChild}Stream, r=asuth,necko-reviewers,kershaw
This gives us various positive benefits, such as using a shared memory ring
buffer for faster communication, not having data streaming being bound to the
thread which transferred the nsIInputStream (which is often the main thread),
and the ability for some backpressure to be applied to data streaming.

After this change, the "delayed start" parameter for IPCStream serialization is
less relevant, as backpressure will serve a similar purpose. It will still be
used to determine whether or not to use RemoteLazyInputStream when serializing
from the parent process.

Differential Revision: https://phabricator.services.mozilla.com/D141038
2022-05-13 14:16:09 +00:00

270 lines
9.6 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InputStreamUtils.h"
#include "nsIIPCSerializableInputStream.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/quota/DecryptingInputStream_impl.h"
#include "mozilla/dom/quota/IPCStreamCipherStrategy.h"
#include "mozilla/ipc/DataPipe.h"
#include "mozilla/InputStreamLengthHelper.h"
#include "mozilla/RemoteLazyInputStream.h"
#include "mozilla/RemoteLazyInputStreamChild.h"
#include "mozilla/RemoteLazyInputStreamStorage.h"
#include "mozilla/SlicedInputStream.h"
#include "mozilla/InputStreamLengthWrapper.h"
#include "nsBufferedStreams.h"
#include "nsComponentManagerUtils.h"
#include "nsDebug.h"
#include "nsFileStreams.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsID.h"
#include "nsIMIMEInputStream.h"
#include "nsIMultiplexInputStream.h"
#include "nsIPipe.h"
#include "nsMIMEInputStream.h"
#include "nsMultiplexInputStream.h"
#include "nsNetCID.h"
#include "nsStreamUtils.h"
#include "nsStringStream.h"
#include "nsXULAppAPI.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace mozilla {
namespace ipc {
namespace {
template <typename M>
void SerializeInputStreamInternal(nsIInputStream* aInputStream,
InputStreamParams& aParams,
nsTArray<FileDescriptor>& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, M* aManager) {
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aManager);
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
do_QueryInterface(aInputStream);
if (!serializable) {
MOZ_CRASH("Input stream is not serializable!");
}
serializable->Serialize(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
if (aParams.type() == InputStreamParams::T__None) {
MOZ_CRASH("Serialize failed!");
}
}
template <typename M>
void SerializeInputStreamAsPipeInternal(nsIInputStream* aInputStream,
InputStreamParams& aParams,
bool aDelayedStart, M* aManager) {
MOZ_ASSERT(aInputStream);
MOZ_ASSERT(aManager);
// Let's try to take the length using InputStreamLengthHelper. If the length
// cannot be taken synchronously, and its length is needed, the stream needs
// to be fully copied in memory on the deserialization side.
int64_t length;
if (!InputStreamLengthHelper::GetSyncLength(aInputStream, &length)) {
length = -1;
}
RefPtr<DataPipeSender> sender;
RefPtr<DataPipeReceiver> receiver;
nsresult rv = NewDataPipe(kDefaultDataPipeCapacity, getter_AddRefs(sender),
getter_AddRefs(receiver));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsCOMPtr<nsIEventTarget> target =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
rv =
NS_AsyncCopy(aInputStream, sender, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
kDefaultDataPipeCapacity, nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
aParams = DataPipeReceiverStreamParams(receiver);
if (length != -1) {
aParams = InputStreamLengthWrapperParams(aParams, length, false);
}
}
} // namespace
void InputStreamHelper::SerializedComplexity(nsIInputStream* aInputStream,
uint32_t aMaxSize,
uint32_t* aSizeUsed,
uint32_t* aPipes,
uint32_t* aTransferables) {
MOZ_ASSERT(aInputStream);
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
do_QueryInterface(aInputStream);
if (!serializable) {
MOZ_CRASH("Input stream is not serializable!");
}
serializable->SerializedComplexity(aMaxSize, aSizeUsed, aPipes,
aTransferables);
}
void InputStreamHelper::SerializeInputStream(
nsIInputStream* aInputStream, InputStreamParams& aParams,
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
uint32_t aMaxSize, uint32_t* aSizeUsed,
ParentToChildStreamActorManager* aManager) {
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
aDelayedStart, aMaxSize, aSizeUsed, aManager);
}
void InputStreamHelper::SerializeInputStream(
nsIInputStream* aInputStream, InputStreamParams& aParams,
nsTArray<FileDescriptor>& aFileDescriptors, bool aDelayedStart,
uint32_t aMaxSize, uint32_t* aSizeUsed,
ChildToParentStreamActorManager* aManager) {
SerializeInputStreamInternal(aInputStream, aParams, aFileDescriptors,
aDelayedStart, aMaxSize, aSizeUsed, aManager);
}
void InputStreamHelper::SerializeInputStreamAsPipe(
nsIInputStream* aInputStream, InputStreamParams& aParams,
bool aDelayedStart, ParentToChildStreamActorManager* aManager) {
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
aManager);
}
void InputStreamHelper::SerializeInputStreamAsPipe(
nsIInputStream* aInputStream, InputStreamParams& aParams,
bool aDelayedStart, ChildToParentStreamActorManager* aManager) {
SerializeInputStreamAsPipeInternal(aInputStream, aParams, aDelayedStart,
aManager);
}
already_AddRefed<nsIInputStream> InputStreamHelper::DeserializeInputStream(
const InputStreamParams& aParams,
const nsTArray<FileDescriptor>& aFileDescriptors) {
if (aParams.type() == InputStreamParams::TRemoteLazyInputStreamParams) {
const RemoteLazyInputStreamParams& params =
aParams.get_RemoteLazyInputStreamParams();
// RemoteLazyInputStreamRefs are not deserializable on the parent side,
// because the parent is the only one that has a copy of the original stream
// in the RemoteLazyInputStreamStorage.
if (params.type() ==
RemoteLazyInputStreamParams::TRemoteLazyInputStreamRef) {
MOZ_ASSERT(XRE_IsParentProcess());
const RemoteLazyInputStreamRef& ref =
params.get_RemoteLazyInputStreamRef();
auto storage = RemoteLazyInputStreamStorage::Get().unwrapOr(nullptr);
MOZ_ASSERT(storage);
nsCOMPtr<nsIInputStream> stream;
storage->GetStream(ref.id(), ref.start(), ref.length(),
getter_AddRefs(stream));
return stream.forget();
}
// parent -> child serializations receive an RemoteLazyInputStream actor.
MOZ_ASSERT(params.type() ==
RemoteLazyInputStreamParams::TPRemoteLazyInputStreamChild);
RemoteLazyInputStreamChild* actor =
static_cast<RemoteLazyInputStreamChild*>(
params.get_PRemoteLazyInputStreamChild());
nsCOMPtr<nsIInputStream> stream = actor->CreateStream();
return stream.forget();
}
if (aParams.type() == InputStreamParams::TDataPipeReceiverStreamParams) {
const DataPipeReceiverStreamParams& pipeParams =
aParams.get_DataPipeReceiverStreamParams();
return do_AddRef(pipeParams.pipe());
}
nsCOMPtr<nsIIPCSerializableInputStream> serializable;
switch (aParams.type()) {
case InputStreamParams::TStringInputStreamParams: {
nsCOMPtr<nsIInputStream> stream;
NS_NewCStringInputStream(getter_AddRefs(stream), ""_ns);
serializable = do_QueryInterface(stream);
} break;
case InputStreamParams::TFileInputStreamParams: {
nsCOMPtr<nsIFileInputStream> stream;
nsFileInputStream::Create(nullptr, NS_GET_IID(nsIFileInputStream),
getter_AddRefs(stream));
serializable = do_QueryInterface(stream);
} break;
case InputStreamParams::TBufferedInputStreamParams: {
nsCOMPtr<nsIBufferedInputStream> stream;
nsBufferedInputStream::Create(nullptr, NS_GET_IID(nsIBufferedInputStream),
getter_AddRefs(stream));
serializable = do_QueryInterface(stream);
} break;
case InputStreamParams::TMIMEInputStreamParams: {
nsCOMPtr<nsIMIMEInputStream> stream;
nsMIMEInputStreamConstructor(nullptr, NS_GET_IID(nsIMIMEInputStream),
getter_AddRefs(stream));
serializable = do_QueryInterface(stream);
} break;
case InputStreamParams::TMultiplexInputStreamParams: {
nsCOMPtr<nsIMultiplexInputStream> stream;
nsMultiplexInputStreamConstructor(
nullptr, NS_GET_IID(nsIMultiplexInputStream), getter_AddRefs(stream));
serializable = do_QueryInterface(stream);
} break;
case InputStreamParams::TSlicedInputStreamParams:
serializable = new SlicedInputStream();
break;
case InputStreamParams::TInputStreamLengthWrapperParams:
serializable = new InputStreamLengthWrapper();
break;
case InputStreamParams::TEncryptedFileInputStreamParams:
serializable = new dom::quota::DecryptingInputStream<
dom::quota::IPCStreamCipherStrategy>();
break;
default:
MOZ_ASSERT(false, "Unknown params!");
return nullptr;
}
MOZ_ASSERT(serializable);
if (!serializable->Deserialize(aParams, aFileDescriptors)) {
MOZ_ASSERT(false, "Deserialize failed!");
return nullptr;
}
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(serializable);
MOZ_ASSERT(stream);
return stream.forget();
}
} // namespace ipc
} // namespace mozilla