Bug 928340 - nsIAsyncStreamCopier2 and implementation. r=mayhemer

This commit is contained in:
David Rajchenbach-Teller 2014-04-09 02:26:00 +02:00
Родитель e38f4a6963
Коммит b12935d64a
5 изменённых файлов: 279 добавлений и 25 удалений

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

@ -12,6 +12,7 @@ XPIDL_SOURCES += [
'nsIApplicationCacheService.idl',
'nsIArrayBufferInputStream.idl',
'nsIAsyncStreamCopier.idl',
'nsIAsyncStreamCopier2.idl',
'nsIAsyncVerifyRedirectCallback.idl',
'nsIAuthInformation.idl',
'nsIAuthModule.idl',

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

@ -9,6 +9,7 @@ interface nsIOutputStream;
interface nsIRequestObserver;
interface nsIEventTarget;
// You should prefer nsIAsyncStreamCopier2
[scriptable, uuid(5a19ca27-e041-4aca-8287-eb248d4c50c0)]
interface nsIAsyncStreamCopier : nsIRequest
{
@ -36,7 +37,9 @@ interface nsIAsyncStreamCopier : nsIRequest
* @param aCloseSink
* true if aSink should be closed after copying.
*
* NOTE: at least one of the streams must be buffered.
* NOTE: at least one of the streams must be buffered. If you do not know
* whether your streams are buffered, you should use nsIAsyncStreamCopier2
* instead.
*/
void init(in nsIInputStream aSource,
in nsIOutputStream aSink,

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

@ -0,0 +1,59 @@
/* 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 "nsIRequest.idl"
interface nsIInputStream;
interface nsIOutputStream;
interface nsIRequestObserver;
interface nsIEventTarget;
[scriptable, uuid(a5b2decf-4ede-4801-8b38-e5fe5db46bf2)]
interface nsIAsyncStreamCopier2 : nsIRequest
{
/**
* Initialize the stream copier.
*
* If neither the source nor the sink are buffered, buffering will
* be automatically added to the sink.
*
*
* @param aSource
* contains the data to be copied.
* @param aSink
* specifies the destination for the data.
* @param aTarget
* specifies the thread on which the copy will occur. a null value
* is permitted and will cause the copy to occur on an unspecified
* background thread.
* @param aChunkSize
* specifies how many bytes to read/write at a time. this controls
* the granularity of the copying. it should match the segment size
* of the "buffered" streams involved.
* @param aCloseSource
* true if aSource should be closed after copying (this is generally
* the desired behavior).
* @param aCloseSink
* true if aSink should be closed after copying (this is generally
* the desired behavior).
*/
void init(in nsIInputStream aSource,
in nsIOutputStream aSink,
in nsIEventTarget aTarget,
in unsigned long aChunkSize,
in boolean aCloseSource,
in boolean aCloseSink);
/**
* asyncCopy triggers the start of the copy. The observer will be notified
* when the copy completes.
*
* @param aObserver
* receives notifications.
* @param aObserverContext
* passed to observer methods.
*/
void asyncCopy(in nsIRequestObserver aObserver,
in nsISupports aObserverContext);
};

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

@ -6,6 +6,7 @@
#include "nsIOService.h"
#include "nsIEventTarget.h"
#include "nsStreamUtils.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "prlog.h"
@ -20,6 +21,44 @@ static PRLogModuleInfo *gStreamCopierLog = nullptr;
#endif
#define LOG(args) PR_LOG(gStreamCopierLog, PR_LOG_DEBUG, args)
/**
* An event used to perform initialization off the main thread.
*/
class AsyncApplyBufferingPolicyEvent MOZ_FINAL: public nsRunnable
{
public:
/**
* @param aCopier
* The nsAsyncStreamCopier requesting the information.
*/
AsyncApplyBufferingPolicyEvent(nsAsyncStreamCopier* aCopier)
: mCopier(aCopier)
, mTarget(NS_GetCurrentThread())
{ }
NS_METHOD Run()
{
nsresult rv = mCopier->ApplyBufferingPolicy();
if (NS_FAILED(rv)) {
mCopier->Cancel(rv);
return NS_OK;
}
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mCopier, &nsAsyncStreamCopier::AsyncCopyInternal);
rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
mCopier->Cancel(rv);
}
return NS_OK;
}
private:
nsRefPtr<nsAsyncStreamCopier> mCopier;
nsCOMPtr<nsIEventTarget> mTarget;
};
//-----------------------------------------------------------------------------
nsAsyncStreamCopier::nsAsyncStreamCopier()
@ -28,6 +67,7 @@ nsAsyncStreamCopier::nsAsyncStreamCopier()
, mChunkSize(nsIOService::gDefaultSegmentSize)
, mStatus(NS_OK)
, mIsPending(false)
, mShouldSniffBuffering(false)
{
#if defined(PR_LOGGING)
if (!gStreamCopierLog)
@ -50,6 +90,12 @@ nsAsyncStreamCopier::IsComplete(nsresult *status)
return !mIsPending;
}
nsIRequest*
nsAsyncStreamCopier::AsRequest()
{
return static_cast<nsIRequest*>(static_cast<nsIAsyncStreamCopier*>(this));
}
void
nsAsyncStreamCopier::Complete(nsresult status)
{
@ -73,7 +119,7 @@ nsAsyncStreamCopier::Complete(nsresult status)
if (observer) {
LOG((" calling OnStopRequest [status=%x]\n", status));
observer->OnStopRequest(this, ctx, status);
observer->OnStopRequest(AsRequest(), ctx, status);
}
}
@ -88,9 +134,19 @@ nsAsyncStreamCopier::OnAsyncCopyComplete(void *closure, nsresult status)
//-----------------------------------------------------------------------------
// nsISupports
NS_IMPL_ISUPPORTS2(nsAsyncStreamCopier,
nsIRequest,
nsIAsyncStreamCopier)
// We cannot use simply NS_IMPL_ISUPPORTSx as both
// nsIAsyncStreamCopier and nsIAsyncStreamCopier2 implement nsIRequest
NS_IMPL_ADDREF(nsAsyncStreamCopier)
NS_IMPL_RELEASE(nsAsyncStreamCopier)
NS_INTERFACE_TABLE_HEAD(nsAsyncStreamCopier)
NS_INTERFACE_TABLE_BEGIN
NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier)
NS_INTERFACE_TABLE_ENTRY(nsAsyncStreamCopier, nsIAsyncStreamCopier2)
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsIRequest, nsIAsyncStreamCopier)
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsAsyncStreamCopier, nsISupports, nsIAsyncStreamCopier)
NS_INTERFACE_TABLE_END
NS_INTERFACE_TABLE_TAIL
//-----------------------------------------------------------------------------
// nsIRequest
@ -178,6 +234,38 @@ nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup *aLoadGroup)
return NS_OK;
}
nsresult
nsAsyncStreamCopier::InitInternal(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
uint32_t chunkSize,
bool closeSource,
bool closeSink)
{
NS_ASSERTION(!mSource && !mSink, "Init() called more than once");
if (chunkSize == 0) {
chunkSize = nsIOService::gDefaultSegmentSize;
}
mChunkSize = chunkSize;
mSource = source;
mSink = sink;
mCloseSource = closeSource;
mCloseSink = closeSink;
if (target) {
mTarget = target;
} else {
nsresult rv;
mTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsIAsyncStreamCopier
@ -192,28 +280,71 @@ nsAsyncStreamCopier::Init(nsIInputStream *source,
bool closeSink)
{
NS_ASSERTION(sourceBuffered || sinkBuffered, "at least one stream must be buffered");
if (chunkSize == 0)
chunkSize = nsIOService::gDefaultSegmentSize;
mChunkSize = chunkSize;
mSource = source;
mSink = sink;
mCloseSource = closeSource;
mCloseSink = closeSink;
mMode = sourceBuffered ? NS_ASYNCCOPY_VIA_READSEGMENTS
: NS_ASYNCCOPY_VIA_WRITESEGMENTS;
if (target)
mTarget = target;
else {
nsresult rv;
mTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
}
//-----------------------------------------------------------------------------
// nsIAsyncStreamCopier2
NS_IMETHODIMP
nsAsyncStreamCopier::Init(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
uint32_t chunkSize,
bool closeSource,
bool closeSink)
{
mShouldSniffBuffering = true;
return InitInternal(source, sink, target, chunkSize, closeSource, closeSink);
}
/**
* Detect whether the input or the output stream is buffered,
* bufferize one of them if neither is buffered.
*/
nsresult
nsAsyncStreamCopier::ApplyBufferingPolicy()
{
// This function causes I/O, it must not be executed on the main
// thread.
MOZ_ASSERT(!NS_IsMainThread());
if (NS_OutputStreamIsBuffered(mSink)) {
// Sink is buffered, no need to perform additional buffering
mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
return NS_OK;
}
if (NS_InputStreamIsBuffered(mSource)) {
// Source is buffered, no need to perform additional buffering
mMode = NS_ASYNCCOPY_VIA_READSEGMENTS;
return NS_OK;
}
// No buffering, let's buffer the sink
nsresult rv;
nsCOMPtr<nsIBufferedOutputStream> sink =
do_CreateInstance(NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
rv = sink->Init(mSink, mChunkSize);
if (NS_FAILED(rv)) {
return rv;
}
mMode = NS_ASYNCCOPY_VIA_WRITESEGMENTS;
mSink = sink;
return NS_OK;
}
//-----------------------------------------------------------------------------
// Both nsIAsyncStreamCopier and nsIAsyncStreamCopier2
NS_IMETHODIMP
nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx)
{
@ -233,11 +364,46 @@ nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx)
mIsPending = true;
if (mObserver) {
rv = mObserver->OnStartRequest(this, nullptr);
rv = mObserver->OnStartRequest(AsRequest(), nullptr);
if (NS_FAILED(rv))
Cancel(rv);
}
if (!mShouldSniffBuffering) {
// No buffer sniffing required, let's proceed
AsyncCopyInternal();
return NS_OK;
}
if (NS_IsMainThread()) {
// Don't perform buffer sniffing on the main thread
nsCOMPtr<AsyncApplyBufferingPolicyEvent> event
= new AsyncApplyBufferingPolicyEvent(this);
rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
Cancel(rv);
}
return NS_OK;
}
// We're not going to block the main thread, so let's sniff here
rv = ApplyBufferingPolicy();
if (NS_FAILED(rv)) {
Cancel(rv);
}
AsyncCopyInternal();
return NS_OK;
}
// Launch async copy.
// All errors are reported through the observer.
void
nsAsyncStreamCopier::AsyncCopyInternal()
{
MOZ_ASSERT(mMode == NS_ASYNCCOPY_VIA_READSEGMENTS
|| mMode == NS_ASYNCCOPY_VIA_WRITESEGMENTS);
nsresult rv;
// we want to receive progress notifications; release happens in
// OnAsyncCopyComplete.
NS_ADDREF_THIS();
@ -251,6 +417,6 @@ nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx)
NS_RELEASE_THIS();
Cancel(rv);
}
return NS_OK;
}

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

@ -6,6 +6,7 @@
#define nsAsyncStreamCopier_h__
#include "nsIAsyncStreamCopier.h"
#include "nsIAsyncStreamCopier2.h"
#include "mozilla/Mutex.h"
#include "nsStreamUtils.h"
#include "nsCOMPtr.h"
@ -14,13 +15,23 @@ class nsIRequestObserver;
//-----------------------------------------------------------------------------
class nsAsyncStreamCopier : public nsIAsyncStreamCopier
class nsAsyncStreamCopier : public nsIAsyncStreamCopier, nsIAsyncStreamCopier2
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIREQUEST
NS_DECL_NSIASYNCSTREAMCOPIER
// nsIAsyncStreamCopier2
// We declare it by hand instead of NS_DECL_NSIASYNCSTREAMCOPIER2
// as nsIAsyncStreamCopier2 duplicates methods of nsIAsyncStreamCopier
NS_IMETHOD Init(nsIInputStream *aSource,
nsIOutputStream *aSink,
nsIEventTarget *aTarget,
uint32_t aChunkSize,
bool aCloseSource,
bool aCloseSink);
nsAsyncStreamCopier();
virtual ~nsAsyncStreamCopier();
@ -31,9 +42,19 @@ public:
void Complete(nsresult status);
private:
nsresult InitInternal(nsIInputStream *source,
nsIOutputStream *sink,
nsIEventTarget *target,
uint32_t chunkSize,
bool closeSource,
bool closeSink);
static void OnAsyncCopyComplete(void *, nsresult);
void AsyncCopyInternal();
nsresult ApplyBufferingPolicy();
nsIRequest* AsRequest();
nsCOMPtr<nsIInputStream> mSource;
nsCOMPtr<nsIOutputStream> mSink;
@ -51,6 +72,10 @@ private:
bool mIsPending;
bool mCloseSource;
bool mCloseSink;
bool mShouldSniffBuffering;
friend class ProceedWithAsyncCopy;
friend class AsyncApplyBufferingPolicyEvent;
};
#endif // !nsAsyncStreamCopier_h__