Bug 1527712 - Move I/O in nsFileChannel::OpenContentStream to a background thread, r=kershaw,dragana

Differential Revision: https://phabricator.services.mozilla.com/D23352

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Honza Bambas 2019-03-19 15:25:47 +00:00
Родитель 222f7413b2
Коммит 44f895e1f6
4 изменённых файлов: 125 добавлений и 16 удалений

Просмотреть файл

@ -250,13 +250,45 @@ nsresult nsBaseChannel::BeginPumpingData() {
nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, 0, 0, true,
target);
if (NS_SUCCEEDED(rv)) {
mPumpingData = true;
mRequest = mPump;
rv = mPump->AsyncRead(this, nullptr);
if (NS_FAILED(rv)) {
return rv;
}
return rv;
mPumpingData = true;
mRequest = mPump;
rv = mPump->AsyncRead(this, nullptr);
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<BlockingPromise> promise;
rv = ListenerBlockingPromise(getter_AddRefs(promise));
if (NS_FAILED(rv)) {
return rv;
}
if (promise) {
mPump->Suspend();
RefPtr<nsBaseChannel> self(this);
nsCOMPtr<nsISerialEventTarget> serialTarget(do_QueryInterface(target));
MOZ_ASSERT(serialTarget);
promise->Then(serialTarget, __func__,
[self, this](nsresult rv) {
MOZ_ASSERT(mPump);
MOZ_ASSERT(NS_SUCCEEDED(rv));
mPump->Resume();
},
[self, this](nsresult rv) {
MOZ_ASSERT(mPump);
MOZ_ASSERT(NS_FAILED(rv));
Cancel(rv);
mPump->Resume();
});
}
return NS_OK;
}
void nsBaseChannel::HandleAsyncRedirect(nsIChannel *newChannel) {

Просмотреть файл

@ -7,6 +7,7 @@
#define nsBaseChannel_h__
#include "mozilla/net/NeckoTargetHolder.h"
#include "mozilla/MozPromise.h"
#include "nsString.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
@ -73,6 +74,8 @@ class nsBaseChannel
virtual ~nsBaseChannel();
using BlockingPromise = mozilla::MozPromise<nsresult, nsresult, true>;
private:
// Implemented by subclass to supply data stream. The parameter, async, is
// true when called from nsIChannel::AsyncOpen and false otherwise. When
@ -106,6 +109,20 @@ class nsBaseChannel
return NS_ERROR_NOT_IMPLEMENTED;
}
// This method may return a promise that will keep the input stream pump
// suspended until the promise is resolved or rejected. On resolution the
// pump is resumed. On rejection the channel is canceled with the resulting
// error and then the pump is also resumed to propagate the error to the
// channel listener. Use it to do any asynchronous/background tasks you need
// to finish prior calling OnStartRequest of the listener. This method is
// called right after OpenContentStream() with async == true, after the input
// stream pump has already been called asyncRead().
virtual nsresult ListenerBlockingPromise(BlockingPromise **aPromise) {
NS_ENSURE_ARG(aPromise);
*aPromise = nullptr;
return NS_OK;
}
// The basechannel calls this method from its OnTransportStatus method to
// determine whether to call nsIProgressEventSink::OnStatus in addition to
// nsIProgressEventSink::OnProgress. This method may be overriden by the

Просмотреть файл

@ -29,6 +29,7 @@
#include "prio.h"
#include <algorithm>
#include "mozilla/TaskQueue.h"
#include "mozilla/Unused.h"
using namespace mozilla;
@ -391,20 +392,20 @@ nsresult nsFileChannel::OpenContentStream(bool async, nsIInputStream **result,
EnableSynthesizedProgressEvents(true);
// fixup content length and type
if (mContentLength < 0) {
int64_t size;
rv = file->GetFileSize(&size);
// when we are called from asyncOpen, the content length fixup will be
// performed on a background thread and block the listener invocation via
// ListenerBlockingPromise method
if (!async && mContentLength < 0) {
rv = FixupContentLength(false);
if (NS_FAILED(rv)) {
if (async && (NS_ERROR_FILE_NOT_FOUND == rv ||
NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv)) {
size = 0;
} else {
return rv;
}
return rv;
}
mContentLength = size;
}
if (!contentType.IsEmpty()) SetContentType(contentType);
if (!contentType.IsEmpty()) {
SetContentType(contentType);
}
}
*result = nullptr;
@ -412,6 +413,59 @@ nsresult nsFileChannel::OpenContentStream(bool async, nsIInputStream **result,
return NS_OK;
}
nsresult nsFileChannel::ListenerBlockingPromise(BlockingPromise **aPromise) {
NS_ENSURE_ARG(aPromise);
*aPromise = nullptr;
if (mContentLength >= 0) {
return NS_OK;
}
nsCOMPtr<nsIEventTarget> sts(
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
if (!sts) {
return FixupContentLength(true);
}
RefPtr<TaskQueue> taskQueue = new TaskQueue(sts.forget());
RefPtr<nsFileChannel> self = this;
RefPtr<BlockingPromise> promise =
mozilla::InvokeAsync(taskQueue, __func__, [self{std::move(self)}]() {
nsresult rv = self->FixupContentLength(true);
if (NS_FAILED(rv)) {
return BlockingPromise::CreateAndReject(rv, __func__);
}
return BlockingPromise::CreateAndResolve(NS_OK, __func__);
});
promise.forget(aPromise);
return NS_OK;
}
nsresult nsFileChannel::FixupContentLength(bool async) {
MOZ_ASSERT(mContentLength < 0);
nsCOMPtr<nsIFile> file;
nsresult rv = GetFile(getter_AddRefs(file));
if (NS_FAILED(rv)) {
return rv;
}
int64_t size;
rv = file->GetFileSize(&size);
if (NS_FAILED(rv)) {
if (async && (NS_ERROR_FILE_NOT_FOUND == rv ||
NS_ERROR_FILE_TARGET_DOES_NOT_EXIST == rv)) {
size = 0;
} else {
return rv;
}
}
mContentLength = size;
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsFileChannel::nsISupports

Просмотреть файл

@ -37,7 +37,13 @@ class nsFileChannel : public nsBaseChannel,
virtual MOZ_MUST_USE nsresult OpenContentStream(
bool async, nsIInputStream **result, nsIChannel **channel) override;
// Implementing the pump blocking promise to fixup content length on a
// background thread prior to calling on mListener
virtual nsresult ListenerBlockingPromise(BlockingPromise **promise) override;
private:
nsresult FixupContentLength(bool async);
nsCOMPtr<nsIInputStream> mUploadStream;
int64_t mUploadLength;
nsCOMPtr<nsIURI> mFileURI;