зеркало из 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,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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче