зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
222f7413b2
Коммит
44f895e1f6
|
@ -250,15 +250,47 @@ nsresult nsBaseChannel::BeginPumpingData() {
|
||||||
nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
|
nsCOMPtr<nsIEventTarget> target = GetNeckoTarget();
|
||||||
rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, 0, 0, true,
|
rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, 0, 0, true,
|
||||||
target);
|
target);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
mPumpingData = true;
|
mPumpingData = true;
|
||||||
mRequest = mPump;
|
mRequest = mPump;
|
||||||
rv = mPump->AsyncRead(this, nullptr);
|
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;
|
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) {
|
void nsBaseChannel::HandleAsyncRedirect(nsIChannel *newChannel) {
|
||||||
NS_ASSERTION(!mPumpingData, "Shouldn't have gotten here");
|
NS_ASSERTION(!mPumpingData, "Shouldn't have gotten here");
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#define nsBaseChannel_h__
|
#define nsBaseChannel_h__
|
||||||
|
|
||||||
#include "mozilla/net/NeckoTargetHolder.h"
|
#include "mozilla/net/NeckoTargetHolder.h"
|
||||||
|
#include "mozilla/MozPromise.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
@ -73,6 +74,8 @@ class nsBaseChannel
|
||||||
|
|
||||||
virtual ~nsBaseChannel();
|
virtual ~nsBaseChannel();
|
||||||
|
|
||||||
|
using BlockingPromise = mozilla::MozPromise<nsresult, nsresult, true>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Implemented by subclass to supply data stream. The parameter, async, is
|
// Implemented by subclass to supply data stream. The parameter, async, is
|
||||||
// true when called from nsIChannel::AsyncOpen and false otherwise. When
|
// true when called from nsIChannel::AsyncOpen and false otherwise. When
|
||||||
|
@ -106,6 +109,20 @@ class nsBaseChannel
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
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
|
// The basechannel calls this method from its OnTransportStatus method to
|
||||||
// determine whether to call nsIProgressEventSink::OnStatus in addition to
|
// determine whether to call nsIProgressEventSink::OnStatus in addition to
|
||||||
// nsIProgressEventSink::OnProgress. This method may be overriden by the
|
// nsIProgressEventSink::OnProgress. This method may be overriden by the
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "prio.h"
|
#include "prio.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "mozilla/TaskQueue.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
@ -391,7 +392,65 @@ nsresult nsFileChannel::OpenContentStream(bool async, nsIInputStream **result,
|
||||||
EnableSynthesizedProgressEvents(true);
|
EnableSynthesizedProgressEvents(true);
|
||||||
|
|
||||||
// fixup content length and type
|
// fixup content length and type
|
||||||
if (mContentLength < 0) {
|
|
||||||
|
// 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)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!contentType.IsEmpty()) {
|
||||||
|
SetContentType(contentType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = nullptr;
|
||||||
|
stream.swap(*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;
|
int64_t size;
|
||||||
rv = file->GetFileSize(&size);
|
rv = file->GetFileSize(&size);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
|
@ -403,12 +462,7 @@ nsresult nsFileChannel::OpenContentStream(bool async, nsIInputStream **result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mContentLength = size;
|
mContentLength = size;
|
||||||
}
|
|
||||||
if (!contentType.IsEmpty()) SetContentType(contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = nullptr;
|
|
||||||
stream.swap(*result);
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,13 @@ class nsFileChannel : public nsBaseChannel,
|
||||||
virtual MOZ_MUST_USE nsresult OpenContentStream(
|
virtual MOZ_MUST_USE nsresult OpenContentStream(
|
||||||
bool async, nsIInputStream **result, nsIChannel **channel) override;
|
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:
|
private:
|
||||||
|
nsresult FixupContentLength(bool async);
|
||||||
|
|
||||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||||
int64_t mUploadLength;
|
int64_t mUploadLength;
|
||||||
nsCOMPtr<nsIURI> mFileURI;
|
nsCOMPtr<nsIURI> mFileURI;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче