diff --git a/mozilla/content/base/src/nsImageLoadingContent.cpp b/mozilla/content/base/src/nsImageLoadingContent.cpp index 84597034687..2b60270fc6f 100644 --- a/mozilla/content/base/src/nsImageLoadingContent.cpp +++ b/mozilla/content/base/src/nsImageLoadingContent.cpp @@ -708,7 +708,7 @@ nsImageLoadingContent::FireEvent(const nsAString& aEventType) rv = eventQ->PostEvent(evt); - if (rv == PR_SUCCESS) { + if (NS_SUCCEEDED(rv)) { // Add the dummy request (the ImageEvent) to the load group only // after all the early returns here! loadGroup->AddRequest(evt, nsnull); diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp index 8f8dc629ebf..d03c1ffe90d 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.cpp +++ b/mozilla/dom/src/base/nsGlobalWindow.cpp @@ -3421,7 +3421,7 @@ GlobalWindowImpl::Close() rv = ev->PostCloseEvent(); if (NS_FAILED(rv)) { - delete ev; + PL_DestroyEvent(ev); } } else rv = NS_ERROR_OUT_OF_MEMORY; } diff --git a/mozilla/htmlparser/src/nsParser.cpp b/mozilla/htmlparser/src/nsParser.cpp index eb0bbf02662..76f5eb14412 100644 --- a/mozilla/htmlparser/src/nsParser.cpp +++ b/mozilla/htmlparser/src/nsParser.cpp @@ -219,41 +219,31 @@ For more details @see bugzilla bug 76722 struct nsParserContinueEvent : public PLEvent { - nsParserContinueEvent(nsIParser* aParser); - ~nsParserContinueEvent() { } + nsParserContinueEvent(nsParser* aParser) + { + NS_ADDREF(aParser); + PL_InitEvent(this, aParser, HandleEvent, DestroyEvent); + } - void HandleEvent() { - if (mParser) { - nsParser* parser = NS_STATIC_CAST(nsParser*, mParser); - parser->HandleParserContinueEvent(); - NS_RELEASE(mParser); - } - }; - - nsIParser* mParser; + ~nsParserContinueEvent() + { + nsParser *parser = (nsParser*) owner; + NS_RELEASE(parser); + } + + PR_STATIC_CALLBACK(void*) HandleEvent(PLEvent* aEvent) + { + nsParser *parser = (nsParser*) aEvent->owner; + parser->HandleParserContinueEvent(); + return nsnull; + } + + PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent* aEvent) + { + delete (nsParserContinueEvent*) aEvent; + } }; -static void PR_CALLBACK HandlePLEvent(nsParserContinueEvent* aEvent) -{ - NS_ASSERTION(nsnull != aEvent,"Event is null"); - aEvent->HandleEvent(); -} - -static void PR_CALLBACK DestroyPLEvent(nsParserContinueEvent* aEvent) -{ - NS_ASSERTION(nsnull != aEvent,"Event is null"); - delete aEvent; -} - -nsParserContinueEvent::nsParserContinueEvent(nsIParser* aParser) -{ - NS_ASSERTION(aParser, "null parameter"); - mParser = aParser; - PL_InitEvent(this, aParser, - (PLHandleEventProc) ::HandlePLEvent, - (PLDestroyEventProc) ::DestroyPLEvent); -} - //-------------- End ParseContinue Event Definition ------------------------ @@ -417,11 +407,14 @@ nsresult nsParser::PostContinueEvent() { if (!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT) && mEventQueue) { - nsParserContinueEvent* ev = new nsParserContinueEvent(NS_STATIC_CAST(nsIParser*, this)); - NS_ENSURE_TRUE(ev,NS_ERROR_OUT_OF_MEMORY); - NS_ADDREF(this); - mEventQueue->PostEvent(ev); - mFlags |= NS_PARSER_FLAG_PENDING_CONTINUE_EVENT; + nsParserContinueEvent* ev = new nsParserContinueEvent(this); + NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY); + if (NS_FAILED(mEventQueue->PostEvent(ev))) { + NS_ERROR("failed to post parser continuation event"); + PL_DestroyEvent(ev); + } + else + mFlags |= NS_PARSER_FLAG_PENDING_CONTINUE_EVENT; } return NS_OK; } diff --git a/mozilla/layout/base/nsFrameManager.cpp b/mozilla/layout/base/nsFrameManager.cpp index 5cdff410f61..c74f9f30bf8 100644 --- a/mozilla/layout/base/nsFrameManager.cpp +++ b/mozilla/layout/base/nsFrameManager.cpp @@ -1262,12 +1262,17 @@ FrameManager::CantRenderReplacedElement(nsIFrame* aFrame) // Create a new event ev = new CantRenderReplacedElementEvent(this, aFrame, mPresShell); - // Add the event to our linked list of posted events - ev->mNext = mPostedEvents; - mPostedEvents = ev; - // Post the event - eventQueue->PostEvent(ev); + rv = eventQueue->PostEvent(ev); + if (NS_FAILED(rv)) { + NS_ERROR("failed to post event"); + PL_DestroyEvent(ev); + } + else { + // Add the event to our linked list of posted events + ev->mNext = mPostedEvents; + mPostedEvents = ev; + } } } diff --git a/mozilla/layout/base/nsPresShell.cpp b/mozilla/layout/base/nsPresShell.cpp index 7d82884353b..dd01fcb253b 100644 --- a/mozilla/layout/base/nsPresShell.cpp +++ b/mozilla/layout/base/nsPresShell.cpp @@ -6391,13 +6391,18 @@ PresShell::PostReflowEvent() if (eventQueue != mReflowEventQueue && !mIsReflowing && mReflowCommands.Count() > 0) { ReflowEvent* ev = new ReflowEvent(NS_STATIC_CAST(nsIPresShell*, this)); - eventQueue->PostEvent(ev); - mReflowEventQueue = eventQueue; -#ifdef DEBUG - if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { - printf("\n*** PresShell::PostReflowEvent(), this=%p, event=%p\n", (void*)this, (void*)ev); + if (NS_FAILED(eventQueue->PostEvent(ev))) { + NS_ERROR("failed to post reflow event"); + PL_DestroyEvent(ev); } + else { + mReflowEventQueue = eventQueue; +#ifdef DEBUG + if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { + printf("\n*** PresShell::PostReflowEvent(), this=%p, event=%p\n", (void*)this, (void*)ev); + } #endif + } } } diff --git a/mozilla/layout/html/base/src/nsFrameManager.cpp b/mozilla/layout/html/base/src/nsFrameManager.cpp index 5cdff410f61..c74f9f30bf8 100644 --- a/mozilla/layout/html/base/src/nsFrameManager.cpp +++ b/mozilla/layout/html/base/src/nsFrameManager.cpp @@ -1262,12 +1262,17 @@ FrameManager::CantRenderReplacedElement(nsIFrame* aFrame) // Create a new event ev = new CantRenderReplacedElementEvent(this, aFrame, mPresShell); - // Add the event to our linked list of posted events - ev->mNext = mPostedEvents; - mPostedEvents = ev; - // Post the event - eventQueue->PostEvent(ev); + rv = eventQueue->PostEvent(ev); + if (NS_FAILED(rv)) { + NS_ERROR("failed to post event"); + PL_DestroyEvent(ev); + } + else { + // Add the event to our linked list of posted events + ev->mNext = mPostedEvents; + mPostedEvents = ev; + } } } diff --git a/mozilla/layout/html/base/src/nsPresShell.cpp b/mozilla/layout/html/base/src/nsPresShell.cpp index 7d82884353b..dd01fcb253b 100644 --- a/mozilla/layout/html/base/src/nsPresShell.cpp +++ b/mozilla/layout/html/base/src/nsPresShell.cpp @@ -6391,13 +6391,18 @@ PresShell::PostReflowEvent() if (eventQueue != mReflowEventQueue && !mIsReflowing && mReflowCommands.Count() > 0) { ReflowEvent* ev = new ReflowEvent(NS_STATIC_CAST(nsIPresShell*, this)); - eventQueue->PostEvent(ev); - mReflowEventQueue = eventQueue; -#ifdef DEBUG - if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { - printf("\n*** PresShell::PostReflowEvent(), this=%p, event=%p\n", (void*)this, (void*)ev); + if (NS_FAILED(eventQueue->PostEvent(ev))) { + NS_ERROR("failed to post reflow event"); + PL_DestroyEvent(ev); } + else { + mReflowEventQueue = eventQueue; +#ifdef DEBUG + if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) { + printf("\n*** PresShell::PostReflowEvent(), this=%p, event=%p\n", (void*)this, (void*)ev); + } #endif + } } } diff --git a/mozilla/mailnews/base/src/nsMsgPrintEngine.cpp b/mozilla/mailnews/base/src/nsMsgPrintEngine.cpp index 4940043d9a8..94437b333bc 100644 --- a/mozilla/mailnews/base/src/nsMsgPrintEngine.cpp +++ b/mozilla/mailnews/base/src/nsMsgPrintEngine.cpp @@ -763,7 +763,13 @@ FireEvent(nsMsgPrintEngine* aMPE, PLHandleEventProc handler, PLDestroyEventProc // The event owns the msgPrintEngine pointer now. NS_ADDREF(aMPE); - event_queue->PostEvent(event); + if (NS_FAILED(event_queue->PostEvent(event))) + { + NS_WARNING("Failed to post event"); + PL_DestroyEvent(event); + return PR_FALSE; + } + return PR_TRUE; } diff --git a/mozilla/mailnews/base/util/nsMsgProtocol.cpp b/mozilla/mailnews/base/util/nsMsgProtocol.cpp index 7c8163deeac..902a2471ead 100644 --- a/mozilla/mailnews/base/util/nsMsgProtocol.cpp +++ b/mozilla/mailnews/base/util/nsMsgProtocol.cpp @@ -862,7 +862,7 @@ nsresult nsMsgProtocol::PostMessage(nsIURI* url, nsIFileSpec *fileSpec) // nsMsgAsyncWriteProtocol subclass and related helper classes ///////////////////////////////////////////////////////////////////// -class nsMsgProtocolStreamProvider : public nsIOutputStreamNotify +class nsMsgProtocolStreamProvider : public nsIOutputStreamCallback { public: NS_DECL_ISUPPORTS @@ -877,7 +877,7 @@ public: } // - // nsIOutputStreamNotify implementation ... + // nsIOutputStreamCallback implementation ... // NS_IMETHODIMP OnOutputStreamReady(nsIAsyncOutputStream *aOutStream) { @@ -911,7 +911,7 @@ public: // try to write again... if (NS_SUCCEEDED(rv)) - rv = aOutStream->AsyncWait(this, 0, mMsgProtocol->mProviderEventQ); + rv = aOutStream->AsyncWait(this, 0, 0, mMsgProtocol->mProviderEventQ); NS_ASSERTION(NS_SUCCEEDED(rv) || rv == NS_BINDING_ABORTED, "unexpected error writing stream"); return NS_OK; @@ -925,7 +925,7 @@ protected: // XXX this probably doesn't need to be threadsafe NS_IMPL_THREADSAFE_ISUPPORTS1(nsMsgProtocolStreamProvider, - nsIOutputStreamNotify) + nsIOutputStreamCallback) class nsMsgFilePostHelper : public nsIStreamListener { @@ -1009,7 +1009,7 @@ NS_IMETHODIMP nsMsgFilePostHelper::OnDataAvailable(nsIRequest * /* aChannel */, // data to write (i.e. the pipe went empty). So resume the channel to kick // things off again. mProtInstance->mSuspendedWrite = PR_FALSE; - mProtInstance->mAsyncOutStream->AsyncWait(mProtInstance->mProvider, 0, + mProtInstance->mAsyncOutStream->AsyncWait(mProtInstance->mProvider, 0, 0, mProtInstance->mProviderEventQ); } @@ -1042,7 +1042,7 @@ NS_IMETHODIMP nsMsgAsyncWriteProtocol::Cancel(nsresult status) m_request->Cancel(status); if (mAsyncOutStream) - mAsyncOutStream->CloseEx(status); + mAsyncOutStream->CloseWithStatus(status); return NS_OK; } @@ -1293,17 +1293,17 @@ nsresult nsMsgAsyncWriteProtocol::SetupTransportState() if (NS_FAILED(rv)) return rv; // wait for the output stream to become writable - rv = mAsyncOutStream->AsyncWait(mProvider, 0, mProviderEventQ); - } // if m_transport - - return rv; + rv = mAsyncOutStream->AsyncWait(mProvider, 0, 0, mProviderEventQ); + } // if m_transport + + return rv; } nsresult nsMsgAsyncWriteProtocol::CloseSocket() { nsresult rv = NS_OK; if (mAsyncOutStream) - mAsyncOutStream->CloseEx(NS_BINDING_ABORTED); + mAsyncOutStream->CloseWithStatus(NS_BINDING_ABORTED); nsMsgProtocol::CloseSocket(); @@ -1357,7 +1357,7 @@ PRInt32 nsMsgAsyncWriteProtocol::SendData(nsIURI * aURL, const char * dataBuffer // data to write (i.e. the pipe went empty). So resume the channel to kick // things off again. mSuspendedWrite = PR_FALSE; - mAsyncOutStream->AsyncWait(mProvider, 0, mProviderEventQ); + mAsyncOutStream->AsyncWait(mProvider, 0, 0, mProviderEventQ); } return NS_OK; } diff --git a/mozilla/mailnews/base/util/nsMsgProtocol.h b/mozilla/mailnews/base/util/nsMsgProtocol.h index 06b6ca4c06a..cce3fa05aed 100644 --- a/mozilla/mailnews/base/util/nsMsgProtocol.h +++ b/mozilla/mailnews/base/util/nsMsgProtocol.h @@ -202,9 +202,9 @@ public: // if we suspended the asynch write while waiting for more data to write then this will be TRUE PRBool mSuspendedWrite; nsCOMPtr m_WriteRequest; - nsCOMPtr mAsyncOutStream; - nsCOMPtr mProvider; - nsCOMPtr mProviderEventQ; + nsCOMPtr mAsyncOutStream; + nsCOMPtr mProvider; + nsCOMPtr mProviderEventQ; // because we are reading the post data in asychronously, it's possible that we aren't sending it // out fast enough and the reading gets blocked. The following set of state variables are used to diff --git a/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp index 5cbbb8c1f37..107f5bc2d23 100644 --- a/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp +++ b/mozilla/modules/plugin/samples/SanePlugin/nsSanePlugin.cpp @@ -2534,12 +2534,11 @@ void PR_CALLBACK scanimage_thread_routine( void * arg ) (PLHandleEventProc) ThreadedHandleEvent, (PLDestroyEventProc) ThreadedDestroyEvent); - if (eventQ->PostEvent(event) != PR_SUCCESS) { + if (NS_FAILED(eventQ->PostEvent(event))) { NS_ERROR("Error trying to post event!\n"); + PL_DestroyEvent(event); return; } - - return; } void PR_CALLBACK ThreadedHandleEvent(PLEvent * event) diff --git a/mozilla/netwerk/base/public/nsIAsyncStreamCopier.idl b/mozilla/netwerk/base/public/nsIAsyncStreamCopier.idl index c7114f8f183..5890087a50c 100644 --- a/mozilla/netwerk/base/public/nsIAsyncStreamCopier.idl +++ b/mozilla/netwerk/base/public/nsIAsyncStreamCopier.idl @@ -40,17 +40,22 @@ interface nsIInputStream; interface nsIOutputStream; interface nsIRequestObserver; +interface nsIEventTarget; [scriptable, uuid(eaa49141-c21c-4fe8-a79b-77860a3910aa)] interface nsIAsyncStreamCopier : nsIRequest { /** - * Initialize the input stream copier. + * Initialize the stream copier. * * @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 aSourceBuffered * true if aSource implements ReadSegments. * @param aSinkBuffered @@ -59,20 +64,23 @@ interface nsIAsyncStreamCopier : nsIRequest * 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. + * + * NOTE: at least one of the streams must be buffered. */ void init(in nsIInputStream aSource, in nsIOutputStream aSink, + in nsIEventTarget aTarget, in boolean aSourceBuffered, in boolean aSinkBuffered, in unsigned long aChunkSize); /** - * asyncRead causes the input stream to be read in chunks and delivered - * asynchronously to the listener via OnDataAvailable. + * asyncCopy triggers the start of the copy. The observer will be notified + * when the copy completes. * - * @param aListener + * @param aObserver * receives notifications. - * @param aListenerContext + * @param aObserverContext * passed to listener methods. */ void asyncCopy(in nsIRequestObserver aObserver, diff --git a/mozilla/netwerk/base/public/nsISocketTransportService.idl b/mozilla/netwerk/base/public/nsISocketTransportService.idl index b73e52baddb..2e045d26793 100644 --- a/mozilla/netwerk/base/public/nsISocketTransportService.idl +++ b/mozilla/netwerk/base/public/nsISocketTransportService.idl @@ -38,7 +38,6 @@ #include "nsISupports.idl" interface nsISocketTransport; -interface nsISocketEventHandler; interface nsIProxyInfo; [scriptable, uuid(05331390-6884-11d3-9382-00104ba0fd40)] @@ -77,40 +76,9 @@ interface nsISocketTransportService : nsISupports void init(); void shutdown(); - /** - * Post an event to be executed on the socket thread. - * - * @param aHandler - * handler that will be executed on the socket thread. - * @param aType - * caller defined message parameter - * @param aUParam - * caller defined message parameter - * @param aVParam - * caller defined message parameter - * - * The socket transport service treats each parameter as opaque data (i.e., - * it is not responsible for cleaning up aVParam if it happens to be - * dynamically allocated). If this function succeeds, then the message - * will be delivered. All messages successfully posted will be delivered - * before the socket transport service shuts down. - */ - [noscript] void postEvent(in nsISocketEventHandler aHandler, - in unsigned long aType, - in unsigned long aUParam, - in voidPtr aVParam); - /** * controls whether or not the socket transport service should poke * the autodialer on connection failure. */ attribute boolean autodialEnabled; }; - -[uuid(c20f98be-b3e4-4f9b-a492-97a688577355)] -interface nsISocketEventHandler : nsISupports -{ - [noscript] void onSocketEvent(in unsigned long aType, - in unsigned long aUParam, - in voidPtr aVParam); -}; diff --git a/mozilla/netwerk/base/public/nsIStreamTransportService.idl b/mozilla/netwerk/base/public/nsIStreamTransportService.idl index 435c3ad3ff7..d92526cf0fe 100644 --- a/mozilla/netwerk/base/public/nsIStreamTransportService.idl +++ b/mozilla/netwerk/base/public/nsIStreamTransportService.idl @@ -98,12 +98,4 @@ interface nsIStreamTransportService : nsISupports in long aStartOffset, in long aWriteLimit, in boolean aCloseWhenDone); - - /** - * init/shutdown routines. - * - * XXX these should move to some necko internal interface instead. - */ - void init(); - void shutdown(); }; diff --git a/mozilla/netwerk/base/public/nsITransport.idl b/mozilla/netwerk/base/public/nsITransport.idl index 3f67ef988ea..c14c9a176a2 100644 --- a/mozilla/netwerk/base/public/nsITransport.idl +++ b/mozilla/netwerk/base/public/nsITransport.idl @@ -40,7 +40,7 @@ interface nsIInputStream; interface nsIOutputStream; interface nsITransportEventSink; -interface nsIEventQueue; +interface nsIEventTarget; [scriptable, uuid(cbb0baeb-5fcb-408b-a2be-9f8fc98d0af1)] interface nsITransport : nsISupports @@ -96,13 +96,13 @@ interface nsITransport : nsISupports * * @param aSink * receives transport layer notifications - * @param aEventQ - * indicates the event queue to which the notifications should + * @param aEventTarget + * indicates the event target to which the notifications should * be delivered. if NULL, then the notifications may occur on - * any thread. (NOTE: the event queue must be resolved.) + * any thread. */ void setEventSink(in nsITransportEventSink aSink, - in nsIEventQueue aEventQ); + in nsIEventTarget aEventTarget); /** * Generic nsITransportEventSink status codes. nsITransport diff --git a/mozilla/netwerk/base/public/nsNetUtil.h b/mozilla/netwerk/base/public/nsNetUtil.h index 89f5d6b0335..370994bb457 100644 --- a/mozilla/netwerk/base/public/nsNetUtil.h +++ b/mozilla/netwerk/base/public/nsNetUtil.h @@ -327,16 +327,16 @@ NS_NewInputStreamPump(nsIInputStreamPump **result, return rv; } -// NOTE: you can optimize the copy by specifying whether or not your streams -// are buffered (i.e., do they implement ReadSegments/WriteSegments). the -// default assumption of FALSE for both streams is OK, but the copy is much -// more efficient if one of the streams is buffered. +// NOTE: you will need to specify whether or not your streams are buffered +// (i.e., do they implement ReadSegments/WriteSegments). the default +// assumption of TRUE for both streams might not be right for you! inline nsresult NS_NewAsyncStreamCopier(nsIAsyncStreamCopier **result, nsIInputStream *source, nsIOutputStream *sink, - PRBool sourceBuffered = PR_FALSE, - PRBool sinkBuffered = PR_FALSE, + nsIEventTarget *target, + PRBool sourceBuffered = PR_TRUE, + PRBool sinkBuffered = PR_TRUE, PRUint32 chunkSize = 0) { nsresult rv; @@ -344,7 +344,7 @@ NS_NewAsyncStreamCopier(nsIAsyncStreamCopier **result, nsCOMPtr copier = do_CreateInstance(kAsyncStreamCopierCID, &rv); if (NS_SUCCEEDED(rv)) { - rv = copier->Init(source, sink, sourceBuffered, sinkBuffered, chunkSize); + rv = copier->Init(source, sink, target, sourceBuffered, sinkBuffered, chunkSize); if (NS_SUCCEEDED(rv)) NS_ADDREF(*result = copier); } diff --git a/mozilla/netwerk/base/src/Makefile.in b/mozilla/netwerk/base/src/Makefile.in index 988477636a5..4fec8ba783f 100644 --- a/mozilla/netwerk/base/src/Makefile.in +++ b/mozilla/netwerk/base/src/Makefile.in @@ -37,6 +37,8 @@ REQUIRES = xpcom \ $(NULL) CPPSRCS = \ + nsIOThreadPool.cpp \ + nsTransportUtils.cpp \ nsAsyncStreamCopier.cpp \ nsAsyncStreamListener.cpp \ nsBufferedStreams.cpp \ diff --git a/mozilla/netwerk/base/src/nsAsyncStreamCopier.cpp b/mozilla/netwerk/base/src/nsAsyncStreamCopier.cpp index 5d2c889aabf..7e060938167 100644 --- a/mozilla/netwerk/base/src/nsAsyncStreamCopier.cpp +++ b/mozilla/netwerk/base/src/nsAsyncStreamCopier.cpp @@ -53,9 +53,8 @@ static PRLogModuleInfo *gStreamCopierLog = nsnull; //----------------------------------------------------------------------------- nsAsyncStreamCopier::nsAsyncStreamCopier() - : mInput(this) - , mOutput(this) - , mLock(PR_NewLock()) + : mLock(nsnull) + , mMode(NS_ASYNCCOPY_VIA_READSEGMENTS) , mChunkSize(NET_DEFAULT_SEGMENT_SIZE) , mStatus(NS_OK) , mIsPending(PR_FALSE) @@ -70,7 +69,8 @@ nsAsyncStreamCopier::nsAsyncStreamCopier() nsAsyncStreamCopier::~nsAsyncStreamCopier() { LOG(("Destroying nsAsyncStreamCopier @%x\n", this)); - PR_DestroyLock(mLock); + if (mLock) + PR_DestroyLock(mLock); } PRBool @@ -83,9 +83,9 @@ nsAsyncStreamCopier::IsComplete(nsresult *status) } void -nsAsyncStreamCopier::Complete(nsresult reason) +nsAsyncStreamCopier::Complete(nsresult status) { - LOG(("nsAsyncStreamCopier::Complete [this=%x reason=%x]\n", this, reason)); + LOG(("nsAsyncStreamCopier::Complete [this=%x status=%x]\n", this, status)); nsCOMPtr observer; nsCOMPtr ctx; @@ -93,7 +93,7 @@ nsAsyncStreamCopier::Complete(nsresult reason) nsAutoLock lock(mLock); if (mIsPending) { mIsPending = PR_FALSE; - mStatus = reason; + mStatus = status; // setup OnStopRequest callback and release references... observer = mObserver; @@ -104,13 +104,19 @@ nsAsyncStreamCopier::Complete(nsresult reason) } if (observer) { - if (reason == NS_BASE_STREAM_CLOSED) - reason = NS_OK; - LOG((" calling OnStopRequest [status=%x]\n", reason)); - observer->OnStopRequest(this, ctx, reason); + LOG((" calling OnStopRequest [status=%x]\n", status)); + observer->OnStopRequest(this, ctx, status); } } +void +nsAsyncStreamCopier::OnAsyncCopyComplete(void *closure, nsresult status) +{ + nsAsyncStreamCopier *self = (nsAsyncStreamCopier *) closure; + self->Complete(status); + NS_RELEASE(self); // addref'd in AsyncCopy +} + //----------------------------------------------------------------------------- // nsISupports @@ -124,7 +130,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(nsAsyncStreamCopier, NS_IMETHODIMP nsAsyncStreamCopier::GetName(nsACString &name) { - name = NS_LITERAL_CSTRING("nsAsyncStreamCopier"); + name.Truncate(); return NS_OK; } @@ -139,11 +145,6 @@ NS_IMETHODIMP nsAsyncStreamCopier::GetStatus(nsresult *status) { IsComplete(status); - - // mask successful "error" code. - if (*status == NS_BASE_STREAM_CLOSED) - *status = NS_OK; - return NS_OK; } @@ -158,22 +159,32 @@ nsAsyncStreamCopier::Cancel(nsresult status) status = NS_BASE_STREAM_CLOSED; } - mInput.CloseEx(status); - mOutput.CloseEx(status); + nsCOMPtr asyncSource = do_QueryInterface(mSource); + if (asyncSource) + asyncSource->CloseWithStatus(status); + else + mSource->Close(); + + nsCOMPtr asyncSink = do_QueryInterface(mSink); + if (asyncSink) + asyncSink->CloseWithStatus(status); + else + mSink->Close(); + return NS_OK; } NS_IMETHODIMP nsAsyncStreamCopier::Suspend() { - // this could be fairly easily implemented by making Read/ReadSegments - // return NS_BASE_STREAM_WOULD_BLOCK. + NS_NOTREACHED("nsAsyncStreamCopier::Suspend"); return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsAsyncStreamCopier::Resume() { + NS_NOTREACHED("nsAsyncStreamCopier::Resume"); return NS_ERROR_NOT_IMPLEMENTED; } @@ -187,7 +198,7 @@ nsAsyncStreamCopier::GetLoadFlags(nsLoadFlags *aLoadFlags) NS_IMETHODIMP nsAsyncStreamCopier::SetLoadFlags(nsLoadFlags aLoadFlags) { - return NS_ERROR_NOT_IMPLEMENTED; + return NS_OK; } NS_IMETHODIMP @@ -200,7 +211,7 @@ nsAsyncStreamCopier::GetLoadGroup(nsILoadGroup **aLoadGroup) NS_IMETHODIMP nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup *aLoadGroup) { - return NS_ERROR_NOT_IMPLEMENTED; + return NS_OK; } //----------------------------------------------------------------------------- @@ -209,21 +220,34 @@ nsAsyncStreamCopier::SetLoadGroup(nsILoadGroup *aLoadGroup) NS_IMETHODIMP nsAsyncStreamCopier::Init(nsIInputStream *source, nsIOutputStream *sink, + nsIEventTarget *target, PRBool sourceBuffered, PRBool sinkBuffered, PRUint32 chunkSize) { + NS_ASSERTION(sourceBuffered || sinkBuffered, "at least one stream must be buffered"); + + NS_ASSERTION(!mLock, "already initialized"); + mLock = PR_NewLock(); + if (!mLock) + return NS_ERROR_OUT_OF_MEMORY; + if (chunkSize == 0) chunkSize = NET_DEFAULT_SEGMENT_SIZE; mChunkSize = chunkSize; - mInput.mSource = source; - mInput.mAsyncSource = do_QueryInterface(source); - mInput.mBuffered = sourceBuffered; + mSource = source; + mSink = sink; - mOutput.mSink = sink; - mOutput.mAsyncSink = do_QueryInterface(sink); - mOutput.mBuffered = sinkBuffered; + mMode = sourceBuffered ? NS_ASYNCCOPY_VIA_READSEGMENTS + : NS_ASYNCCOPY_VIA_WRITESEGMENTS; + if (target) + mTarget = target; + else { + nsresult rv; + mTarget = do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + } return NS_OK; } @@ -232,319 +256,35 @@ nsAsyncStreamCopier::AsyncCopy(nsIRequestObserver *observer, nsISupports *ctx) { LOG(("nsAsyncStreamCopier::AsyncCopy [this=%x observer=%x]\n", this, observer)); - NS_ENSURE_ARG_POINTER(observer); - NS_ENSURE_TRUE(mInput.mSource, NS_ERROR_NOT_INITIALIZED); - NS_ENSURE_TRUE(mOutput.mSink, NS_ERROR_NOT_INITIALIZED); - + NS_ASSERTION(mSource && mSink, "not initialized"); nsresult rv; - // we could perhaps work around this requirement by automatically using - // the stream transport service, but then we are left having to make a - // rather arbitrary selection between opening an asynchronous input - // stream or an asynchronous output stream. that choice is probably - // best left up to the consumer of this async stream copier. - if (!mInput.mAsyncSource && !mOutput.mAsyncSink) { - NS_ERROR("at least one stream must be asynchronous"); - return NS_ERROR_UNEXPECTED; + if (observer) { + // build proxy for observer events + rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), observer); + if (NS_FAILED(rv)) return rv; } - // build proxy for observer events - rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), observer); - if (NS_FAILED(rv)) return rv; - // from this point forward, AsyncCopy is going to return NS_OK. any errors // will be reported via OnStopRequest. mIsPending = PR_TRUE; mObserverContext = ctx; - rv = mObserver->OnStartRequest(this, mObserverContext); - if (NS_FAILED(rv)) - Cancel(rv); - - rv = NS_AsyncCopy(&mInput, &mOutput, mInput.mBuffered, mOutput.mBuffered, mChunkSize); - if (NS_FAILED(rv)) - Cancel(rv); - - return NS_OK; -} - -//----------------------------------------------------------------------------- -// nsInputWrapper -// -// NOTE: the input stream methods may be accessed on any thread; however, we -// assume that Read/ReadSegments will not be called simultaneously on -// more than one thread. - -NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier:: -nsInputWrapper::AddRef() -{ - return mCopier->AddRef(); -} - -NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier:: -nsInputWrapper::Release() -{ - return mCopier->Release(); -} - -NS_IMPL_QUERY_INTERFACE3(nsAsyncStreamCopier::nsInputWrapper, - nsIAsyncInputStream, - nsIInputStream, - nsIInputStreamNotify) - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::Close() -{ - return CloseEx(NS_BASE_STREAM_CLOSED); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::Available(PRUint32 *avail) -{ - nsresult status; - if (mCopier->IsComplete(&status)) - return status; - - NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED); - - nsresult rv = mSource->Available(avail); - if (NS_FAILED(rv)) - CloseEx(rv); - return rv; -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::Read(char *buf, PRUint32 count, PRUint32 *countRead) -{ - nsresult status; - if (mCopier->IsComplete(&status)) { - *countRead = 0; - return status == NS_BASE_STREAM_CLOSED ? NS_OK : status; - } - - NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED); - - return mSource->Read(buf, count, countRead); -} - -NS_METHOD nsAsyncStreamCopier:: -nsInputWrapper::ReadSegmentsThunk(nsIInputStream *stream, - void *closure, - const char *segment, - PRUint32 offset, - PRUint32 count, - PRUint32 *countRead) -{ - nsInputWrapper *self = (nsInputWrapper *) closure; - return self->mWriter(self, self->mClosure, segment, offset, count, countRead); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::ReadSegments(nsWriteSegmentFun writer, void *closure, - PRUint32 count, PRUint32 *countRead) -{ - nsresult status; - if (mCopier->IsComplete(&status)) { - *countRead = 0; - return status == NS_BASE_STREAM_CLOSED ? NS_OK : status; - } - - NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED); - - if (!mBuffered) - return NS_ERROR_NOT_IMPLEMENTED; - - mWriter = writer; - mClosure = closure; - - return mSource->ReadSegments(ReadSegmentsThunk, this, count, countRead); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::IsNonBlocking(PRBool *result) -{ - nsresult status; - if (mCopier->IsComplete(&status)) - return status; - - NS_ENSURE_TRUE(mSource, NS_ERROR_NOT_INITIALIZED); - return mSource->IsNonBlocking(result); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::CloseEx(nsresult reason) -{ - LOG(("nsAsyncStreamCopier::nsInputWrapper::CloseEx [this=%x]\n", this)); - - mCopier->Complete(reason); - - if (mAsyncSource) - mAsyncSource->CloseEx(reason); - else - mSource->Close(); - return NS_OK; -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::AsyncWait(nsIInputStreamNotify *notify, PRUint32 amount, nsIEventQueue *eventQ) -{ - // we'll cheat a little bit here since we know that NS_AsyncCopy does not - // pass a non-null event queue. - NS_ASSERTION(eventQ == nsnull, "unexpected"); - - if (mAsyncSource) { - mNotify = notify; - return mAsyncSource->AsyncWait(this, amount, eventQ); - } - - // else, stream is ready - notify->OnInputStreamReady(this); - return NS_OK; -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsInputWrapper::OnInputStreamReady(nsIAsyncInputStream *stream) -{ - // simple thunk - return mNotify->OnInputStreamReady(this); -} - -//----------------------------------------------------------------------------- -// nsOutputWrapper -// -// NOTE: the output stream methods may be accessed on any thread; however, we -// assume that Write/WriteSegments will not be called simultaneously on -// more than one thread. - -NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier:: -nsOutputWrapper::AddRef() -{ - return mCopier->AddRef(); -} - -NS_IMETHODIMP_(nsrefcnt) nsAsyncStreamCopier:: -nsOutputWrapper::Release() -{ - return mCopier->Release(); -} - -NS_IMPL_QUERY_INTERFACE3(nsAsyncStreamCopier::nsOutputWrapper, - nsIAsyncOutputStream, - nsIOutputStream, - nsIOutputStreamNotify) - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::Close() -{ - return CloseEx(NS_BASE_STREAM_CLOSED); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::Flush() -{ - NS_NOTREACHED("nsOutputWrapper::Flush"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::Write(const char *buf, PRUint32 count, PRUint32 *countWritten) -{ - nsresult status; - if (mCopier->IsComplete(&status)) { - *countWritten = 0; - return status; - } - - NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED); - - return mSink->Write(buf, count, countWritten); -} - -NS_METHOD nsAsyncStreamCopier:: -nsOutputWrapper::WriteSegmentsThunk(nsIOutputStream *stream, - void *closure, - char *segment, - PRUint32 offset, - PRUint32 count, - PRUint32 *countWritten) -{ - nsOutputWrapper *self = (nsOutputWrapper *) closure; - return self->mReader(self, self->mClosure, segment, offset, count, countWritten); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::WriteSegments(nsReadSegmentFun reader, void *closure, - PRUint32 count, PRUint32 *countWritten) -{ - nsresult status; - if (mCopier->IsComplete(&status)) { - *countWritten = 0; - return status; - } - - NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED); - - if (!mBuffered) - return NS_ERROR_NOT_IMPLEMENTED; - - mReader = reader; - mClosure = closure; - - return mSink->WriteSegments(WriteSegmentsThunk, this, count, countWritten); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::WriteFrom(nsIInputStream *stream, PRUint32 count, PRUint32 *countWritten) -{ - NS_NOTREACHED("nsOutputWrapper::WriteFrom"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::IsNonBlocking(PRBool *result) -{ - nsresult status; - if (mCopier->IsComplete(&status)) - return status; - - NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED); - return mSink->IsNonBlocking(result); -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::CloseEx(nsresult reason) -{ - LOG(("nsAsyncStreamCopier::nsOutputWrapper::CloseEx [this=%x]\n", this)); - - mCopier->Complete(reason); - - if (mAsyncSink) - mAsyncSink->CloseEx(reason); - else - mSink->Close(); - return NS_OK; -} - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::AsyncWait(nsIOutputStreamNotify *notify, PRUint32 amount, nsIEventQueue *eventQ) -{ - // we'll cheat a little bit here since we know that NS_AsyncCopy does not - // pass a non-null event queue. - NS_ASSERTION(eventQ == nsnull, "unexpected"); - - if (mAsyncSink) { - mNotify = notify; - return mAsyncSink->AsyncWait(this, amount, eventQ); + if (mObserver) { + rv = mObserver->OnStartRequest(this, mObserverContext); + if (NS_FAILED(rv)) + Cancel(rv); } - // else, stream is ready - notify->OnOutputStreamReady(this); + // we want to receive progress notifications; release happens in + // OnAsyncCopyComplete. + NS_ADDREF_THIS(); + rv = NS_AsyncCopy(mSource, mSink, mTarget, mMode, mChunkSize, + OnAsyncCopyComplete, this); + if (NS_FAILED(rv)) { + NS_RELEASE_THIS(); + Cancel(rv); + } + return NS_OK; } - -NS_IMETHODIMP nsAsyncStreamCopier:: -nsOutputWrapper::OnOutputStreamReady(nsIAsyncOutputStream *stream) -{ - // simple thunk - return mNotify->OnOutputStreamReady(this); -} diff --git a/mozilla/netwerk/base/src/nsAsyncStreamCopier.h b/mozilla/netwerk/base/src/nsAsyncStreamCopier.h index 4c44eebc551..a103c349388 100644 --- a/mozilla/netwerk/base/src/nsAsyncStreamCopier.h +++ b/mozilla/netwerk/base/src/nsAsyncStreamCopier.h @@ -42,6 +42,7 @@ #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsIRequestObserver.h" +#include "nsStreamUtils.h" #include "nsCOMPtr.h" #include "prlock.h" @@ -63,88 +64,24 @@ public: PRBool IsComplete(nsresult *status = nsnull); void Complete(nsresult status); - //------------------------------------------------------------------------- - // nsInputWrapper - //------------------------------------------------------------------------- - - class nsInputWrapper : public nsIAsyncInputStream - , public nsIInputStreamNotify - { - public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIINPUTSTREAM - NS_DECL_NSIASYNCINPUTSTREAM - NS_DECL_NSIINPUTSTREAMNOTIFY - - nsInputWrapper(nsAsyncStreamCopier *copier) - : mCopier(copier) - , mBuffered(PR_FALSE) - { } - virtual ~nsInputWrapper() {} - - nsAsyncStreamCopier *mCopier; - nsCOMPtr mSource; - nsCOMPtr mAsyncSource; - PRBool mBuffered; - - private: - // ReadSegments thunk impl - nsWriteSegmentFun mWriter; - void *mClosure; - static NS_METHOD ReadSegmentsThunk(nsIInputStream *, void *, const char *, - PRUint32, PRUint32, PRUint32 *); - - // AsyncWait thunk impl - nsCOMPtr mNotify; - }; - - //------------------------------------------------------------------------- - // nsInputWrapper - //------------------------------------------------------------------------- - - class nsOutputWrapper : public nsIAsyncOutputStream - , public nsIOutputStreamNotify - { - public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIOUTPUTSTREAM - NS_DECL_NSIASYNCOUTPUTSTREAM - NS_DECL_NSIOUTPUTSTREAMNOTIFY - - nsOutputWrapper(nsAsyncStreamCopier *copier) - : mCopier(copier) - , mBuffered(PR_FALSE) - { } - virtual ~nsOutputWrapper() {} - - nsAsyncStreamCopier *mCopier; - nsCOMPtr mSink; - nsCOMPtr mAsyncSink; - PRBool mBuffered; - - private: - // WriteSegments thunk impl - nsReadSegmentFun mReader; - void *mClosure; - static NS_METHOD WriteSegmentsThunk(nsIOutputStream *, void *, char *, - PRUint32, PRUint32, PRUint32 *); - - // AsyncWait thunk impl - nsCOMPtr mNotify; - }; - private: - nsInputWrapper mInput; - nsOutputWrapper mOutput; + + static void OnAsyncCopyComplete(void *, nsresult); + + nsCOMPtr mSource; + nsCOMPtr mSink; nsCOMPtr mObserver; nsCOMPtr mObserverContext; + nsCOMPtr mTarget; + PRLock *mLock; + nsAsyncCopyMode mMode; PRUint32 mChunkSize; nsresult mStatus; - PRBool mIsPending; + PRPackedBool mIsPending; }; #endif // !nsAsyncStreamCopier_h__ diff --git a/mozilla/netwerk/base/src/nsIOService.cpp b/mozilla/netwerk/base/src/nsIOService.cpp index 18b9d6601fc..3305392f03c 100644 --- a/mozilla/netwerk/base/src/nsIOService.cpp +++ b/mozilla/netwerk/base/src/nsIOService.cpp @@ -192,10 +192,6 @@ nsIOService::Init() if (NS_FAILED(rv)) NS_WARNING("failed to get socket transport service"); - mStreamTransportService = do_GetService(kStreamTransportServiceCID, &rv); - if (NS_FAILED(rv)) - NS_WARNING("failed to get stream transport service"); - mDNSService = do_GetService(kDNSServiceCID, &rv); if (NS_FAILED(rv)) NS_WARNING("failed to get DNS service"); @@ -673,9 +669,6 @@ nsIOService::Observe(nsISupports *subject, else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { SetOffline(PR_TRUE); - if (mStreamTransportService) - mStreamTransportService->Shutdown(); - // Break circular reference. mProxyService = 0; } diff --git a/mozilla/netwerk/base/src/nsIOService.h b/mozilla/netwerk/base/src/nsIOService.h index ddcd0333789..e355c05fbde 100644 --- a/mozilla/netwerk/base/src/nsIOService.h +++ b/mozilla/netwerk/base/src/nsIOService.h @@ -44,7 +44,6 @@ #include "nsIIOService.h" #include "nsVoidArray.h" #include "nsISocketTransportService.h" -#include "nsIStreamTransportService.h" #include "nsIDNSService.h" #include "nsIProtocolProxyService.h" #include "nsCOMPtr.h" @@ -110,7 +109,6 @@ protected: PRPackedBool mOffline; PRPackedBool mOfflineForProfileChange; nsCOMPtr mSocketTransportService; - nsCOMPtr mStreamTransportService; nsCOMPtr mDNSService; nsCOMPtr mProxyService; nsCOMPtr mEventQueueService; diff --git a/mozilla/netwerk/base/src/nsIOThreadPool.cpp b/mozilla/netwerk/base/src/nsIOThreadPool.cpp new file mode 100644 index 00000000000..2b6d203079c --- /dev/null +++ b/mozilla/netwerk/base/src/nsIOThreadPool.cpp @@ -0,0 +1,290 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIEventTarget.h" +#include "nsIServiceManager.h" +#include "nsIObserverService.h" +#include "nsIObserver.h" +#include "nsAutoLock.h" +#include "nsCOMPtr.h" +#include "prclist.h" +#include "prlog.h" + +#if defined(PR_LOGGING) +// +// NSPR_LOG_MODULES=nsIOThreadPool:5 +// +static PRLogModuleInfo *gIOThreadPoolLog = nsnull; +#endif +#define LOG(args) PR_LOG(gIOThreadPoolLog, PR_LOG_DEBUG, args) + +#define MAX_THREADS 4 +#define THREAD_IDLE_TIMEOUT PR_SecondsToInterval(10) + +#define PLEVENT_FROM_LINK(_link) \ + ((PLEvent*) ((char*) (_link) - offsetof(PLEvent, link))) + +//----------------------------------------------------------------------------- +// pool of joinable threads used for general purpose i/o tasks + +class nsIOThreadPool : public nsIEventTarget + , public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIEVENTTARGET + NS_DECL_NSIOBSERVER + + nsresult Init(); + void Shutdown(); + +private: + virtual ~nsIOThreadPool(); + + int GetCurrentThreadIndex(); + + PR_STATIC_CALLBACK(void) ThreadFunc(void *); + + // mLock protects all (exceptions during Init and Shutdown) + PRLock *mLock; + PRCondVar *mCV; + PRThread *mThreads[MAX_THREADS]; + PRUint32 mNumIdleThreads; + PRCList mEventQ; + PRBool mShutdown; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsIOThreadPool, nsIEventTarget, nsIObserver) + +nsresult +nsIOThreadPool::Init() +{ +#if defined(PR_LOGGING) + if (!gIOThreadPoolLog) + gIOThreadPoolLog = PR_NewLogModule("nsIOThreadPool"); +#endif + + mNumIdleThreads = 0; + mShutdown = PR_FALSE; + + mLock = PR_NewLock(); + if (!mLock) + return NS_ERROR_OUT_OF_MEMORY; + + mCV = PR_NewCondVar(mLock); + if (!mCV) + return NS_ERROR_OUT_OF_MEMORY; + + memset(mThreads, 0, sizeof(mThreads)); + PR_INIT_CLIST(&mEventQ); + + // we want to shutdown the i/o thread pool at xpcom-shutdown time... + nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1"); + if (os) + os->AddObserver(this, "xpcom-shutdown", PR_FALSE); + return NS_OK; +} + +nsIOThreadPool::~nsIOThreadPool() +{ + LOG(("Destroying nsIOThreadPool @%p\n", this)); + +#ifdef DEBUG + NS_ASSERTION(PR_CLIST_IS_EMPTY(&mEventQ), "leaking events"); + for (int i=0; ilink, &mEventQ); + + // now, look for an available thread... + if (mNumIdleThreads) + PR_NotifyCondVar(mCV); // wake up an idle thread + else { + // try to create a new thread unless we have reached our maximum... + for (int i=0; imLock); + + for (;;) { + // never wait if we are shutting down; always process queued events... + if (PR_CLIST_IS_EMPTY(&pool->mEventQ) && !pool->mShutdown) { + pool->mNumIdleThreads++; + PR_WaitCondVar(pool->mCV, THREAD_IDLE_TIMEOUT); + pool->mNumIdleThreads--; + } + + // if the queue is still empty, then kill this thread... + if (PR_CLIST_IS_EMPTY(&pool->mEventQ)) + break; + + // handle one event at a time: we don't want this one thread to hog + // all the events while other threads may be able to help out ;-) + do { + PLEvent *event = PLEVENT_FROM_LINK(PR_LIST_HEAD(&pool->mEventQ)); + PR_REMOVE_AND_INIT_LINK(&event->link); + + LOG(("event:%p\n", event)); + + // release lock! + PR_Unlock(pool->mLock); + PL_HandleEvent(event); + PR_Lock(pool->mLock); + } + while (!PR_CLIST_IS_EMPTY(&pool->mEventQ)); + } + + // thread is dying... cleanup mThreads, unless of course if we are + // shutting down, in which case Shutdown will clean up mThreads for us. + if (!pool->mShutdown) + pool->mThreads[pool->GetCurrentThreadIndex()] = nsnull; + + PR_Unlock(pool->mLock); + + // release our reference to the pool + NS_RELEASE(pool); + + LOG(("leaving ThreadFunc\n")); +} + +//----------------------------------------------------------------------------- + +nsresult +net_NewIOThreadPool(nsISupports *outer, REFNSIID iid, void **result) +{ + nsIOThreadPool *pool = new nsIOThreadPool(); + if (!pool) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(pool); + nsresult rv = pool->Init(); + if (NS_SUCCEEDED(rv)) + rv = pool->QueryInterface(iid, result); + NS_RELEASE(pool); + return rv; +} diff --git a/mozilla/netwerk/base/src/nsIOThreadPool.h b/mozilla/netwerk/base/src/nsIOThreadPool.h new file mode 100644 index 00000000000..971354a40bf --- /dev/null +++ b/mozilla/netwerk/base/src/nsIOThreadPool.h @@ -0,0 +1,45 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsIOThreadPool_h__ +#define nsIOThreadPool_h__ + +/** + * XPCOM constructor for nsIOThreadPool + */ +nsresult net_NewIOThreadPool(nsISupports *outer, REFNSIID iid, void **result); + +#endif // nsIOThreadPool_h__ diff --git a/mozilla/netwerk/base/src/nsInputStreamPump.cpp b/mozilla/netwerk/base/src/nsInputStreamPump.cpp index 8ceb9993f41..3c35d3004d7 100644 --- a/mozilla/netwerk/base/src/nsInputStreamPump.cpp +++ b/mozilla/netwerk/base/src/nsInputStreamPump.cpp @@ -85,7 +85,7 @@ nsresult nsInputStreamPump::EnsureWaiting() { if (!mWaiting) { - nsresult rv = mAsyncStream->AsyncWait(this, 0, mEventQ); + nsresult rv = mAsyncStream->AsyncWait(this, 0, 0, mEventQ); if (NS_FAILED(rv)) { NS_ERROR("AsyncWait failed"); return rv; @@ -104,7 +104,7 @@ nsInputStreamPump::EnsureWaiting() // understands the limitations of this. NS_IMPL_THREADSAFE_ISUPPORTS3(nsInputStreamPump, nsIRequest, - nsIInputStreamNotify, + nsIInputStreamCallback, nsIInputStreamPump) //----------------------------------------------------------------------------- @@ -148,7 +148,7 @@ nsInputStreamPump::Cancel(nsresult status) // close input stream if (mAsyncStream) { - mAsyncStream->CloseEx(status); + mAsyncStream->CloseWithStatus(status); mSuspendCount = 0; // un-suspend EnsureWaiting(); } @@ -295,7 +295,7 @@ nsInputStreamPump::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) } //----------------------------------------------------------------------------- -// nsInputStreamPump::nsIInputStreamNotify implementation +// nsInputStreamPump::nsIInputStreamCallback implementation //----------------------------------------------------------------------------- NS_IMETHODIMP @@ -472,7 +472,7 @@ nsInputStreamPump::OnStateStop() // this is OK. otherwise, be sure to honor the "close-when-done" option. if (NS_FAILED(mStatus)) - mAsyncStream->CloseEx(mStatus); + mAsyncStream->CloseWithStatus(mStatus); else if (mCloseWhenDone) mAsyncStream->Close(); diff --git a/mozilla/netwerk/base/src/nsInputStreamPump.h b/mozilla/netwerk/base/src/nsInputStreamPump.h index 5a95cc5918b..7acaae69b74 100644 --- a/mozilla/netwerk/base/src/nsInputStreamPump.h +++ b/mozilla/netwerk/base/src/nsInputStreamPump.h @@ -50,13 +50,13 @@ #include "nsCOMPtr.h" class nsInputStreamPump : public nsIInputStreamPump - , public nsIInputStreamNotify + , public nsIInputStreamCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSIREQUEST NS_DECL_NSIINPUTSTREAMPUMP - NS_DECL_NSIINPUTSTREAMNOTIFY + NS_DECL_NSIINPUTSTREAMCALLBACK nsInputStreamPump(); virtual ~nsInputStreamPump(); diff --git a/mozilla/netwerk/base/src/nsSocketTransport2.cpp b/mozilla/netwerk/base/src/nsSocketTransport2.cpp index e36d6efada7..76a1b757384 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport2.cpp +++ b/mozilla/netwerk/base/src/nsSocketTransport2.cpp @@ -43,6 +43,7 @@ #include "nsIOService.h" #include "nsStreamUtils.h" #include "nsNetSegmentUtils.h" +#include "nsTransportUtils.h" #include "nsNetCID.h" #include "nsAutoLock.h" #include "nsCOMPtr.h" @@ -73,6 +74,44 @@ static NS_DEFINE_CID(kDNSServiceCID, NS_DNSSERVICE_CID); //----------------------------------------------------------------------------- +class nsSocketEvent : public PLEvent +{ +public: + nsSocketEvent(nsSocketTransport *transport, PRUint32 type, + nsresult status = NS_OK, nsISupports *param = nsnull) + : mType(type) + , mStatus(status) + , mParam(param) + { + NS_ADDREF(transport); + PL_InitEvent(this, transport, HandleEvent, DestroyEvent); + } + + PR_STATIC_CALLBACK(void*) + HandleEvent(PLEvent *event) + { + nsSocketTransport *trans = (nsSocketTransport *) event->owner; + nsSocketEvent *se = (nsSocketEvent *) event; + trans->OnSocketEvent(se->mType, se->mStatus, se->mParam); + return nsnull; + } + + PR_STATIC_CALLBACK(void) + DestroyEvent(PLEvent *event) + { + nsSocketTransport *trans = (nsSocketTransport *) event->owner; + NS_RELEASE(trans); + delete (nsSocketEvent *) event; + } + +private: + PRUint32 mType; + nsresult mStatus; + nsCOMPtr mParam; +}; + +//----------------------------------------------------------------------------- + //#define TEST_CONNECT_ERRORS #ifdef TEST_CONNECT_ERRORS #include @@ -151,6 +190,7 @@ nsSocketInputStream::nsSocketInputStream(nsSocketTransport *trans) : mTransport(trans) , mReaderRefCnt(0) , mCondition(NS_OK) + , mCallbackFlags(0) , mByteCount(0) { } @@ -169,7 +209,7 @@ nsSocketInputStream::OnSocketReady(nsresult condition) LOG(("nsSocketInputStream::OnSocketReady [this=%x cond=%x]\n", this, condition)); - nsCOMPtr notify; + nsCOMPtr callback; { nsAutoLock lock(mTransport->mLock); @@ -178,13 +218,16 @@ nsSocketInputStream::OnSocketReady(nsresult condition) if (NS_SUCCEEDED(mCondition)) mCondition = condition; - // see if anyone wants to be notified... - notify = mNotify; - mNotify = nsnull; + // ignore event if only waiting for closure and not closed. + if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) { + callback = mCallback; + mCallback = nsnull; + mCallbackFlags = 0; + } } - if (notify) - notify->OnInputStreamReady(this); + if (callback) + callback->OnInputStreamReady(this); } NS_IMPL_QUERY_INTERFACE2(nsSocketInputStream, @@ -209,7 +252,7 @@ nsSocketInputStream::Release() NS_IMETHODIMP nsSocketInputStream::Close() { - return CloseEx(NS_BASE_STREAM_CLOSED); + return CloseWithStatus(NS_BASE_STREAM_CLOSED); } NS_IMETHODIMP @@ -327,9 +370,9 @@ nsSocketInputStream::IsNonBlocking(PRBool *nonblocking) } NS_IMETHODIMP -nsSocketInputStream::CloseEx(nsresult reason) +nsSocketInputStream::CloseWithStatus(nsresult reason) { - LOG(("nsSocketInputStream::CloseEx [this=%x reason=%x]\n", this, reason)); + LOG(("nsSocketInputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason)); // may be called from any thread @@ -348,29 +391,33 @@ nsSocketInputStream::CloseEx(nsresult reason) } NS_IMETHODIMP -nsSocketInputStream::AsyncWait(nsIInputStreamNotify *notify, PRUint32 amount, - nsIEventQueue *eventQ) +nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback, + PRUint32 flags, + PRUint32 amount, + nsIEventTarget *target) { LOG(("nsSocketInputStream::AsyncWait [this=%x]\n", this)); { nsAutoLock lock(mTransport->mLock); - if (eventQ) { + if (target) { // // build event proxy // // failure to create an event proxy (most likely out of memory) // shouldn't alter the state of the transport. // - nsCOMPtr temp; + nsCOMPtr temp; nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(temp), - notify, eventQ); + callback, target); if (NS_FAILED(rv)) return rv; - mNotify = temp; + mCallback = temp; } else - mNotify = notify; + mCallback = callback; + + mCallbackFlags = flags; } mTransport->OnInputPending(); return NS_OK; @@ -384,6 +431,7 @@ nsSocketOutputStream::nsSocketOutputStream(nsSocketTransport *trans) : mTransport(trans) , mWriterRefCnt(0) , mCondition(NS_OK) + , mCallbackFlags(0) , mByteCount(0) { } @@ -402,7 +450,7 @@ nsSocketOutputStream::OnSocketReady(nsresult condition) LOG(("nsSocketOutputStream::OnSocketReady [this=%x cond=%x]\n", this, condition)); - nsCOMPtr notify; + nsCOMPtr callback; { nsAutoLock lock(mTransport->mLock); @@ -411,13 +459,16 @@ nsSocketOutputStream::OnSocketReady(nsresult condition) if (NS_SUCCEEDED(mCondition)) mCondition = condition; - // see if anyone wants to be notified... - notify = mNotify; - mNotify = nsnull; + // ignore event if only waiting for closure and not closed. + if (NS_FAILED(mCondition) || !(mCallbackFlags & WAIT_CLOSURE_ONLY)) { + callback = mCallback; + mCallback = nsnull; + mCallbackFlags = 0; + } } - if (notify) - notify->OnOutputStreamReady(this); + if (callback) + callback->OnOutputStreamReady(this); } NS_IMPL_QUERY_INTERFACE2(nsSocketOutputStream, @@ -442,7 +493,7 @@ nsSocketOutputStream::Release() NS_IMETHODIMP nsSocketOutputStream::Close() { - return CloseEx(NS_BASE_STREAM_CLOSED); + return CloseWithStatus(NS_BASE_STREAM_CLOSED); } NS_IMETHODIMP @@ -543,9 +594,9 @@ nsSocketOutputStream::IsNonBlocking(PRBool *nonblocking) } NS_IMETHODIMP -nsSocketOutputStream::CloseEx(nsresult reason) +nsSocketOutputStream::CloseWithStatus(nsresult reason) { - LOG(("nsSocketOutputStream::CloseEx [this=%x reason=%x]\n", this, reason)); + LOG(("nsSocketOutputStream::CloseWithStatus [this=%x reason=%x]\n", this, reason)); // may be called from any thread @@ -564,29 +615,33 @@ nsSocketOutputStream::CloseEx(nsresult reason) } NS_IMETHODIMP -nsSocketOutputStream::AsyncWait(nsIOutputStreamNotify *notify, PRUint32 amount, - nsIEventQueue *eventQ) +nsSocketOutputStream::AsyncWait(nsIOutputStreamCallback *callback, + PRUint32 flags, + PRUint32 amount, + nsIEventTarget *target) { LOG(("nsSocketOutputStream::AsyncWait [this=%x]\n", this)); { nsAutoLock lock(mTransport->mLock); - if (eventQ) { + if (target) { // // build event proxy // // failure to create an event proxy (most likely out of memory) // shouldn't alter the state of the transport. // - nsCOMPtr temp; + nsCOMPtr temp; nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(temp), - notify, eventQ); + callback, target); if (NS_FAILED(rv)) return rv; - mNotify = temp; + mCallback = temp; } else - mNotify = notify; + mCallback = callback; + + mCallbackFlags = flags; } mTransport->OnOutputPending(); return NS_OK; @@ -701,6 +756,20 @@ nsSocketTransport::Init(const char **types, PRUint32 typeCount, return NS_OK; } +nsresult +nsSocketTransport::PostEvent(PRUint32 type, nsresult status, nsISupports *param) +{ + PLEvent *event = new nsSocketEvent(this, type, status, param); + if (!event) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = gSocketTransportService->PostEvent(event); + if (NS_FAILED(rv)) + PL_DestroyEvent(event); + + return rv; +} + void nsSocketTransport::SendStatus(nsresult status) { @@ -733,16 +802,25 @@ nsSocketTransport::ResolveHost() LOG(("nsSocketTransport::ResolveHost [this=%x]\n", this)); nsresult rv; - - nsCOMPtr dns = do_GetService(kDNSServiceCID, &rv); - if (NS_FAILED(rv)) return rv; - - rv = dns->AsyncResolve(SocketHost(), PR_FALSE, this, nsnull, - getter_AddRefs(mDNSRequest)); - if (NS_SUCCEEDED(rv)) { - LOG((" advancing to STATE_RESOLVING\n")); + // if this is a numeric ip address, then we can simply circumvent the + // DNS resolver. + if (PR_StringToNetAddr(SocketHost().get(), &mNetAddr) == PR_SUCCESS) { + mNetAddr.inet.port = PR_htons(SocketPort()); mState = STATE_RESOLVING; - SendStatus(STATUS_RESOLVING); + // not sending a DNS record... + rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, NS_OK, nsnull); + } + else { + nsCOMPtr dns = do_GetService(kDNSServiceCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = dns->AsyncResolve(SocketHost(), PR_FALSE, this, nsnull, + getter_AddRefs(mDNSRequest)); + if (NS_SUCCEEDED(rv)) { + LOG((" advancing to STATE_RESOLVING\n")); + mState = STATE_RESOLVING; + SendStatus(STATUS_RESOLVING); + } } return rv; } @@ -850,6 +928,8 @@ nsSocketTransport::InitiateSocket() { LOG(("nsSocketTransport::InitiateSocket [this=%x]\n", this)); + nsresult rv; + // // find out if it is going to be ok to attach another socket to the STS. // if not then we have to wait for the STS to tell us that it is ok. @@ -862,8 +942,15 @@ nsSocketTransport::InitiateSocket() // FIFO ordering (which wouldn't even be that valuable IMO). see bug // 194402 for more info. // - if (!gSocketTransportService->CanAttachSocket()) - return gSocketTransportService->NotifyWhenCanAttachSocket(this, MSG_RETRY_INIT_SOCKET); + if (!gSocketTransportService->CanAttachSocket()) { + PLEvent *event = new nsSocketEvent(this, MSG_RETRY_INIT_SOCKET); + if (!event) + return NS_ERROR_OUT_OF_MEMORY; + rv = gSocketTransportService->NotifyWhenCanAttachSocket(event); + if (NS_FAILED(rv)) + PL_DestroyEvent(event); + return rv; + } // // create new socket fd, push io layers, etc. @@ -872,7 +959,7 @@ nsSocketTransport::InitiateSocket() PRBool proxyTransparent; PRBool usingSSL; - nsresult rv = BuildSocket(fd, proxyTransparent, usingSSL); + rv = BuildSocket(fd, proxyTransparent, usingSSL); if (NS_FAILED(rv)) { LOG((" BuildSocket failed [rv=%x]\n", rv)); return rv; @@ -1035,7 +1122,7 @@ nsSocketTransport::RecoverFromError() msg = MSG_ENSURE_CONNECT; } - rv = gSocketTransportService->PostEvent(this, msg, NS_OK, nsnull); + rv = PostEvent(msg, NS_OK); if (NS_FAILED(rv)) tryAgain = PR_FALSE; } @@ -1131,11 +1218,11 @@ nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd) //----------------------------------------------------------------------------- // socket event handler impl -NS_IMETHODIMP -nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) +void +nsSocketTransport::OnSocketEvent(PRUint32 type, nsresult status, nsISupports *param) { - LOG(("nsSocketTransport::OnSocketEvent [this=%x type=%u u=%x v=%x]\n", - this, type, uparam, vparam)); + LOG(("nsSocketTransport::OnSocketEvent [this=%p type=%u status=%x param=%p]\n", + this, type, status, param)); if (NS_FAILED(mCondition)) { // block event since we're apparently already dead. @@ -1145,7 +1232,7 @@ nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) // mInput.OnSocketReady(mCondition); mOutput.OnSocketReady(mCondition); - return NS_OK; + return; } switch (type) { @@ -1164,19 +1251,17 @@ nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) case MSG_DNS_LOOKUP_COMPLETE: LOG((" MSG_DNS_LOOKUP_COMPLETE\n")); mDNSRequest = 0; - if (vparam) { - nsIDNSRecord *rec = NS_REINTERPRET_CAST(nsIDNSRecord *, vparam); - mDNSRecord = rec; - NS_RELEASE(rec); + if (param) { + mDNSRecord = NS_STATIC_CAST(nsIDNSRecord *, param); mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr); } - // uparam contains DNS lookup status - if (NS_FAILED(uparam)) { + // status contains DNS lookup status + if (NS_FAILED(status)) { // fixup error code if proxy was not found - if ((uparam == NS_ERROR_UNKNOWN_HOST) && !mProxyHost.IsEmpty()) + if ((status == NS_ERROR_UNKNOWN_HOST) && !mProxyHost.IsEmpty()) mCondition = NS_ERROR_UNKNOWN_PROXY_HOST; else - mCondition = uparam; + mCondition = status; } else if (mState == STATE_RESOLVING) mCondition = InitiateSocket(); @@ -1188,7 +1273,7 @@ nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) case MSG_INPUT_CLOSED: LOG((" MSG_INPUT_CLOSED\n")); - OnMsgInputClosed(uparam); + OnMsgInputClosed(status); break; case MSG_INPUT_PENDING: @@ -1198,7 +1283,7 @@ nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) case MSG_OUTPUT_CLOSED: LOG((" MSG_OUTPUT_CLOSED\n")); - OnMsgOutputClosed(uparam); + OnMsgOutputClosed(status); break; case MSG_OUTPUT_PENDING: @@ -1217,8 +1302,6 @@ nsSocketTransport::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) } else if (mPollFlags == PR_POLL_EXCEPT) mPollFlags = 0; // make idle - - return NS_OK; } //----------------------------------------------------------------------------- @@ -1331,8 +1414,7 @@ nsSocketTransport::OnSocketDetached(PRFileDesc *fd) //----------------------------------------------------------------------------- // xpcom api -NS_IMPL_THREADSAFE_ISUPPORTS4(nsSocketTransport, - nsISocketEventHandler, +NS_IMPL_THREADSAFE_ISUPPORTS3(nsSocketTransport, nsISocketTransport, nsITransport, nsIDNSListener) @@ -1366,8 +1448,8 @@ nsSocketTransport::OpenInputStream(PRUint32 flags, if (NS_FAILED(rv)) return rv; // async copy from socket to pipe - rv = NS_AsyncCopy(&mInput, pipeOut, PR_FALSE, PR_TRUE, - segsize, 1, segalloc); + rv = NS_AsyncCopy(&mInput, pipeOut, gSocketTransportService, + NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize); if (NS_FAILED(rv)) return rv; *result = pipeIn; @@ -1378,8 +1460,7 @@ nsSocketTransport::OpenInputStream(PRUint32 flags, // flag input stream as open mInputClosed = PR_FALSE; - rv = gSocketTransportService->PostEvent(this, MSG_ENSURE_CONNECT, - 0, nsnull); + rv = PostEvent(MSG_ENSURE_CONNECT); if (NS_FAILED(rv)) return rv; NS_ADDREF(*result); @@ -1414,8 +1495,8 @@ nsSocketTransport::OpenOutputStream(PRUint32 flags, if (NS_FAILED(rv)) return rv; // async copy from socket to pipe - rv = NS_AsyncCopy(pipeIn, &mOutput, PR_TRUE, PR_FALSE, - segsize, 1, segalloc); + rv = NS_AsyncCopy(pipeIn, &mOutput, gSocketTransportService, + NS_ASYNCCOPY_VIA_READSEGMENTS, segsize); if (NS_FAILED(rv)) return rv; *result = pipeOut; @@ -1426,8 +1507,7 @@ nsSocketTransport::OpenOutputStream(PRUint32 flags, // flag output stream as open mOutputClosed = PR_FALSE; - rv = gSocketTransportService->PostEvent(this, MSG_ENSURE_CONNECT, - 0, nsnull); + rv = PostEvent(MSG_ENSURE_CONNECT); if (NS_FAILED(rv)) return rv; NS_ADDREF(*result); @@ -1440,8 +1520,8 @@ nsSocketTransport::Close(nsresult reason) if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED; - mInput.CloseEx(reason); - mOutput.CloseEx(reason); + mInput.CloseWithStatus(reason); + mOutput.CloseWithStatus(reason); return NS_OK; } @@ -1472,19 +1552,14 @@ nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks) NS_IMETHODIMP nsSocketTransport::SetEventSink(nsITransportEventSink *sink, - nsIEventQueue *eventQ) + nsIEventTarget *target) { nsCOMPtr temp; - if (eventQ) { - nsresult rv; - - rv = NS_GetProxyForObject(eventQ, - NS_GET_IID(nsITransportEventSink), - sink, - PROXY_ASYNC | PROXY_ALWAYS, - getter_AddRefs(temp)); - if (NS_FAILED(rv)) return rv; - + if (target) { + nsresult rv = net_NewTransportEventSinkProxy(getter_AddRefs(temp), + sink, target); + if (NS_FAILED(rv)) + return rv; sink = temp.get(); } @@ -1556,21 +1631,14 @@ nsSocketTransport::OnLookupComplete(nsIDNSRequest *request, nsIDNSRecord *rec, nsresult status) { - // event handler will release this reference. - NS_IF_ADDREF(rec); - - nsresult rv = gSocketTransportService->PostEvent(this, - MSG_DNS_LOOKUP_COMPLETE, - status, rec); + nsresult rv = PostEvent(MSG_DNS_LOOKUP_COMPLETE, status, rec); // if posting a message fails, then we should assume that the socket // transport has been shutdown. this should never happen! if it does // it means that the socket transport service was shutdown before the // DNS service. - if (NS_FAILED(rv)) { + if (NS_FAILED(rv)) NS_WARNING("unable to post DNS lookup complete message"); - NS_IF_RELEASE(rec); - } return NS_OK; } diff --git a/mozilla/netwerk/base/src/nsSocketTransport2.h b/mozilla/netwerk/base/src/nsSocketTransport2.h index 85e4fa1c492..cd9ff57fb6c 100644 --- a/mozilla/netwerk/base/src/nsSocketTransport2.h +++ b/mozilla/netwerk/base/src/nsSocketTransport2.h @@ -75,13 +75,14 @@ public: void OnSocketReady(nsresult condition); private: - nsSocketTransport *mTransport; - nsrefcnt mReaderRefCnt; + nsSocketTransport *mTransport; + nsrefcnt mReaderRefCnt; // access to these is protected by mTransport->mLock - nsresult mCondition; - nsCOMPtr mNotify; - PRUint32 mByteCount; + nsresult mCondition; + nsCOMPtr mCallback; + PRUint32 mCallbackFlags; + PRUint32 mByteCount; }; //----------------------------------------------------------------------------- @@ -108,25 +109,24 @@ private: const char *, PRUint32 offset, PRUint32 count, PRUint32 *countRead); - nsSocketTransport *mTransport; - nsrefcnt mWriterRefCnt; + nsSocketTransport *mTransport; + nsrefcnt mWriterRefCnt; // access to these is protected by mTransport->mLock - nsresult mCondition; - nsCOMPtr mNotify; - PRUint32 mByteCount; + nsresult mCondition; + nsCOMPtr mCallback; + PRUint32 mCallbackFlags; + PRUint32 mByteCount; }; //----------------------------------------------------------------------------- class nsSocketTransport : public nsASocketHandler - , public nsISocketEventHandler , public nsISocketTransport , public nsIDNSListener { public: NS_DECL_ISUPPORTS - NS_DECL_NSISOCKETEVENTHANDLER NS_DECL_NSITRANSPORT NS_DECL_NSISOCKETTRANSPORT NS_DECL_NSIDNSLISTENER @@ -141,19 +141,24 @@ public: void OnSocketReady(PRFileDesc *, PRInt16 outFlags); void OnSocketDetached(PRFileDesc *); + // called when a socket event is handled + void OnSocketEvent(PRUint32 type, nsresult status, nsISupports *param); + private: virtual ~nsSocketTransport(); + // event types enum { - MSG_ENSURE_CONNECT, // no args - MSG_DNS_LOOKUP_COMPLETE, // uparam holds "status" - MSG_RETRY_INIT_SOCKET, // no args - MSG_INPUT_CLOSED, // uparam holds "reason" - MSG_INPUT_PENDING, // no args - MSG_OUTPUT_CLOSED, // uparam holds "reason" - MSG_OUTPUT_PENDING // no args + MSG_ENSURE_CONNECT, + MSG_DNS_LOOKUP_COMPLETE, + MSG_RETRY_INIT_SOCKET, + MSG_INPUT_CLOSED, + MSG_INPUT_PENDING, + MSG_OUTPUT_CLOSED, + MSG_OUTPUT_PENDING }; + nsresult PostEvent(PRUint32 type, nsresult status = NS_OK, nsISupports *param = nsnull); enum { STATE_CLOSED, @@ -253,7 +258,7 @@ private: if (PR_GetCurrentThread() == gSocketThread) OnMsgInputClosed(reason); else - gSocketTransportService->PostEvent(this, MSG_INPUT_CLOSED, reason, nsnull); + PostEvent(MSG_INPUT_CLOSED, reason); } void OnInputPending() { @@ -261,7 +266,7 @@ private: if (PR_GetCurrentThread() == gSocketThread) OnMsgInputPending(); else - gSocketTransportService->PostEvent(this, MSG_INPUT_PENDING, 0, nsnull); + PostEvent(MSG_INPUT_PENDING); } void OnOutputClosed(nsresult reason) { @@ -269,7 +274,7 @@ private: if (PR_GetCurrentThread() == gSocketThread) OnMsgOutputClosed(reason); // XXX need to not be inside lock! else - gSocketTransportService->PostEvent(this, MSG_OUTPUT_CLOSED, reason, nsnull); + PostEvent(MSG_OUTPUT_CLOSED, reason); } void OnOutputPending() { @@ -277,7 +282,7 @@ private: if (PR_GetCurrentThread() == gSocketThread) OnMsgOutputPending(); else - gSocketTransportService->PostEvent(this, MSG_OUTPUT_PENDING, 0, nsnull); + PostEvent(MSG_OUTPUT_PENDING); } }; diff --git a/mozilla/netwerk/base/src/nsSocketTransportService2.cpp b/mozilla/netwerk/base/src/nsSocketTransportService2.cpp index 5c3cdcca5f9..d7f5ade9ddb 100644 --- a/mozilla/netwerk/base/src/nsSocketTransportService2.cpp +++ b/mozilla/netwerk/base/src/nsSocketTransportService2.cpp @@ -56,6 +56,22 @@ PRLogModuleInfo *gSocketTransportLog = nsnull; nsSocketTransportService *gSocketTransportService = nsnull; PRThread *gSocketThread = nsnull; +#define PLEVENT_FROM_LINK(_link) \ + NS_REINTERPRET_CAST(PLEvent *, \ + NS_REINTERPRET_CAST(char *, _link) - offsetof(PLEvent, link)) + +static inline void +MoveCList(PRCList &from, PRCList &to) +{ + if (!PR_CLIST_IS_EMPTY(&from)) { + to.next = from.next; + to.prev = from.prev; + to.next->prev = &to; + to.prev->next = &to; + PR_INIT_CLIST(&from); + } +} + //----------------------------------------------------------------------------- // ctor/dtor (called on the main/UI thread by the service manager) @@ -64,13 +80,9 @@ nsSocketTransportService::nsSocketTransportService() , mThread(nsnull) , mThreadEvent(nsnull) , mAutodialEnabled(PR_FALSE) - , mEventQHead(nsnull) - , mEventQTail(nsnull) , mEventQLock(PR_NewLock()) , mActiveCount(0) , mIdleCount(0) - , mPendingQHead(nsnull) - , mPendingQTail(nsnull) { #if defined(PR_LOGGING) gSocketTransportLog = PR_NewLogModule("nsSocketTransport"); @@ -78,6 +90,9 @@ nsSocketTransportService::nsSocketTransportService() NS_ASSERTION(nsIThread::IsMainThread(), "wrong thread"); + PR_INIT_CLIST(&mEventQ); + PR_INIT_CLIST(&mPendingSocketQ); + gSocketTransportService = this; } @@ -98,29 +113,17 @@ nsSocketTransportService::~nsSocketTransportService() // event queue (any thread) NS_IMETHODIMP -nsSocketTransportService::PostEvent(nsISocketEventHandler *handler, - PRUint32 type, PRUint32 uparam, - void *vparam) +nsSocketTransportService::PostEvent(PLEvent *event) { - LOG(("nsSocketTransportService::PostEvent [handler=%x type=%u u=%x v=%x]\n", - handler, type, uparam, vparam)); + LOG(("nsSocketTransportService::PostEvent [event=%p]\n", event)); - NS_ASSERTION(handler, "null handler"); + NS_ASSERTION(event, "null event"); nsAutoLock lock(mEventQLock); if (!mInitialized) return NS_ERROR_OFFLINE; - SocketEvent *event = new SocketEvent(handler, type, uparam, vparam); - if (!event) - return NS_ERROR_OUT_OF_MEMORY; - - // XXX generalize this into some kind of template class - if (mEventQTail) - mEventQTail->mNext = event; - mEventQTail = event; - if (!mEventQHead) - mEventQHead = event; + PR_APPEND_LINK(&event->link, &mEventQ); if (mThreadEvent) PR_SetPollableEvent(mThreadEvent); @@ -128,31 +131,27 @@ nsSocketTransportService::PostEvent(nsISocketEventHandler *handler, return NS_OK; } +NS_IMETHODIMP +nsSocketTransportService::IsOnCurrentThread(PRBool *result) +{ + *result = (PR_GetCurrentThread() == gSocketThread); + return NS_OK; +} + //----------------------------------------------------------------------------- // socket api (socket thread only) nsresult -nsSocketTransportService::NotifyWhenCanAttachSocket(nsISocketEventHandler *handler, - PRUint32 msg) +nsSocketTransportService::NotifyWhenCanAttachSocket(PLEvent *event) { LOG(("nsSocketTransportService::NotifyWhenCanAttachSocket\n")); if (CanAttachSocket()) { NS_WARNING("should have called CanAttachSocket"); - return PostEvent(handler, msg, 0, nsnull); + return PostEvent(event); } - PendingSocket *ps = new PendingSocket(handler, msg); - if (!ps) - return NS_ERROR_OUT_OF_MEMORY; - - // XXX generalize this into some kind of template class - if (mPendingQTail) - mPendingQTail->mNext = ps; - mPendingQTail = ps; - if (!mPendingQHead) - mPendingQHead = ps; - + PR_APPEND_LINK(&event->link, &mPendingSocketQ); return NS_OK; } @@ -193,15 +192,13 @@ nsSocketTransportService::DetachSocket(SocketContext *sock) // NOTE: sock is now an invalid pointer // - // notify the first element on the pending socket handler queue... + // notify the first element on the pending socket queue... // - if (mPendingQHead) { - PendingSocket *ps = mPendingQHead; - mPendingQHead = ps->mNext; - if (!mPendingQHead) - mPendingQTail = nsnull; - PostEvent(ps->mHandler, ps->mMsg, 0, nsnull); - delete ps; + if (!PR_CLIST_IS_EMPTY(&mPendingSocketQ)) { + // move event from pending queue to event queue + PLEvent *event = PLEVENT_FROM_LINK(PR_LIST_HEAD(&mPendingSocketQ)); + PR_REMOVE_AND_INIT_LINK(&event->link); + PostEvent(event); } return NS_OK; } @@ -330,26 +327,23 @@ nsSocketTransportService::ServiceEventQ() PRBool keepGoing; // grab the event queue - SocketEvent *head = nsnull, *event; + PRCList eq; + PR_INIT_CLIST(&eq); { nsAutoLock lock(mEventQLock); - head = mEventQHead; - mEventQHead = nsnull; - mEventQTail = nsnull; + MoveCList(mEventQ, eq); // check to see if we're supposed to shutdown keepGoing = mInitialized; } // service the event queue - while (head) { - head->mHandler->OnSocketEvent(head->mType, - head->mUparam, - head->mVparam); - // delete head of queue - event = head->mNext; - delete head; - head = event; + PLEvent *event; + while (!PR_CLIST_IS_EMPTY(&eq)) { + event = PLEVENT_FROM_LINK(PR_LIST_HEAD(&eq)); + PR_REMOVE_AND_INIT_LINK(&event->link); + + PL_HandleEvent(event); } return keepGoing; } @@ -358,8 +352,9 @@ nsSocketTransportService::ServiceEventQ() //----------------------------------------------------------------------------- // xpcom api -NS_IMPL_THREADSAFE_ISUPPORTS2(nsSocketTransportService, +NS_IMPL_THREADSAFE_ISUPPORTS3(nsSocketTransportService, nsISocketTransportService, + nsIEventTarget, nsIRunnable) // called from main thread only diff --git a/mozilla/netwerk/base/src/nsSocketTransportService2.h b/mozilla/netwerk/base/src/nsSocketTransportService2.h index f0c686ef05e..cecb031cb87 100644 --- a/mozilla/netwerk/base/src/nsSocketTransportService2.h +++ b/mozilla/netwerk/base/src/nsSocketTransportService2.h @@ -39,6 +39,7 @@ #define nsSocketTransportService2_h__ #include "nsISocketTransportService.h" +#include "nsIEventTarget.h" #include "nsIRunnable.h" #include "nsIThread.h" #include "nsCOMPtr.h" @@ -107,11 +108,13 @@ public: //----------------------------------------------------------------------------- class nsSocketTransportService : public nsISocketTransportService + , public nsIEventTarget , public nsIRunnable { public: NS_DECL_ISUPPORTS NS_DECL_NSISOCKETTRANSPORTSERVICE + NS_DECL_NSIEVENTTARGET NS_DECL_NSIRUNNABLE nsSocketTransportService(); @@ -128,11 +131,10 @@ public: // // if the number of sockets is at the limit, then consumers can be notified // when the number of sockets becomes less than the limit. the notification - // is asynchronous, delivered via the nsISocketEventHandler interface. the - // consumer can specify the message parameter passed to its OnSocketEvent - // method. the uparam and vparam args will be zero and null respectively. + // is asynchronous, delivered via the given PLEvent instance on the socket + // transport thread. // - nsresult NotifyWhenCanAttachSocket(nsISocketEventHandler *, PRUint32 msg); + nsresult NotifyWhenCanAttachSocket(PLEvent *); // // add a new socket to the list of controlled sockets. returns a socket @@ -163,26 +165,8 @@ private: // event queue (any thread) //------------------------------------------------------------------------- - struct SocketEvent - { - SocketEvent(nsISocketEventHandler *handler, - PRUint32 type, PRUint32 uparam, void *vparam) - : mHandler(handler) - , mType(type) - , mUparam(uparam) - , mVparam(vparam) - , mNext(nsnull) - { } - - nsCOMPtr mHandler; - PRUint32 mType; - PRUint32 mUparam; - void *mVparam; - struct SocketEvent *mNext; - }; - SocketEvent *mEventQHead; - SocketEvent *mEventQTail; - PRLock *mEventQLock; + PRCList mEventQ; // list of PLEvent objects + PRLock *mEventQLock; //------------------------------------------------------------------------- // socket lists (socket thread only) @@ -230,23 +214,10 @@ private: PRInt32 Poll(); // calls PR_Poll //------------------------------------------------------------------------- - // pending socket handler queue - see NotifyWhenCanAttachSocket + // pending socket queue - see NotifyWhenCanAttachSocket //------------------------------------------------------------------------- - struct PendingSocket - { - PendingSocket(nsISocketEventHandler *handler, PRUint32 msg) - : mHandler(handler) - , mMsg(msg) - , mNext(nsnull) - { } - - nsCOMPtr mHandler; - PRUint32 mMsg; - struct PendingSocket *mNext; - }; - PendingSocket *mPendingQHead; - PendingSocket *mPendingQTail; + PRCList mPendingSocketQ; // list of PLEvent objects }; extern nsSocketTransportService *gSocketTransportService; diff --git a/mozilla/netwerk/base/src/nsStreamTransportService.cpp b/mozilla/netwerk/base/src/nsStreamTransportService.cpp index 666f7a7c168..0d48f969d7d 100644 --- a/mozilla/netwerk/base/src/nsStreamTransportService.cpp +++ b/mozilla/netwerk/base/src/nsStreamTransportService.cpp @@ -38,9 +38,12 @@ #include "nsStreamTransportService.h" #include "nsNetSegmentUtils.h" #include "nsAutoLock.h" -#include "netCore.h" -#include "prlog.h" +#include "nsTransportUtils.h" +#include "nsStreamUtils.h" +#include "nsNetError.h" +#include "nsNetCID.h" +#include "nsIServiceManager.h" #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsISeekableStream.h" @@ -48,204 +51,61 @@ #include "nsITransport.h" #include "nsIRunnable.h" #include "nsIProxyObjectManager.h" - -#if defined(PR_LOGGING) -// -// set NSPR_LOG_MODULES=nsStreamTransport:5 -// -static PRLogModuleInfo *gSTSLog; -#endif -#define LOG(args) PR_LOG(gSTSLog, PR_LOG_DEBUG, args) - -#define MIN_THREADS 1 -#define MAX_THREADS 4 -#define DEFAULT_SEGMENT_SIZE 4096 -#define DEFAULT_SEGMENT_COUNT 16 - -static nsStreamTransportService *gSTS = nsnull; - -//----------------------------------------------------------------------------- - -inline static nsresult -NewEventSinkProxy(nsITransportEventSink *sink, nsIEventQueue *eventQ, - nsITransportEventSink **result) -{ - return NS_GetProxyForObject(eventQ, - NS_GET_IID(nsITransportEventSink), - sink, - PROXY_ASYNC | PROXY_ALWAYS, - (void **) result); -} +#include "nsIEventTarget.h" //----------------------------------------------------------------------------- // nsInputStreamTransport +// +// Implements nsIInputStream as a wrapper around the real input stream. This +// allows the transport to support seeking, range-limiting, progress reporting, +// and close-when-done semantics while utilizing NS_AsyncCopy. //----------------------------------------------------------------------------- -class nsInputStreamTransport : public nsIRunnable - , public nsITransport - , public nsIOutputStreamNotify +class nsInputStreamTransport : public nsITransport + , public nsIInputStream { public: NS_DECL_ISUPPORTS - NS_DECL_NSIRUNNABLE NS_DECL_NSITRANSPORT - NS_DECL_NSIOUTPUTSTREAMNOTIFY + NS_DECL_NSIINPUTSTREAM nsInputStreamTransport(nsIInputStream *source, PRUint32 offset, PRUint32 limit, PRBool closeWhenDone) : mSource(source) - , mSourceCondition(NS_OK) , mOffset(offset) , mLimit(limit) - , mSegSize(0) - , mInProgress(PR_FALSE) , mCloseWhenDone(closeWhenDone) , mFirstTime(PR_TRUE) + , mInProgress(PR_FALSE) { - NS_ADDREF(gSTS); } virtual ~nsInputStreamTransport() { - nsStreamTransportService *serv = gSTS; - NS_RELEASE(serv); } - static NS_METHOD FillPipeSegment(nsIOutputStream *, void *, char *, - PRUint32, PRUint32, PRUint32 *); - private: - // the pipe input end is never accessed from Run nsCOMPtr mPipeIn; - nsCOMPtr mPipeOut; + // while the copy is active, these members may only be accessed from the + // nsIInputStream implementation. nsCOMPtr mEventSink; nsCOMPtr mSource; - nsresult mSourceCondition; PRUint32 mOffset; PRUint32 mLimit; - PRUint32 mSegSize; - PRPackedBool mInProgress; PRPackedBool mCloseWhenDone; PRPackedBool mFirstTime; + + // this variable serves as a lock to prevent the state of the transport + // from being modified once the copy is in progress. + PRPackedBool mInProgress; }; -NS_IMPL_THREADSAFE_ISUPPORTS3(nsInputStreamTransport, - nsIRunnable, +NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamTransport, nsITransport, - nsIOutputStreamNotify) - -NS_METHOD -nsInputStreamTransport::FillPipeSegment(nsIOutputStream *stream, - void *closure, - char *segment, - PRUint32 offset, - PRUint32 count, - PRUint32 *countRead) -{ - nsInputStreamTransport *trans = (nsInputStreamTransport *) closure; - - // apply read limit - PRUint32 limit = trans->mLimit - trans->mOffset; - if (count > limit) { - count = limit; - if (count == 0) { - *countRead = 0; - return trans->mSourceCondition = NS_BASE_STREAM_CLOSED; - } - } - - nsresult rv = trans->mSource->Read(segment, count, countRead); - if (NS_FAILED(rv)) - trans->mSourceCondition = rv; - else if (*countRead == 0) - trans->mSourceCondition = NS_BASE_STREAM_CLOSED; - else { - trans->mOffset += *countRead; - if (trans->mEventSink) - trans->mEventSink->OnTransportStatus(trans, - nsITransport::STATUS_READING, - trans->mOffset, trans->mLimit); - } - - return trans->mSourceCondition; -} - -/** nsIRunnable **/ - -NS_IMETHODIMP -nsInputStreamTransport::Run() -{ - LOG(("nsInputStreamTransport::Run\n")); - - nsresult rv; - PRUint32 n; - - if (mFirstTime) { - mFirstTime = PR_FALSE; - - if (mOffset != ~0U) { - nsCOMPtr seekable = do_QueryInterface(mSource); - if (seekable) - seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); - } - - // mOffset now holds the number of bytes read, which we will use to - // enforce mLimit. - mOffset = 0; - } - - // do as much work as we can before yielding this thread. - for (;;) { - rv = mPipeOut->WriteSegments(FillPipeSegment, this, mSegSize, &n); - - LOG(("nsInputStreamTransport: WriteSegments returned [this=%x rv=%x n=%u]\n", - this, rv, n)); - - if (rv == NS_BASE_STREAM_WOULD_BLOCK) { - // wait for pipe to become writable - mPipeOut->AsyncWait(this, 0, nsnull); - break; - } - - if (NS_SUCCEEDED(rv)) { - if (n == 0) - rv = NS_BASE_STREAM_CLOSED; - } - else if (NS_FAILED(mSourceCondition)) - rv = mSourceCondition; - - // check for source error/eof - if (NS_FAILED(rv)) { - LOG(("nsInputStreamTransport: got write error [this=%x error=%x]\n", - this, rv)); - // close mPipeOut, propogating error condition... - mPipeOut->CloseEx(rv); - mPipeOut = 0; - if (mCloseWhenDone) - mSource->Close(); - mSource = 0; - break; - } - } - return NS_OK; -} - -/** nsIOutputStreamNotify **/ - -NS_IMETHODIMP -nsInputStreamTransport::OnOutputStreamReady(nsIAsyncOutputStream *stream) -{ - LOG(("nsInputStreamTransport::OnOutputStreamReady\n")); - - // called on whatever thread removed data from the pipe or closed it. - NS_ASSERTION(mPipeOut == stream, "wrong stream"); - nsresult rv = gSTS->Dispatch(this); - NS_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed"); - return NS_OK; -} + nsIInputStream) /** nsITransport **/ @@ -257,6 +117,11 @@ nsInputStreamTransport::OpenInputStream(PRUint32 flags, { NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS); + nsresult rv; + nsCOMPtr target = + do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + // XXX if the caller requests an unbuffered stream, then perhaps // we'd want to simply return mSource; however, then we would // not be reading mSource on a background thread. is this ok? @@ -266,20 +131,22 @@ nsInputStreamTransport::OpenInputStream(PRUint32 flags, net_ResolveSegmentParams(segsize, segcount); nsIMemory *segalloc = net_GetSegmentAlloc(segsize); - nsresult rv = NS_NewPipe2(getter_AddRefs(mPipeIn), - getter_AddRefs(mPipeOut), - nonblocking, PR_TRUE, - segsize, segcount, segalloc); + nsCOMPtr pipeOut; + rv = NS_NewPipe2(getter_AddRefs(mPipeIn), + getter_AddRefs(pipeOut), + nonblocking, PR_TRUE, + segsize, segcount, segalloc); if (NS_FAILED(rv)) return rv; - mSegSize = segsize; mInProgress = PR_TRUE; - rv = gSTS->Dispatch(this); // now writing to pipe - if (NS_FAILED(rv)) return rv; + // startup async copy process... + rv = NS_AsyncCopy(this, pipeOut, target, + NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize); + if (NS_SUCCEEDED(rv)) + NS_ADDREF(*result = mPipeIn); - NS_ADDREF(*result = mPipeIn); - return NS_OK; + return rv; } NS_IMETHODIMP @@ -299,191 +166,143 @@ nsInputStreamTransport::Close(nsresult reason) if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED; - return mPipeIn->CloseEx(reason); + return mPipeIn->CloseWithStatus(reason); } NS_IMETHODIMP nsInputStreamTransport::SetEventSink(nsITransportEventSink *sink, - nsIEventQueue *eventQ) + nsIEventTarget *target) { NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS); - if (eventQ) - return NewEventSinkProxy(sink, eventQ, getter_AddRefs(mEventSink)); + if (target) + return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink), + sink, target); mEventSink = sink; return NS_OK; } +/** nsIInputStream **/ + +NS_IMETHODIMP +nsInputStreamTransport::Close() +{ + if (mCloseWhenDone) + mSource->Close(); + + // make additional reads return early... + mOffset = mLimit = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsInputStreamTransport::Available(PRUint32 *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsInputStreamTransport::Read(char *buf, PRUint32 count, PRUint32 *result) +{ + if (mFirstTime) { + mFirstTime = PR_FALSE; + if (mOffset) { + nsCOMPtr seekable = do_QueryInterface(mSource); + if (seekable) + seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); + // reset offset to zero so we can use it to enforce limit + mOffset = 0; + } + } + + // limit amount read + PRUint32 max = mLimit - mOffset; + if (max == 0) { + *result = 0; + return NS_OK; + } + + if (count > max) + count = max; + + nsresult rv = mSource->Read(buf, count, result); + + if (NS_SUCCEEDED(rv)) { + mOffset += *result; + if (mEventSink) + mEventSink->OnTransportStatus(this, STATUS_READING, mOffset, mLimit); + } + return rv; +} + +NS_IMETHODIMP +nsInputStreamTransport::ReadSegments(nsWriteSegmentFun writer, void *closure, + PRUint32 count, PRUint32 *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsInputStreamTransport::IsNonBlocking(PRBool *result) +{ + *result = PR_FALSE; + return NS_OK; +} + //----------------------------------------------------------------------------- // nsOutputStreamTransport +// +// Implements nsIOutputStream as a wrapper around the real input stream. This +// allows the transport to support seeking, range-limiting, progress reporting, +// and close-when-done semantics while utilizing NS_AsyncCopy. //----------------------------------------------------------------------------- -class nsOutputStreamTransport : public nsIRunnable - , public nsITransport - , public nsIInputStreamNotify +class nsOutputStreamTransport : public nsITransport + , public nsIOutputStream { public: NS_DECL_ISUPPORTS - NS_DECL_NSIRUNNABLE NS_DECL_NSITRANSPORT - NS_DECL_NSIINPUTSTREAMNOTIFY + NS_DECL_NSIOUTPUTSTREAM nsOutputStreamTransport(nsIOutputStream *sink, PRUint32 offset, PRUint32 limit, PRBool closeWhenDone) : mSink(sink) - , mSinkCondition(NS_OK) , mOffset(offset) , mLimit(limit) - , mSegSize(0) - , mInProgress(PR_FALSE) , mCloseWhenDone(closeWhenDone) , mFirstTime(PR_TRUE) + , mInProgress(PR_FALSE) { - NS_ADDREF(gSTS); } virtual ~nsOutputStreamTransport() { - nsStreamTransportService *serv = gSTS; - NS_RELEASE(serv); } - static NS_METHOD ConsumePipeSegment(nsIInputStream *, void *, const char *, - PRUint32, PRUint32, PRUint32 *); - private: - // the pipe output end is never accessed from Run nsCOMPtr mPipeOut; - nsCOMPtr mPipeIn; + // while the copy is active, these members may only be accessed from the + // nsIOutputStream implementation. nsCOMPtr mEventSink; nsCOMPtr mSink; - nsresult mSinkCondition; PRUint32 mOffset; PRUint32 mLimit; - PRUint32 mSegSize; - PRPackedBool mInProgress; PRPackedBool mCloseWhenDone; PRPackedBool mFirstTime; + + // this variable serves as a lock to prevent the state of the transport + // from being modified once the copy is in progress. + PRPackedBool mInProgress; }; -NS_IMPL_THREADSAFE_ISUPPORTS3(nsOutputStreamTransport, - nsIRunnable, +NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamTransport, nsITransport, - nsIInputStreamNotify) - -NS_METHOD -nsOutputStreamTransport::ConsumePipeSegment(nsIInputStream *stream, - void *closure, - const char *segment, - PRUint32 offset, - PRUint32 count, - PRUint32 *countWritten) -{ - nsOutputStreamTransport *trans = (nsOutputStreamTransport *) closure; - - // apply write limit - PRUint32 limit = trans->mLimit - trans->mOffset; - if (count > limit) { - count = limit; - if (count == 0) { - *countWritten = 0; - return trans->mSinkCondition = NS_BASE_STREAM_CLOSED; - } - } - - nsresult rv = trans->mSink->Write(segment, count, countWritten); - if (NS_FAILED(rv)) - trans->mSinkCondition = rv; - else if (*countWritten == 0) - trans->mSinkCondition = NS_BASE_STREAM_CLOSED; - else { - trans->mOffset += *countWritten; - if (trans->mEventSink) - trans->mEventSink->OnTransportStatus(trans, - nsITransport::STATUS_WRITING, - trans->mOffset, trans->mLimit); - } - - return trans->mSinkCondition; -} - -/** nsIRunnable **/ - -NS_IMETHODIMP -nsOutputStreamTransport::Run() -{ - LOG(("nsOutputStreamTransport::Run\n")); - - nsresult rv; - PRUint32 n; - - if (mFirstTime) { - mFirstTime = PR_FALSE; - - if (mOffset != ~0U) { - nsCOMPtr seekable = do_QueryInterface(mSink); - if (seekable) - seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); - } - - // mOffset now holds the number of bytes written, which we will use to - // enforce mLimit. - mOffset = 0; - } - - // do as much work as we can before yielding this thread. - for (;;) { - rv = mPipeIn->ReadSegments(ConsumePipeSegment, this, mSegSize, &n); - - LOG(("nsOutputStreamTransport: ReadSegments returned [this=%x rv=%x n=%u]\n", - this, rv, n)); - - if (rv == NS_BASE_STREAM_WOULD_BLOCK) { - // wait for pipe to become readable - mPipeIn->AsyncWait(this, 0, nsnull); - break; - } - - if (NS_SUCCEEDED(rv)) { - if (n == 0) - rv = NS_BASE_STREAM_CLOSED; - } - else if (NS_FAILED(mSinkCondition)) - rv = mSinkCondition; - - // check for sink error - if (NS_FAILED(rv)) { - LOG(("nsOutputStreamTransport: got read error [this=%x error=%x]\n", - this, rv)); - // close mPipeIn, propogating error condition... - mPipeIn->CloseEx(rv); - mPipeIn = 0; - if (mCloseWhenDone) - mSink->Close(); - mSink = 0; - break; - } - } - return NS_OK; -} - -/** nsIInputStreamNotify **/ - -NS_IMETHODIMP -nsOutputStreamTransport::OnInputStreamReady(nsIAsyncInputStream *stream) -{ - LOG(("nsOutputStreamTransport::OnInputStreamReady\n")); - - // called on whatever thread added data to the pipe or closed it. - NS_ASSERTION(mPipeIn == stream, "wrong stream"); - nsresult rv = gSTS->Dispatch(this); - NS_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed"); - return NS_OK; -} + nsIOutputStream) /** nsITransport **/ @@ -506,6 +325,11 @@ nsOutputStreamTransport::OpenOutputStream(PRUint32 flags, { NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS); + nsresult rv; + nsCOMPtr target = + do_GetService(NS_IOTHREADPOOL_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + // XXX if the caller requests an unbuffered stream, then perhaps // we'd want to simply return mSink; however, then we would // not be writing to mSink on a background thread. is this ok? @@ -515,20 +339,22 @@ nsOutputStreamTransport::OpenOutputStream(PRUint32 flags, net_ResolveSegmentParams(segsize, segcount); nsIMemory *segalloc = net_GetSegmentAlloc(segsize); - nsresult rv = NS_NewPipe2(getter_AddRefs(mPipeIn), - getter_AddRefs(mPipeOut), - PR_TRUE, nonblocking, - segsize, segcount, segalloc); + nsCOMPtr pipeIn; + rv = NS_NewPipe2(getter_AddRefs(pipeIn), + getter_AddRefs(mPipeOut), + PR_TRUE, nonblocking, + segsize, segcount, segalloc); if (NS_FAILED(rv)) return rv; - mSegSize = segsize; mInProgress = PR_TRUE; - rv = gSTS->Dispatch(this); // now reading from pipe - if (NS_FAILED(rv)) return rv; + // startup async copy process... + rv = NS_AsyncCopy(pipeIn, this, target, + NS_ASYNCCOPY_VIA_READSEGMENTS, segsize); + if (NS_SUCCEEDED(rv)) + NS_ADDREF(*result = mPipeOut); - NS_ADDREF(*result = mPipeOut); - return NS_OK; + return rv; } NS_IMETHODIMP @@ -537,80 +363,100 @@ nsOutputStreamTransport::Close(nsresult reason) if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED; - return mPipeOut->CloseEx(reason); + return mPipeOut->CloseWithStatus(reason); } NS_IMETHODIMP nsOutputStreamTransport::SetEventSink(nsITransportEventSink *sink, - nsIEventQueue *eventQ) + nsIEventTarget *target) { NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS); - if (eventQ) - return NewEventSinkProxy(sink, eventQ, getter_AddRefs(mEventSink)); + if (target) + return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink), + sink, target); mEventSink = sink; return NS_OK; } +/** nsIOutputStream **/ + +NS_IMETHODIMP +nsOutputStreamTransport::Close() +{ + if (mCloseWhenDone) + mSink->Close(); + + // make additional writes return early... + mOffset = mLimit = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsOutputStreamTransport::Flush() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsOutputStreamTransport::Write(const char *buf, PRUint32 count, PRUint32 *result) +{ + if (mFirstTime) { + mFirstTime = PR_FALSE; + if (mOffset) { + nsCOMPtr seekable = do_QueryInterface(mSink); + if (seekable) + seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); + // reset offset to zero so we can use it to enforce limit + mOffset = 0; + } + } + + // limit amount written + PRUint32 max = mLimit - mOffset; + if (max == 0) { + *result = 0; + return NS_OK; + } + + if (count > max) + count = max; + + nsresult rv = mSink->Write(buf, count, result); + + if (NS_SUCCEEDED(rv)) { + mOffset += *result; + if (mEventSink) + mEventSink->OnTransportStatus(this, STATUS_WRITING, mOffset, mLimit); + } + return rv; +} + +NS_IMETHODIMP +nsOutputStreamTransport::WriteSegments(nsReadSegmentFun reader, void *closure, + PRUint32 count, PRUint32 *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsOutputStreamTransport::WriteFrom(nsIInputStream *in, PRUint32 count, PRUint32 *result) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsOutputStreamTransport::IsNonBlocking(PRBool *result) +{ + *result = PR_FALSE; + return NS_OK; +} + //----------------------------------------------------------------------------- // nsStreamTransportService //----------------------------------------------------------------------------- -nsStreamTransportService::nsStreamTransportService() - : mLock(PR_NewLock()) -{ - gSTS = this; - -#if defined(PR_LOGGING) - gSTSLog = PR_NewLogModule("nsStreamTransport"); -#endif -} - -nsStreamTransportService::~nsStreamTransportService() -{ - gSTS = 0; - - if (mLock) - PR_DestroyLock(mLock); -} - -NS_IMETHODIMP -nsStreamTransportService::Init() -{ - nsAutoLock lock(mLock); - - LOG(("nsStreamTransportService::Init\n")); - - return NS_NewThreadPool(getter_AddRefs(mPool), - MIN_THREADS, MAX_THREADS, 0); -} - -NS_IMETHODIMP -nsStreamTransportService::Shutdown() -{ - nsAutoLock lock(mLock); - - LOG(("nsStreamTransportService::Shutdown\n")); - - if (mPool) { - mPool->Shutdown(); - mPool = 0; - } - return NS_OK; -} - -nsresult -nsStreamTransportService::Dispatch(nsIRunnable *runnable) -{ - nsAutoLock lock(mLock); - - if (!mPool) - return NS_ERROR_NOT_INITIALIZED; - - return mPool->DispatchRequest(runnable); -} - NS_IMPL_THREADSAFE_ISUPPORTS1(nsStreamTransportService, nsIStreamTransportService) NS_IMETHODIMP @@ -620,9 +466,6 @@ nsStreamTransportService::CreateInputTransport(nsIInputStream *stream, PRBool closeWhenDone, nsITransport **result) { - nsAutoLock lock(mLock); - NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED); - nsInputStreamTransport *trans = new nsInputStreamTransport(stream, offset, limit, closeWhenDone); if (!trans) @@ -638,9 +481,6 @@ nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream, PRBool closeWhenDone, nsITransport **result) { - nsAutoLock lock(mLock); - NS_ENSURE_TRUE(mPool, NS_ERROR_NOT_INITIALIZED); - nsOutputStreamTransport *trans = new nsOutputStreamTransport(stream, offset, limit, closeWhenDone); if (!trans) @@ -648,4 +488,3 @@ nsStreamTransportService::CreateOutputTransport(nsIOutputStream *stream, NS_ADDREF(*result = trans); return NS_OK; } - diff --git a/mozilla/netwerk/base/src/nsStreamTransportService.h b/mozilla/netwerk/base/src/nsStreamTransportService.h index 63fb6dbb85b..b467115108b 100644 --- a/mozilla/netwerk/base/src/nsStreamTransportService.h +++ b/mozilla/netwerk/base/src/nsStreamTransportService.h @@ -36,9 +36,6 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIStreamTransportService.h" -#include "nsIThreadPool.h" -#include "nsCOMPtr.h" -#include "prlock.h" class nsStreamTransportService : public nsIStreamTransportService { @@ -46,15 +43,6 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSISTREAMTRANSPORTSERVICE - nsStreamTransportService(); - virtual ~nsStreamTransportService(); - - /** used internally **/ - nsresult Dispatch(nsIRunnable *); - -private: - nsresult Dispatch_Locked(nsIRunnable *); - - nsCOMPtr mPool; - PRLock *mLock; + nsStreamTransportService() {} + virtual ~nsStreamTransportService() {} }; diff --git a/mozilla/netwerk/base/src/nsTransportUtils.cpp b/mozilla/netwerk/base/src/nsTransportUtils.cpp new file mode 100644 index 00000000000..ab8bd1cd7d0 --- /dev/null +++ b/mozilla/netwerk/base/src/nsTransportUtils.cpp @@ -0,0 +1,194 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM, Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsTransportUtils.h" +#include "nsITransport.h" +#include "nsIEventTarget.h" +#include "nsProxyRelease.h" +#include "nsAutoLock.h" +#include "nsCOMPtr.h" +#include "plevent.h" + +//----------------------------------------------------------------------------- + +class nsTransportStatusEvent; + +class nsTransportEventSinkProxy : public nsITransportEventSink +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSITRANSPORTEVENTSINK + + nsTransportEventSinkProxy(nsITransportEventSink *sink, + nsIEventTarget *target, + PRBool coalesceAll) + : mSink(sink) + , mTarget(target) + , mLock(PR_NewLock()) + , mLastEvent(nsnull) + , mCoalesceAll(coalesceAll) + { + NS_ADDREF(mSink); + } + + virtual ~nsTransportEventSinkProxy() + { + if (mLock) + PR_DestroyLock(mLock); + + // our reference to mSink could be the last, so be sure to release + // it on the target thread. otherwise, we could get into trouble. + NS_ProxyRelease(mTarget, mSink); + } + + nsITransportEventSink *mSink; + nsCOMPtr mTarget; + PRLock *mLock; + nsTransportStatusEvent *mLastEvent; + PRBool mCoalesceAll; +}; + +class nsTransportStatusEvent : public PLEvent +{ +public: + nsTransportStatusEvent(nsTransportEventSinkProxy *proxy, + nsITransport *transport, + nsresult status, + PRUint32 progress, + PRUint32 progressMax) + : mTransport(transport) + , mStatus(status) + , mProgress(progress) + , mProgressMax(progressMax) + { + NS_ADDREF(proxy); + PL_InitEvent(this, proxy, HandleEvent, DestroyEvent); + } + + ~nsTransportStatusEvent() + { + nsTransportEventSinkProxy *proxy = + (nsTransportEventSinkProxy *) owner; + NS_RELEASE(proxy); + } + + PR_STATIC_CALLBACK(void*) HandleEvent(PLEvent *event) + { + nsTransportStatusEvent *self = (nsTransportStatusEvent *) event; + nsTransportEventSinkProxy *proxy = (nsTransportEventSinkProxy *) event->owner; + + // since this event is being handled, we need to clear the proxy's ref. + // if not coalescing all, then last event may not equal self! + { + nsAutoLock lock(proxy->mLock); + if (proxy->mLastEvent == self) + proxy->mLastEvent = nsnull; + } + + proxy->mSink->OnTransportStatus(self->mTransport, + self->mStatus, + self->mProgress, + self->mProgressMax); + return nsnull; + } + + PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *event) + { + delete (nsTransportStatusEvent *) event; + } + + // parameters to OnTransportStatus + nsCOMPtr mTransport; + nsresult mStatus; + PRUint32 mProgress; + PRUint32 mProgressMax; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsTransportEventSinkProxy, nsITransportEventSink) + +NS_IMETHODIMP +nsTransportEventSinkProxy::OnTransportStatus(nsITransport *transport, + nsresult status, + PRUint32 progress, + PRUint32 progressMax) +{ + nsresult rv = NS_OK; + PLEvent *event; + { + nsAutoLock lock(mLock); + + // try to coalesce events! ;-) + if (mLastEvent && (mCoalesceAll || mLastEvent->mStatus == status)) { + mLastEvent->mStatus = status; + mLastEvent->mProgress = progress; + mLastEvent->mProgressMax = progressMax; + event = nsnull; + } + else { + event = new nsTransportStatusEvent(this, transport, status, + progress, progressMax); + if (!event) + rv = NS_ERROR_OUT_OF_MEMORY; + mLastEvent = (nsTransportStatusEvent *) event; + } + } + if (event) { + rv = mTarget->PostEvent(event); + if (NS_FAILED(rv)) { + NS_WARNING("unable to post transport status event"); + PL_DestroyEvent(event); + + nsAutoLock lock(mLock); // cleanup.. don't reference anymore! + mLastEvent = nsnull; + } + } + return rv; +} + +//----------------------------------------------------------------------------- + +nsresult +net_NewTransportEventSinkProxy(nsITransportEventSink **result, + nsITransportEventSink *sink, + nsIEventTarget *target, + PRBool coalesceAll) +{ + *result = new nsTransportEventSinkProxy(sink, target, coalesceAll); + if (!*result) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(*result); + return NS_OK; +} diff --git a/mozilla/netwerk/base/src/nsTransportUtils.h b/mozilla/netwerk/base/src/nsTransportUtils.h new file mode 100644 index 00000000000..ae3cef9a028 --- /dev/null +++ b/mozilla/netwerk/base/src/nsTransportUtils.h @@ -0,0 +1,61 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM, Corp. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsTransportUtils_h__ +#define nsTransportUtils_h__ + +#include "nsITransport.h" + +/** + * This function returns a proxy object for a transport event sink instance. + * The transport event sink will be called on the thread indicated by the + * given event target. Like events are automatically coalesced. This means + * that for example if the status value is the same from event to event, and + * the previous event has not yet been delivered, then only one event will + * be delivered. The progress reported will be that from the second event. + * If aCoalesceAllEvents is true, then any undelivered event will be replaced + * with the next event if it arrives early enough. This option should be used + * cautiously since it can cause states to be effectively skipped. Coalescing + * events can help prevent a backlog of unprocessed transport events in the + * case that the target thread is overworked. + */ +nsresult +net_NewTransportEventSinkProxy(nsITransportEventSink **aResult, + nsITransportEventSink *aSink, + nsIEventTarget *aTarget, + PRBool aCoalesceAllEvents = PR_FALSE); + +#endif // nsTransportUtils_h__ diff --git a/mozilla/netwerk/build/nsNetCID.h b/mozilla/netwerk/build/nsNetCID.h index 24d52f97da9..b8b33923036 100644 --- a/mozilla/netwerk/build/nsNetCID.h +++ b/mozilla/netwerk/build/nsNetCID.h @@ -57,6 +57,20 @@ {0x93, 0x37, 0x00, 0x10, 0x4b, 0xa0, 0xfd, 0x40} \ } +// service implementing nsIEventTarget. events dispatched to this event +// target will be executed on one of necko's background i/o threads. +#define NS_IOTHREADPOOL_CLASSNAME \ + "nsIOThreadPool" +#define NS_IOTHREADPOOL_CONTRACTID \ + "@mozilla.org/network/io-thread-pool;1" +#define NS_IOTHREADPOOL_CID \ +{ /* f1d62b49-5051-48e2-9155-c3509428461e */ \ + 0xf1d62b49, \ + 0x5051, \ + 0x48e2, \ + {0x91, 0x55, 0xc3, 0x50, 0x94, 0x28, 0x46, 0x1e} \ +} + // service implementing nsIProtocolProxyService. #define NS_PROTOCOLPROXYSERVICE_CLASSNAME \ "nsProtocolProxyService" diff --git a/mozilla/netwerk/build/nsNetModule.cpp b/mozilla/netwerk/build/nsNetModule.cpp index ce2662c9c8e..bd3b8c83e80 100644 --- a/mozilla/netwerk/build/nsNetModule.cpp +++ b/mozilla/netwerk/build/nsNetModule.cpp @@ -55,6 +55,7 @@ #include "nsMIMEInputStream.h" #include "nsSOCKSSocketProvider.h" #include "nsCacheService.h" +#include "nsIOThreadPool.h" #include "nsNetCID.h" @@ -78,7 +79,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDNSService, Init) NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsProtocolProxyService, Init) #include "nsStreamTransportService.h" -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStreamTransportService, Init) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsStreamTransportService) #include "nsSocketTransportService2.h" #undef LOG @@ -567,6 +568,10 @@ static const nsModuleComponentInfo gNetModuleInfo[] = { NS_IOSERVICE_CID, NS_IOSERVICE_CONTRACTID, nsIOServiceConstructor }, + { NS_IOTHREADPOOL_CLASSNAME, + NS_IOTHREADPOOL_CID, + NS_IOTHREADPOOL_CONTRACTID, + net_NewIOThreadPool }, { NS_STREAMTRANSPORTSERVICE_CLASSNAME, NS_STREAMTRANSPORTSERVICE_CID, NS_STREAMTRANSPORTSERVICE_CONTRACTID, diff --git a/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp b/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp index 1a7b5416ceb..cfeac1457de 100644 --- a/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp +++ b/mozilla/netwerk/protocol/file/src/nsFileChannel.cpp @@ -438,6 +438,7 @@ nsFileChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) // nsCOMPtr copier; rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, asyncOut, + nsnull, // perform copy using default i/o thread PR_FALSE, // assume the upload stream is unbuffered PR_TRUE); // but, the async output stream is buffered! if (NS_FAILED(rv)) return rv; diff --git a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp index 3e3f621f0c9..ee4d7f2f0cd 100644 --- a/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp +++ b/mozilla/netwerk/protocol/ftp/src/nsFtpConnectionThread.cpp @@ -1648,11 +1648,18 @@ nsFtpState::R_stor() { rv = mDPipe->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, getter_AddRefs(output)); if (NS_FAILED(rv)) return FTP_ERROR; + + // perform the data copy on the socket transport thread. we do this + // because "output" is a socket output stream, so the result is that + // all work will be done on the socket transport thread. + nsCOMPtr stEventTarget = do_GetService(kSocketTransportServiceCID, &rv); + if (NS_FAILED(rv)) return FTP_ERROR; nsCOMPtr copier; rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier), mWriteStream, output, + stEventTarget, PR_TRUE, // mWriteStream is buffered PR_FALSE); // output is NOT buffered if (NS_FAILED(rv)) return FTP_ERROR; diff --git a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp index 2d6ff204412..6b7525e781d 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -236,13 +236,12 @@ nsHttpChannel::AsyncCall(nsAsyncCallback funcPtr) nsHttpChannel::AsyncCall_EventHandlerFunc, nsHttpChannel::AsyncCall_EventCleanupFunc); - PRStatus status = eventQ->PostEvent(event); - if (status != PR_SUCCESS) { - delete event; + nsresult rv = eventQ->PostEvent(event); + if (NS_FAILED(rv)) { + PL_DestroyEvent(event); NS_RELEASE_THIS(); - return NS_ERROR_FAILURE; } - return NS_OK; + return rv; } void *PR_CALLBACK @@ -347,24 +346,20 @@ nsHttpChannel::AsyncAbort(nsresult status) mStatus = status; mIsPending = PR_FALSE; - // create an async proxy for the listener.. - nsCOMPtr mgr; - gHttpHandler->GetProxyObjectManager(getter_AddRefs(mgr)); - if (mgr) { - nsCOMPtr observer; - mgr->GetProxyForObject(NS_CURRENT_EVENTQ, - NS_GET_IID(nsIRequestObserver), - mListener, - PROXY_ASYNC | PROXY_ALWAYS, - getter_AddRefs(observer)); - if (observer) { - observer->OnStartRequest(this, mListenerContext); - observer->OnStopRequest(this, mListenerContext, mStatus); - } - mListener = 0; - mListenerContext = 0; + // create a proxy for the listener.. + nsCOMPtr observer; + NS_NewRequestObserverProxy(getter_AddRefs(observer), + mListener, NS_CURRENT_EVENTQ); + if (observer) { + observer->OnStartRequest(this, mListenerContext); + observer->OnStopRequest(this, mListenerContext, mStatus); } - // XXX else, no proxy object manager... what do we do? + else { + NS_ERROR("unable to create request observer proxy"); + // XXX else, no proxy object manager... what do we do? + } + mListener = 0; + mListenerContext = 0; // finally remove ourselves from the load group. if (mLoadGroup) diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp b/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp index c7d48f105d0..9945b9f7c9a 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnection.cpp @@ -139,7 +139,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps) } // wait for the output stream to be readable - rv = mSocketOut->AsyncWait(this, 0, nsnull); + rv = mSocketOut->AsyncWait(this, 0, 0, nsnull); if (NS_SUCCEEDED(rv)) return rv; @@ -333,7 +333,7 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans, *reset = PR_TRUE; ProxyStartSSL(); mCompletedSSLConnect = PR_TRUE; - nsresult rv = mSocketOut->AsyncWait(this, 0, nsnull); + nsresult rv = mSocketOut->AsyncWait(this, 0, 0, nsnull); // XXX what if this fails -- need to handle this error NS_ASSERTION(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed"); } @@ -363,7 +363,7 @@ nsHttpConnection::ResumeSend() NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); if (mSocketOut) - return mSocketOut->AsyncWait(this, 0, nsnull); + return mSocketOut->AsyncWait(this, 0, 0, nsnull); NS_NOTREACHED("no socket output stream"); return NS_ERROR_UNEXPECTED; @@ -377,7 +377,7 @@ nsHttpConnection::ResumeRecv() NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); if (mSocketIn) - return mSocketIn->AsyncWait(this, 0, nsnull); + return mSocketIn->AsyncWait(this, 0, 0, nsnull); NS_NOTREACHED("no socket input stream"); return NS_ERROR_UNEXPECTED; @@ -547,7 +547,7 @@ nsHttpConnection::OnSocketWritable() } else if (NS_FAILED(mSocketOutCondition)) { if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) - rv = mSocketOut->AsyncWait(this, 0, nsnull); // continue writing + rv = mSocketOut->AsyncWait(this, 0, 0, nsnull); // continue writing else rv = mSocketOutCondition; again = PR_FALSE; @@ -561,7 +561,7 @@ nsHttpConnection::OnSocketWritable() // mTransaction->OnTransportStatus(nsISocketTransport::STATUS_WAITING_FOR, 0); - rv = mSocketIn->AsyncWait(this, 0, nsnull); // start reading + rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading again = PR_FALSE; } // write more to the socket until error or end-of-request... @@ -626,7 +626,7 @@ nsHttpConnection::OnSocketReadable() else if (NS_FAILED(mSocketInCondition)) { // continue waiting for the socket if necessary... if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK) - rv = mSocketIn->AsyncWait(this, 0, nsnull); + rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); else rv = mSocketInCondition; again = PR_FALSE; @@ -691,13 +691,13 @@ nsHttpConnection::SetupSSLProxyConnect() //----------------------------------------------------------------------------- NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnection, - nsIInputStreamNotify, - nsIOutputStreamNotify, + nsIInputStreamCallback, + nsIOutputStreamCallback, nsITransportEventSink, nsIInterfaceRequestor) //----------------------------------------------------------------------------- -// nsHttpConnection::nsIInputStreamNotify +// nsHttpConnection::nsIInputStreamCallback //----------------------------------------------------------------------------- // called on the socket transport thread @@ -721,7 +721,7 @@ nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in) } //----------------------------------------------------------------------------- -// nsHttpConnection::nsIOutputStreamNotify +// nsHttpConnection::nsIOutputStreamCallback //----------------------------------------------------------------------------- NS_IMETHODIMP diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnection.h b/mozilla/netwerk/protocol/http/src/nsHttpConnection.h index f03ed82e438..059e0abb20e 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnection.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnection.h @@ -34,7 +34,7 @@ #include "nsIStreamListener.h" #include "nsISocketTransport.h" -#include "nsIEventQueue.h" +#include "nsIEventTarget.h" #include "nsIAsyncInputStream.h" #include "nsIAsyncOutputStream.h" #include "nsIInterfaceRequestor.h" @@ -48,8 +48,8 @@ class nsHttpConnection : public nsAHttpSegmentReader , public nsAHttpSegmentWriter - , public nsIInputStreamNotify - , public nsIOutputStreamNotify + , public nsIInputStreamCallback + , public nsIOutputStreamCallback , public nsITransportEventSink , public nsIInterfaceRequestor { @@ -57,8 +57,8 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSAHTTPSEGMENTREADER NS_DECL_NSAHTTPSEGMENTWRITER - NS_DECL_NSIINPUTSTREAMNOTIFY - NS_DECL_NSIOUTPUTSTREAMNOTIFY + NS_DECL_NSIINPUTSTREAMCALLBACK + NS_DECL_NSIOUTPUTSTREAMCALLBACK NS_DECL_NSITRANSPORTEVENTSINK NS_DECL_NSIINTERFACEREQUESTOR diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp index 162d381d1bb..0308cf0fb22 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.cpp @@ -53,7 +53,8 @@ static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); //----------------------------------------------------------------------------- nsHttpConnectionMgr::nsHttpConnectionMgr() - : mMonitor(nsAutoMonitor::NewMonitor("nsHttpConnectionMgr")) + : mRef(0) + , mMonitor(nsAutoMonitor::NewMonitor("nsHttpConnectionMgr")) , mMaxConns(0) , mMaxConnsPerHost(0) , mMaxConnsPerProxy(0) @@ -87,7 +88,7 @@ nsHttpConnectionMgr::Init(PRUint16 maxConns, nsAutoMonitor mon(mMonitor); // do nothing if already initialized - if (mSTS) + if (mSTEventTarget) return NS_OK; // no need to do any special synchronization here since there cannot be @@ -101,7 +102,7 @@ nsHttpConnectionMgr::Init(PRUint16 maxConns, mMaxPipelinedRequests = maxPipelinedRequests; nsresult rv; - mSTS = do_GetService(kSocketTransportServiceCID, &rv); + mSTEventTarget = do_GetService(kSocketTransportServiceCID, &rv); return rv; } @@ -113,15 +114,15 @@ nsHttpConnectionMgr::Shutdown() nsAutoMonitor mon(mMonitor); // do nothing if already shutdown - if (!mSTS) + if (!mSTEventTarget) return NS_OK; - nsresult rv = mSTS->PostEvent(this, MSG_SHUTDOWN, 0, nsnull); + nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown); // release our reference to the STS to prevent further events // from being posted. this is how we indicate that we are // shutting down. - mSTS = 0; + mSTEventTarget = 0; if (NS_FAILED(rv)) { NS_WARNING("unable to post SHUTDOWN message\n"); @@ -134,13 +135,26 @@ nsHttpConnectionMgr::Shutdown() } nsresult -nsHttpConnectionMgr::PostEvent(PRUint32 type, PRUint32 uparam, void *vparam) +nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, nsresult status, void *param) { nsAutoMonitor mon(mMonitor); - NS_ENSURE_TRUE(mSTS, NS_ERROR_NOT_INITIALIZED); - - return mSTS->PostEvent(this, type, uparam, vparam); + nsresult rv; + if (!mSTEventTarget) { + NS_WARNING("cannot post event if not initialized"); + rv = NS_ERROR_NOT_INITIALIZED; + } + else { + PLEvent *event = new nsConnEvent(this, handler, status, param); + if (!event) + rv = NS_ERROR_OUT_OF_MEMORY; + else { + rv = mSTEventTarget->PostEvent(event); + if (NS_FAILED(rv)) + PL_DestroyEvent(event); + } + } + return rv; } //----------------------------------------------------------------------------- @@ -151,12 +165,9 @@ nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans) LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x]\n", trans)); NS_ADDREF(trans); - - nsresult rv = PostEvent(MSG_NEW_TRANSACTION, 0, trans); + nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, NS_OK, trans); if (NS_FAILED(rv)) NS_RELEASE(trans); - - // if successful, then OnSocketEvent will be called return rv; } @@ -166,27 +177,23 @@ nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason)); NS_ADDREF(trans); - - nsresult rv = PostEvent(MSG_CANCEL_TRANSACTION, reason, trans); + nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans); if (NS_FAILED(rv)) NS_RELEASE(trans); - - // if successful, then OnSocketEvent will be called return rv; } nsresult nsHttpConnectionMgr::PruneDeadConnections() { - return PostEvent(MSG_PRUNE_DEAD_CONNECTIONS, 0, nsnull); - // if successful, then OnSocketEvent will be called + return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections); } nsresult -nsHttpConnectionMgr::GetSTS(nsISocketTransportService **sts) +nsHttpConnectionMgr::GetSocketThreadEventTarget(nsIEventTarget **target) { nsAutoMonitor mon(mMonitor); - NS_IF_ADDREF(*sts = mSTS); + NS_IF_ADDREF(*target = mSTEventTarget); return NS_OK; } @@ -226,7 +233,7 @@ nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn) LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn)); NS_ADDREF(conn); - nsresult rv = PostEvent(MSG_RECLAIM_CONNECTION, 0, conn); + nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, NS_OK, conn); if (NS_FAILED(rv)) NS_RELEASE(conn); return rv; @@ -238,7 +245,7 @@ nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci) LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get())); NS_ADDREF(ci); - nsresult rv = PostEvent(MSG_PROCESS_PENDING_Q, 0, ci); + nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, NS_OK, ci); if (NS_FAILED(rv)) NS_RELEASE(ci); return rv; @@ -615,25 +622,9 @@ nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent, return PR_TRUE; } -//----------------------------------------------------------------------------- - -void -nsHttpConnectionMgr::OnMsgShutdown() -{ - LOG(("nsHttpConnectionMgr::OnMsgShutdown\n")); - - mCT.Reset(ShutdownPassCB, this); - - // signal shutdown complete - nsAutoMonitor mon(mMonitor); - mon.Notify(); -} - nsresult -nsHttpConnectionMgr::OnMsgNewTransaction(nsHttpTransaction *trans) +nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans) { - LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%x]\n", trans)); - // since "adds" and "cancels" are processed asynchronously and because // various events might trigger an "add" directly on the socket thread, // we must take care to avoid dispatching a transaction that has already @@ -703,11 +694,38 @@ nsHttpConnectionMgr::OnMsgNewTransaction(nsHttpTransaction *trans) return rv; } +//----------------------------------------------------------------------------- + void -nsHttpConnectionMgr::OnMsgCancelTransaction(nsHttpTransaction *trans, - nsresult reason) +nsHttpConnectionMgr::OnMsgShutdown(nsresult, void *) { - LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%x]\n", trans)); + LOG(("nsHttpConnectionMgr::OnMsgShutdown\n")); + + mCT.Reset(ShutdownPassCB, this); + + // signal shutdown complete + nsAutoMonitor mon(mMonitor); + mon.Notify(); +} + +void +nsHttpConnectionMgr::OnMsgNewTransaction(nsresult, void *param) +{ + LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param)); + + nsHttpTransaction *trans = (nsHttpTransaction *) param; + nsresult rv = ProcessNewTransaction(trans); + if (NS_FAILED(rv)) + trans->Close(rv); // for whatever its worth + NS_RELEASE(trans); +} + +void +nsHttpConnectionMgr::OnMsgCancelTransaction(nsresult reason, void *param) +{ + LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param)); + + nsHttpTransaction *trans = (nsHttpTransaction *) param; // // if the transaction owns a connection and the transaction is not done, // then ask the connection to close the transaction. otherwise, close the @@ -730,27 +748,30 @@ nsHttpConnectionMgr::OnMsgCancelTransaction(nsHttpTransaction *trans, } trans->Close(reason); } + NS_RELEASE(trans); } void -nsHttpConnectionMgr::OnMsgProcessPendingQ(nsHttpConnectionInfo *ci) +nsHttpConnectionMgr::OnMsgProcessPendingQ(nsresult, void *param) { + nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param; + LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get())); // start by processing the queue identified by the given connection info. nsCStringKey key(ci->HashKey()); nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key); - if (ent && ProcessPendingQForEntry(ent)) - return; + if (!(ent && ProcessPendingQForEntry(ent))) { + // if we reach here, it means that we couldn't dispatch a transaction + // for the specified connection info. walk the connection table... + mCT.Enumerate(ProcessOneTransactionCB, this); + } - // if we reach here, it means that we couldn't dispatch a transaction - // for the specified connection info. walk the connection table... - - mCT.Enumerate(ProcessOneTransactionCB, this); + NS_RELEASE(ci); } void -nsHttpConnectionMgr::OnMsgPruneDeadConnections() +nsHttpConnectionMgr::OnMsgPruneDeadConnections(nsresult, void *) { LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n")); @@ -759,9 +780,11 @@ nsHttpConnectionMgr::OnMsgPruneDeadConnections() } void -nsHttpConnectionMgr::OnMsgReclaimConnection(nsHttpConnection *conn) +nsHttpConnectionMgr::OnMsgReclaimConnection(nsresult, void *param) { - LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%x]\n", conn)); + LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param)); + + nsHttpConnection *conn = (nsHttpConnection *) param; // // 1) remove the connection from the active list @@ -791,63 +814,13 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(nsHttpConnection *conn) LOG((" connection cannot be reused; closing connection\n")); // make sure the connection is closed and release our reference. conn->Close(NS_ERROR_ABORT); - NS_RELEASE(conn); + nsHttpConnection *temp = conn; + NS_RELEASE(temp); } } - OnMsgProcessPendingQ(ci); - NS_RELEASE(ci); -} - -//----------------------------------------------------------------------------- - -NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsISocketEventHandler) - -// called on the socket thread -NS_IMETHODIMP -nsHttpConnectionMgr::OnSocketEvent(PRUint32 type, PRUint32 uparam, void *vparam) -{ - switch (type) { - case MSG_SHUTDOWN: - OnMsgShutdown(); - break; - case MSG_NEW_TRANSACTION: - { - nsHttpTransaction *trans = (nsHttpTransaction *) vparam; - - nsresult rv = OnMsgNewTransaction(trans); - if (NS_FAILED(rv)) - trans->Close(rv); // for whatever its worth - - NS_RELEASE(trans); - } - break; - case MSG_CANCEL_TRANSACTION: - { - nsHttpTransaction *trans = (nsHttpTransaction *) vparam; - OnMsgCancelTransaction(trans, (nsresult) uparam); - NS_RELEASE(trans); - } - break; - case MSG_PROCESS_PENDING_Q: - { - nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) vparam; - OnMsgProcessPendingQ(ci); - NS_RELEASE(ci); - } - break; - case MSG_PRUNE_DEAD_CONNECTIONS: - OnMsgPruneDeadConnections(); - break; - case MSG_RECLAIM_CONNECTION: - { - nsHttpConnection *conn = (nsHttpConnection *) vparam; - OnMsgReclaimConnection(conn); - NS_RELEASE(conn); - } - break; - } - return NS_OK; + OnMsgProcessPendingQ(NS_OK, ci); // releases |ci| + NS_RELEASE(conn); } //----------------------------------------------------------------------------- diff --git a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h index f962c2f4b54..7ade5bca086 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpConnectionMgr.h @@ -44,24 +44,20 @@ #include "nsHashtable.h" #include "prmon.h" -#include "nsISocketTransportService.h" +#include "nsIEventTarget.h" class nsHttpPipeline; //----------------------------------------------------------------------------- -class nsHttpConnectionMgr : public nsISocketEventHandler +class nsHttpConnectionMgr { public: - NS_DECL_ISUPPORTS - NS_DECL_NSISOCKETEVENTHANDLER - //------------------------------------------------------------------------- // NOTE: functions below may only be called on the main thread. //------------------------------------------------------------------------- nsHttpConnectionMgr(); - virtual ~nsHttpConnectionMgr(); // XXX cleanup the way we pass around prefs. nsresult Init(PRUint16 maxConnections, @@ -77,6 +73,19 @@ public: // NOTE: functions below may be called on any thread. //------------------------------------------------------------------------- + nsrefcnt AddRef() + { + return PR_AtomicIncrement(&mRef); + } + + nsrefcnt Release() + { + nsrefcnt n = PR_AtomicDecrement(&mRef); + if (n == 0) + delete this; + return n; + } + // adds a transaction to the list of managed transactions. nsresult AddTransaction(nsHttpTransaction *); @@ -89,7 +98,7 @@ public: // called to get a reference to the socket transport service. the socket // transport service is not available when the connection manager is down. - nsresult GetSTS(nsISocketTransportService **); + nsresult GetSocketThreadEventTarget(nsIEventTarget **); // called when a connection is done processing a transaction. if the // connection can be reused then it will be added to the idle list, else @@ -109,15 +118,7 @@ public: nsresult ProcessPendingQ(nsHttpConnectionInfo *); private: - - enum { - MSG_SHUTDOWN, - MSG_NEW_TRANSACTION, - MSG_CANCEL_TRANSACTION, - MSG_PROCESS_PENDING_Q, - MSG_PRUNE_DEAD_CONNECTIONS, - MSG_RECLAIM_CONNECTION - }; + virtual ~nsHttpConnectionMgr(); // nsConnectionEntry // @@ -165,8 +166,9 @@ private: // NOTE: these members may be accessed from any thread (use mMonitor) //------------------------------------------------------------------------- - PRMonitor *mMonitor; - nsCOMPtr mSTS; // cached reference to STS + PRInt32 mRef; + PRMonitor *mMonitor; + nsCOMPtr mSTEventTarget; // event target for socket thread // connection limits PRUint16 mMaxConns; @@ -192,14 +194,62 @@ private: nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *, PRUint8 caps, nsHttpConnection *); PRBool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **); - nsresult PostEvent(PRUint32 type, PRUint32 uparam, void *vparam); + nsresult ProcessNewTransaction(nsHttpTransaction *); - void OnMsgShutdown(); - nsresult OnMsgNewTransaction(nsHttpTransaction *); - void OnMsgCancelTransaction(nsHttpTransaction *trans, nsresult reason); - void OnMsgProcessPendingQ(nsHttpConnectionInfo *ci); - void OnMsgPruneDeadConnections(); - void OnMsgReclaimConnection(nsHttpConnection *); + // message handlers have this signature + typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(nsresult, void *); + + // nsConnEvent + // + // subclass of PLEvent used to marshall events to the socket transport + // thread. this class is used to implement PostEvent. + // + class nsConnEvent : public PLEvent + { + public: + nsConnEvent(nsHttpConnectionMgr *mgr, + nsConnEventHandler handler, + nsresult status, + void *param) + : mHandler(handler) + , mStatus(status) + , mParam(param) + { + NS_ADDREF(mgr); + PL_InitEvent(this, mgr, HandleEvent, DestroyEvent); + } + + PR_STATIC_CALLBACK(void*) HandleEvent(PLEvent *event) + { + nsHttpConnectionMgr *mgr = (nsHttpConnectionMgr *) event->owner; + nsConnEvent *self = (nsConnEvent *) event; + nsConnEventHandler handler = self->mHandler; + (mgr->*handler)(self->mStatus, self->mParam); + NS_RELEASE(mgr); + return nsnull; + } + PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *event) + { + delete (nsConnEvent *) event; + } + + private: + nsConnEventHandler mHandler; + nsresult mStatus; + void *mParam; + }; + + nsresult PostEvent(nsConnEventHandler handler, + nsresult status = NS_OK, + void *param = nsnull); + + // message handlers + void OnMsgShutdown (nsresult status, void *param); + void OnMsgNewTransaction (nsresult status, void *param); + void OnMsgCancelTransaction (nsresult status, void *param); + void OnMsgProcessPendingQ (nsresult status, void *param); + void OnMsgPruneDeadConnections (nsresult status, void *param); + void OnMsgReclaimConnection (nsresult status, void *param); // counters PRUint16 mNumActiveConns; diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp b/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp index e283d0c7e03..08d4a673f8e 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpHandler.cpp @@ -416,19 +416,6 @@ nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy, return NS_OK; } -nsresult -nsHttpHandler::GetProxyObjectManager(nsIProxyObjectManager **result) -{ - if (!mProxyMgr) { - nsresult rv; - mProxyMgr = do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv); - if (NS_FAILED(rv)) return rv; - } - *result = mProxyMgr; - NS_ADDREF(*result); - return NS_OK; -} - nsresult nsHttpHandler::GetEventQueueService(nsIEventQueueService **result) { diff --git a/mozilla/netwerk/protocol/http/src/nsHttpHandler.h b/mozilla/netwerk/protocol/http/src/nsHttpHandler.h index 12f64205631..6b1f4a49459 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpHandler.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpHandler.h @@ -140,11 +140,15 @@ public: return mConnMgr->ProcessPendingQ(cinfo); } + nsresult GetSocketThreadEventTarget(nsIEventTarget **target) + { + return mConnMgr->GetSocketThreadEventTarget(target); + } + // // The HTTP handler caches pointers to specific XPCOM services, and // provides the following helper routines for accessing those services: // - nsresult GetProxyObjectManager(nsIProxyObjectManager **); nsresult GetEventQueueService(nsIEventQueueService **); nsresult GetStreamConverterService(nsIStreamConverterService **); nsresult GetMimeService(nsIMIMEService **); @@ -183,7 +187,6 @@ private: // cached services nsCOMPtr mIOService; - nsCOMPtr mProxyMgr; nsCOMPtr mEventQueueService; nsCOMPtr mStreamConvSvc; nsCOMPtr mObserverService; diff --git a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp index 587df5de09b..436f4c5db15 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp +++ b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.cpp @@ -28,6 +28,7 @@ #include "nsHttpRequestHead.h" #include "nsHttpResponseHead.h" #include "nsHttpChunkedDecoder.h" +#include "nsTransportUtils.h" #include "nsIOService.h" #include "nsAutoLock.h" #include "pratom.h" @@ -107,10 +108,6 @@ nsHttpTransaction::nsHttpTransaction() , mContentRead(0) , mChunkedDecoder(nsnull) , mStatus(NS_OK) - , mLock(PR_NewLock()) - , mTransportStatus(0) - , mTransportProgress(0) - , mTransportProgressMax(0) , mRestartCount(0) , mCaps(0) , mClosed(PR_FALSE) @@ -138,8 +135,6 @@ nsHttpTransaction::~nsHttpTransaction() delete mResponseHead; delete mChunkedDecoder; - - PR_DestroyLock(mLock); } nsresult @@ -159,10 +154,15 @@ nsHttpTransaction::Init(PRUint8 caps, NS_ASSERTION(cinfo, "ouch"); NS_ASSERTION(requestHead, "ouch"); + NS_ASSERTION(queue, "ouch"); + + // create transport event sink proxy that coalesces all events + rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink), + eventsink, queue, PR_TRUE); + if (NS_FAILED(rv)) return rv; NS_ADDREF(mConnInfo = cinfo); mCallbacks = callbacks; - mTransportSink = eventsink; mConsumerEventQ = queue; mCaps = caps; @@ -267,50 +267,33 @@ nsHttpTransaction::OnTransportStatus(nsresult status, PRUint32 progress) { LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%u]\n", this, status, progress)); + + if (!mTransportSink) + return; NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); - PRBool postEvent; - { - nsAutoLock lock(mLock); + PRUint32 progressMax; - // stamp latest socket status - mTransportStatus = status; - - if (status == nsISocketTransport::STATUS_RECEIVING_FROM) { - // ignore the progress argument and use our own. as a result, - // the progress reported will not include the size of the response - // headers. this is OK b/c we only want to report the progress - // downloading the body of the response. - mTransportProgress = mContentRead; - mTransportProgressMax = mContentLength; - } - else if (status == nsISocketTransport::STATUS_SENDING_TO) { - // when uploading, we include the request headers in the progress - // notifications. - mTransportProgress = progress; - mTransportProgressMax = mRequestSize; - } - else { - mTransportProgress = 0; - mTransportProgressMax = 0; - } - - postEvent = !mStatusEventPending; - mStatusEventPending = PR_TRUE; + if (status == nsISocketTransport::STATUS_RECEIVING_FROM) { + // ignore the progress argument and use our own. as a result, + // the progress reported will not include the size of the response + // headers. this is OK b/c we only want to report the progress + // downloading the body of the response. + progress = mContentRead; + progressMax = mContentLength; + } + else if (status == nsISocketTransport::STATUS_SENDING_TO) { + // when uploading, we include the request headers in the progress + // notifications. + progressMax = mRequestSize; + } + else { + progress = 0; + progressMax = 0; } - // only post an event if there is not already an event in progress. we - // do this as an optimization to avoid an excessive number of status events. - if (postEvent) { - PLEvent *ev = new PLEvent; - NS_ADDREF_THIS(); - PL_InitEvent(ev, this, TransportStatus_Handler, TransportStatus_Cleanup); - if (mConsumerEventQ->PostEvent(ev) != PR_SUCCESS) { - NS_RELEASE_THIS(); - delete ev; - } - } + mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax); } PRBool @@ -421,9 +404,18 @@ nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer, mWriter = nsnull; - // if pipe would block then we need to AsyncWait on it. - if (rv == NS_BASE_STREAM_WOULD_BLOCK) - mPipeOut->AsyncWait(this, 0, nsnull); + // if pipe would block then we need to AsyncWait on it. have callback + // occur on socket thread so we stay synchronized. + if (rv == NS_BASE_STREAM_WOULD_BLOCK) { + nsCOMPtr target; + gHttpHandler->GetSocketThreadEventTarget(getter_AddRefs(target)); + if (target) + mPipeOut->AsyncWait(this, 0, 0, target); + else { + NS_ERROR("no socket thread event target"); + rv = NS_ERROR_UNEXPECTED; + } + } return rv; } @@ -503,7 +495,7 @@ nsHttpTransaction::Close(nsresult reason) } // closing this pipe signals triggers the channel's OnStopRequest method. - mPipeOut->CloseEx(reason); + mPipeOut->CloseWithStatus(reason); } //----------------------------------------------------------------------------- @@ -871,41 +863,7 @@ nsHttpTransaction::ProcessData(char *buf, PRUint32 count, PRUint32 *countRead) } //----------------------------------------------------------------------------- -// nsHttpTransaction events -//----------------------------------------------------------------------------- - -void *PR_CALLBACK -nsHttpTransaction::TransportStatus_Handler(PLEvent *ev) -{ - nsHttpTransaction *trans = - NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev)); - - LOG(("nsHttpTransaction::SocketStatus_Handler [trans=%x]\n", trans)); - - nsresult status; - PRUint32 progress, progressMax; - { - nsAutoLock lock(trans->mLock); - - status = trans->mTransportStatus; - progress = trans->mTransportProgress; - progressMax = trans->mTransportProgressMax; - - trans->mStatusEventPending = PR_FALSE; - } - - trans->mTransportSink->OnTransportStatus(nsnull, status, progress, progressMax); - - NS_RELEASE(trans); - return nsnull; -} - -void PR_CALLBACK -nsHttpTransaction::TransportStatus_Cleanup(PLEvent *ev) -{ - delete ev; -} - +// nsHttpTransaction deletion event //----------------------------------------------------------------------------- void @@ -937,8 +895,9 @@ nsHttpTransaction::DeleteSelfOnConsumerThread() PL_InitEvent(event, this, DeleteThis_Handler, DeleteThis_Cleanup); - PRStatus status = mConsumerEventQ->PostEvent(event); - NS_ASSERTION(status == PR_SUCCESS, "PostEvent failed"); + nsresult status = mConsumerEventQ->PostEvent(event); + if (NS_FAILED(status)) + NS_ERROR("PostEvent failed"); } } @@ -983,33 +942,15 @@ nsHttpTransaction::Release() return count; } -NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHttpTransaction, - nsIOutputStreamNotify, - nsISocketEventHandler) +NS_IMPL_THREADSAFE_QUERY_INTERFACE1(nsHttpTransaction, nsIOutputStreamCallback) //----------------------------------------------------------------------------- -// nsHttpTransaction::nsIOutputStreamNotify +// nsHttpTransaction::nsIOutputStreamCallback //----------------------------------------------------------------------------- +// called on the socket thread NS_IMETHODIMP nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out) -{ - // proxy this event to the socket thread - - nsCOMPtr sts; - gHttpHandler->ConnMgr()->GetSTS(getter_AddRefs(sts)); - if (sts) - sts->PostEvent(this, 0, 0, nsnull); // only one type of event so far - - return NS_OK; -} - -//----------------------------------------------------------------------------- -// nsHttpTransaction::nsISocketEventHandler -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -nsHttpTransaction::OnSocketEvent(PRUint32 type, PRUint32 param1, void *param2) { if (mConnection) { nsresult rv = mConnection->ResumeRecv(); diff --git a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h index 8e928770b45..a7642ca7021 100644 --- a/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h +++ b/mozilla/netwerk/protocol/http/src/nsHttpTransaction.h @@ -51,14 +51,12 @@ class nsHttpChunkedDecoder; //----------------------------------------------------------------------------- class nsHttpTransaction : public nsAHttpTransaction - , public nsIOutputStreamNotify - , public nsISocketEventHandler + , public nsIOutputStreamCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSAHTTPTRANSACTION - NS_DECL_NSIOUTPUTSTREAMNOTIFY - NS_DECL_NSISOCKETEVENTHANDLER + NS_DECL_NSIOUTPUTSTREAMCALLBACK nsHttpTransaction(); virtual ~nsHttpTransaction(); @@ -90,7 +88,7 @@ public: nsHttpRequestHead *reqHeaders, nsIInputStream *reqBody, PRBool reqBodyIncludesHeaders, - nsIEventQueue *eventQ, + nsIEventQueue *consumerEventQ, nsIInterfaceRequestor *callbacks, nsITransportEventSink *eventsink, nsIAsyncInputStream **responseBody); @@ -101,7 +99,6 @@ public: nsHttpRequestHead *RequestHead() { return mRequestHead; } nsHttpResponseHead *ResponseHead() { return mHaveAllHeaders ? mResponseHead : nsnull; } nsISupports *SecurityInfo() { return mSecurityInfo; } - nsresult TransportStatus(){ return mTransportStatus; } nsIInterfaceRequestor *Callbacks() { return mCallbacks; } nsIEventQueue *ConsumerEventQ() { return mConsumerEventQ; } @@ -163,15 +160,6 @@ private: nsresult mStatus; - // this lock is used to protect access to members which may be accessed - // from both the main thread as well as the socket thread. - PRLock *mLock; - - // these transport status fields are protected by mLock - nsresult mTransportStatus; - PRUint32 mTransportProgress; - PRUint32 mTransportProgressMax; - PRUint16 mRestartCount; // the number of times this transaction has been restarted PRUint8 mCaps; diff --git a/mozilla/netwerk/test/Makefile.in b/mozilla/netwerk/test/Makefile.in index c564c4ccb3b..c4d3b005ab9 100644 --- a/mozilla/netwerk/test/Makefile.in +++ b/mozilla/netwerk/test/Makefile.in @@ -34,23 +34,25 @@ REQUIRES = xpcom \ util \ $(NULL) -CPPSRCS = PropertiesTest.cpp \ - urltest.cpp \ - TestCallbacks.cpp \ - TestPageLoad.cpp \ - TestPerf.cpp \ - TestIDN.cpp \ - TestURLParser.cpp \ - TestStandardURL.cpp \ - TestSocketTransport.cpp \ - TestUpload.cpp \ - TestStreamTransport.cpp \ - TestStreamChannel.cpp \ - TestStreamPump.cpp \ - TestProtocols.cpp \ - TestBlockingSocket.cpp \ - TestDNS.cpp \ - $(NULL) +CPPSRCS = \ + PropertiesTest.cpp \ + urltest.cpp \ + TestCallbacks.cpp \ + TestPageLoad.cpp \ + TestPerf.cpp \ + TestIDN.cpp \ + TestURLParser.cpp \ + TestStandardURL.cpp \ + TestSocketTransport.cpp \ + TestUpload.cpp \ + TestStreamTransport.cpp \ + TestStreamChannel.cpp \ + TestStreamPump.cpp \ + TestProtocols.cpp \ + TestBlockingSocket.cpp \ + TestDNS.cpp \ + TestIOThreads.cpp \ + $(NULL) SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=$(BIN_SUFFIX)) diff --git a/mozilla/netwerk/test/TestIOThreads.cpp b/mozilla/netwerk/test/TestIOThreads.cpp new file mode 100644 index 00000000000..e990d2c458c --- /dev/null +++ b/mozilla/netwerk/test/TestIOThreads.cpp @@ -0,0 +1,101 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIServiceManager.h" +#include "nsIEventTarget.h" +#include "nsCOMPtr.h" +#include "plevent.h" +#include "prlog.h" + +#if defined(PR_LOGGING) +// +// set NSPR_LOG_MODULES=Test:5 +// +static PRLogModuleInfo *gTestLog = nsnull; +#endif +#define LOG(args) PR_LOG(gTestLog, PR_LOG_DEBUG, args) + +PR_STATIC_CALLBACK(void *) HandleEvent(PLEvent *event) +{ + LOG(("HandleEvent:%d\n", (int) event->owner)); + return nsnull; +} + +PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent *event) +{ + delete event; +} + +static nsresult RunTest() +{ + nsresult rv; + nsCOMPtr target = do_GetService("@mozilla.org/network/io-thread-pool;1", &rv); + if (NS_FAILED(rv)) + return rv; + + for (int i=0; i<10; ++i) { + PLEvent *event = new PLEvent(); + PL_InitEvent(event, (void *) i, HandleEvent, DestroyEvent); + LOG(("PostEvent:%d\n", i)); + if (NS_FAILED(target->PostEvent(event))) + PL_DestroyEvent(event); + } + + return rv; +} + +int main() +{ + nsresult rv; + +#if defined(PR_LOGGING) + gTestLog = PR_NewLogModule("Test"); +#endif + + rv = NS_InitXPCOM2(nsnull, nsnull, nsnull); + if (NS_FAILED(rv)) + return rv; + + rv = RunTest(); + if (NS_FAILED(rv)) + LOG(("RunTest failed [rv=%x]\n", rv)); + + LOG(("sleeping main thread for 2 seconds...\n")); + PR_Sleep(PR_SecondsToInterval(2)); + + NS_ShutdownXPCOM(nsnull); + return 0; +} diff --git a/mozilla/netwerk/test/TestSocketTransport.cpp b/mozilla/netwerk/test/TestSocketTransport.cpp index ec83145b724..7640d45e506 100644 --- a/mozilla/netwerk/test/TestSocketTransport.cpp +++ b/mozilla/netwerk/test/TestSocketTransport.cpp @@ -108,8 +108,8 @@ PostDoneEvent() //////////////////////////////////////////////////////////////////////////////// -class MyHandler : public nsIOutputStreamNotify - , public nsIInputStreamNotify +class MyHandler : public nsIOutputStreamCallback + , public nsIInputStreamCallback { public: NS_DECL_ISUPPORTS @@ -142,14 +142,14 @@ public: if (NS_FAILED(rv) || (n == 0)) { if (rv != NS_BASE_STREAM_WOULD_BLOCK) { LOG((" done writing; starting to read\n")); - mInput->AsyncWait(this, 0, nsnull); + mInput->AsyncWait(this, 0, 0, nsnull); return NS_OK; } } mWriteOffset += n; - return out->AsyncWait(this, 0, nsnull); + return out->AsyncWait(this, 0, 0, nsnull); } // called on any thread @@ -172,7 +172,7 @@ public: } } - return in->AsyncWait(this, 0, nsnull); + return in->AsyncWait(this, 0, 0, nsnull); } private: @@ -183,8 +183,8 @@ private: }; NS_IMPL_THREADSAFE_ISUPPORTS2(MyHandler, - nsIOutputStreamNotify, - nsIInputStreamNotify) + nsIOutputStreamCallback, + nsIInputStreamCallback) //////////////////////////////////////////////////////////////////////////////// @@ -257,7 +257,7 @@ RunTest(nsISocketTransportService *sts, return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(handler); - rv = asyncOut->AsyncWait(handler, 0, nsnull); + rv = asyncOut->AsyncWait(handler, 0, 0, nsnull); if (NS_SUCCEEDED(rv)) { PLEvent* event; diff --git a/mozilla/netwerk/test/TestStreamTransport.cpp b/mozilla/netwerk/test/TestStreamTransport.cpp index 189cb9cce1f..d2d87328fac 100644 --- a/mozilla/netwerk/test/TestStreamTransport.cpp +++ b/mozilla/netwerk/test/TestStreamTransport.cpp @@ -109,8 +109,8 @@ PostDoneEvent() #define CHUNK_SIZE 500 -class MyCopier : public nsIInputStreamNotify - , public nsIOutputStreamNotify +class MyCopier : public nsIInputStreamCallback + , public nsIOutputStreamCallback { public: NS_DECL_ISUPPORTS @@ -173,9 +173,9 @@ public: nsresult rv = mOutput->WriteSegments(FillOutputBuffer, this, CHUNK_SIZE, &n); if (NS_FAILED(rv) || (n == 0)) { if (rv == NS_BASE_STREAM_WOULD_BLOCK) - mOutput->AsyncWait(this, 0, nsnull); + mOutput->AsyncWait(this, 0, 0, nsnull); else if (mInputCondition == NS_BASE_STREAM_WOULD_BLOCK) - mInput->AsyncWait(this, 0, nsnull); + mInput->AsyncWait(this, 0, 0, nsnull); else Close_Locked(); break; @@ -202,7 +202,7 @@ public: mInput = do_QueryInterface(inStr); mOutput = do_QueryInterface(outStr); - return mInput->AsyncWait(this, 0, nsnull); + return mInput->AsyncWait(this, 0, 0, nsnull); } static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr, @@ -231,8 +231,8 @@ protected: }; NS_IMPL_THREADSAFE_ISUPPORTS2(MyCopier, - nsIInputStreamNotify, - nsIOutputStreamNotify) + nsIInputStreamCallback, + nsIOutputStreamCallback) //////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/parser/htmlparser/src/nsParser.cpp b/mozilla/parser/htmlparser/src/nsParser.cpp index eb0bbf02662..76f5eb14412 100644 --- a/mozilla/parser/htmlparser/src/nsParser.cpp +++ b/mozilla/parser/htmlparser/src/nsParser.cpp @@ -219,41 +219,31 @@ For more details @see bugzilla bug 76722 struct nsParserContinueEvent : public PLEvent { - nsParserContinueEvent(nsIParser* aParser); - ~nsParserContinueEvent() { } + nsParserContinueEvent(nsParser* aParser) + { + NS_ADDREF(aParser); + PL_InitEvent(this, aParser, HandleEvent, DestroyEvent); + } - void HandleEvent() { - if (mParser) { - nsParser* parser = NS_STATIC_CAST(nsParser*, mParser); - parser->HandleParserContinueEvent(); - NS_RELEASE(mParser); - } - }; - - nsIParser* mParser; + ~nsParserContinueEvent() + { + nsParser *parser = (nsParser*) owner; + NS_RELEASE(parser); + } + + PR_STATIC_CALLBACK(void*) HandleEvent(PLEvent* aEvent) + { + nsParser *parser = (nsParser*) aEvent->owner; + parser->HandleParserContinueEvent(); + return nsnull; + } + + PR_STATIC_CALLBACK(void) DestroyEvent(PLEvent* aEvent) + { + delete (nsParserContinueEvent*) aEvent; + } }; -static void PR_CALLBACK HandlePLEvent(nsParserContinueEvent* aEvent) -{ - NS_ASSERTION(nsnull != aEvent,"Event is null"); - aEvent->HandleEvent(); -} - -static void PR_CALLBACK DestroyPLEvent(nsParserContinueEvent* aEvent) -{ - NS_ASSERTION(nsnull != aEvent,"Event is null"); - delete aEvent; -} - -nsParserContinueEvent::nsParserContinueEvent(nsIParser* aParser) -{ - NS_ASSERTION(aParser, "null parameter"); - mParser = aParser; - PL_InitEvent(this, aParser, - (PLHandleEventProc) ::HandlePLEvent, - (PLDestroyEventProc) ::DestroyPLEvent); -} - //-------------- End ParseContinue Event Definition ------------------------ @@ -417,11 +407,14 @@ nsresult nsParser::PostContinueEvent() { if (!(mFlags & NS_PARSER_FLAG_PENDING_CONTINUE_EVENT) && mEventQueue) { - nsParserContinueEvent* ev = new nsParserContinueEvent(NS_STATIC_CAST(nsIParser*, this)); - NS_ENSURE_TRUE(ev,NS_ERROR_OUT_OF_MEMORY); - NS_ADDREF(this); - mEventQueue->PostEvent(ev); - mFlags |= NS_PARSER_FLAG_PENDING_CONTINUE_EVENT; + nsParserContinueEvent* ev = new nsParserContinueEvent(this); + NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY); + if (NS_FAILED(mEventQueue->PostEvent(ev))) { + NS_ERROR("failed to post parser continuation event"); + PL_DestroyEvent(ev); + } + else + mFlags |= NS_PARSER_FLAG_PENDING_CONTINUE_EVENT; } return NS_OK; } diff --git a/mozilla/xpcom/build/nsXPComInit.cpp b/mozilla/xpcom/build/nsXPComInit.cpp index 36921a809f6..ab7b5756eb0 100644 --- a/mozilla/xpcom/build/nsXPComInit.cpp +++ b/mozilla/xpcom/build/nsXPComInit.cpp @@ -307,7 +307,6 @@ static const nsModuleComponentInfo components[] = { COMPONENT(EVENTQUEUESERVICE, nsEventQueueServiceImplConstructor), COMPONENT(EVENTQUEUE, nsEventQueueImpl::Create), COMPONENT(THREAD, nsThread::Create), - COMPONENT(THREADPOOL, nsThreadPool::Create), #define NS_XPCOMPROXY_CID NS_PROXYEVENT_MANAGER_CID COMPONENT(XPCOMPROXY, nsProxyObjectManager::Create), diff --git a/mozilla/xpcom/io/nsIAsyncInputStream.idl b/mozilla/xpcom/io/nsIAsyncInputStream.idl index 7b1f7b62a56..6ce0bb30fff 100644 --- a/mozilla/xpcom/io/nsIAsyncInputStream.idl +++ b/mozilla/xpcom/io/nsIAsyncInputStream.idl @@ -37,44 +37,100 @@ #include "nsIInputStream.idl" -interface nsIInputStreamNotify; -interface nsIEventQueue; +interface nsIInputStreamCallback; +interface nsIEventTarget; -[scriptable, uuid(cc911e09-2a02-4d2d-ba4b-b2afb173e96d)] +/** + * If an input stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK + * when read. The caller must then wait for the stream to have some data to + * read. If the stream implements nsIAsyncInputStream, then the caller can use + * this interface to request an asynchronous notification when the stream + * becomes readable or closed (via the AsyncWait method). + * + * While this interface is almost exclusively used with non-blocking streams, it + * is not necessary that nsIInputStream::isNonBlocking return true. Nor is it + * necessary that a non-blocking nsIInputStream implementation also implement + * nsIAsyncInputStream. + */ +[scriptable, uuid(15a15329-00de-44e8-ab06-0d0b0d43dc5b)] interface nsIAsyncInputStream : nsIInputStream { /** - * This function extends nsIInputStream::close, allowing the caller to - * specify the reason for closing the stream. Any future attempts to - * access the stream (e.g., nsIInputStream::read) will result in an - * exception with the value given by |reason|. + * This method closes the stream and sets its internal status. If the + * stream is already closed, then this method is ignored. Once the stream + * is closed, the stream's status cannot be changed. Any successful status + * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which + * has an effect equivalent to nsIInputStream::close. * - * close() is equivalent to closeEx(NS_BASE_STREAM_CLOSED). + * NOTE: this method exists in part to support pipes, which have both an + * input end and an output end. If the input end of a pipe is closed, then + * writes to the output end of the pipe will fail. The error code returned + * when an attempt is made to write to a "broken" pipe corresponds to the + * status code passed in when the input end of the pipe was closed, which + * greatly simplifies working with pipes in some cases. * - * Any pending asyncWait will be notified. + * @param aStatus + * The error that will be reported if this stream is accessed after + * it has been closed. */ - void closeEx(in nsresult reason); + void closeWithStatus(in nsresult aStatus); /** - * Asynchronously wait for the stream to be readable or closed. + * Asynchronously wait for the stream to be readable or closed. The + * notification is one-shot, meaning that each asyncWait call will result + * in exactly one notification callback. After the OnInputStreamReady event + * is dispatched, the stream releases its reference to the + * nsIInputStreamCallback object. It is safe to call asyncWait again from the + * notification handler. + * + * This method may be called at any time (even if read has not been called). + * In other words, this method may be called when the stream already has + * data to read. It may also be called when the stream is closed. If the + * stream is already readable or closed when AsyncWait is called, then the + * OnInputStreamReady event will be dispatched immediately. Otherwise, the + * event will be dispatched when the stream becomes readable or closed. * - * @param aNotify + * @param aCallback * This object is notified when the stream becomes ready. + * @param aFlags + * This parameter specifies optional flags passed in to configure + * the behavior of this method. Pass zero to specify no flags. * @param aRequestedCount - * Wait until at least this many bytes may be read. This is only - * a suggestion to the underlying stream; it may be ignored. - * @param aEventQ - * Specify NULL to receive notification on ANY thread, or specify - * that notification be delivered to a specific event queue. Caller - * must pass a "resolved" event queue (see nsIEventQueueService). + * Wait until at least this many bytes can be read. This is only + * a suggestion to the underlying stream; it may be ignored. The + * caller may pass zero to indicate no preference. + * @param aEventTarget + * Specify NULL to receive notification on ANY thread (possibly even + * recursively on the calling thread -- i.e., synchronously), or + * specify that the notification be delivered to a specific event + * target. */ - void asyncWait(in nsIInputStreamNotify aNotify, + void asyncWait(in nsIInputStreamCallback aCallback, + in unsigned long aFlags, in unsigned long aRequestedCount, - in nsIEventQueue aEventQ); + in nsIEventTarget aEventTarget); + + /** + * If passed to asyncWait, this flag overrides the default behavior, + * causing the OnInputStreamReady notification to be suppressed until the + * stream becomes closed (either as a result of closeWithStatus/close being + * called on the stream or possibly due to some error in the underlying + * stream). + */ + const unsigned long WAIT_CLOSURE_ONLY = (1<<0); }; -[scriptable, uuid(20c665a3-0ebf-4ebb-97ba-794248bfa8fe)] -interface nsIInputStreamNotify : nsISupports +/** + * This is a companion interface for nsIAsyncInputStream::asyncWait. + */ +[scriptable, uuid(d1f28e94-3a6e-4050-a5f5-2e81b1fc2a43)] +interface nsIInputStreamCallback : nsISupports { + /** + * Called to indicate that the stream is either readable or closed. + * + * @param aStream + * The stream whose asyncWait method was called. + */ void onInputStreamReady(in nsIAsyncInputStream aStream); }; diff --git a/mozilla/xpcom/io/nsIAsyncOutputStream.idl b/mozilla/xpcom/io/nsIAsyncOutputStream.idl index c579a65cc4d..b0dd9ec0323 100644 --- a/mozilla/xpcom/io/nsIAsyncOutputStream.idl +++ b/mozilla/xpcom/io/nsIAsyncOutputStream.idl @@ -37,44 +37,100 @@ #include "nsIOutputStream.idl" -interface nsIOutputStreamNotify; -interface nsIEventQueue; +interface nsIOutputStreamCallback; +interface nsIEventTarget; -[scriptable, uuid(04ba1b53-fad0-4033-91c9-3e24db22079c)] +/** + * If an output stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK + * when written to. The caller must then wait for the stream to become + * writable. If the stream implements nsIAsyncOutputStream, then the caller can + * use this interface to request an asynchronous notification when the stream + * becomes writable or closed (via the AsyncWait method). + * + * While this interface is almost exclusively used with non-blocking streams, it + * is not necessary that nsIOutputStream::isNonBlocking return true. Nor is it + * necessary that a non-blocking nsIOutputStream implementation also implement + * nsIAsyncOutputStream. + */ +[scriptable, uuid(10dc9c94-8aff-49c6-8af9-d7fdb7339dae)] interface nsIAsyncOutputStream : nsIOutputStream { /** - * This function extends nsIOutputStream::close, allowing the caller to - * specify the reason for closing the stream. Any future attempts to - * access the stream (e.g., nsIInputStream::write) will result in an - * exception with the value given by |reason|. + * This method closes the stream and sets its internal status. If the + * stream is already closed, then this method is ignored. Once the stream + * is closed, the stream's status cannot be changed. Any successful status + * code passed to this method is treated as NS_BASE_STREAM_CLOSED, which + * is equivalent to nsIInputStream::close. * - * close() is equivalent to closeEx(NS_BASE_STREAM_CLOSED). + * NOTE: this method exists in part to support pipes, which have both an + * input end and an output end. If the output end of a pipe is closed, then + * reads from the input end of the pipe will fail. The error code returned + * when an attempt is made to read from a "closed" pipe corresponds to the + * status code passed in when the output end of the pipe is closed, which + * greatly simplifies working with pipes in some cases. * - * Any pending asyncWait will be notified. + * @param aStatus + * The error that will be reported if this stream is accessed after + * it has been closed. */ - void closeEx(in nsresult reason); + void closeWithStatus(in nsresult reason); /** - * Asynchronously wait for the stream to be writable or closed. + * Asynchronously wait for the stream to be writable or closed. The + * notification is one-shot, meaning that each asyncWait call will result + * in exactly one notification callback. After the OnOutputStreamReady event + * is dispatched, the stream releases its reference to the + * nsIOutputStreamCallback object. It is safe to call asyncWait again from the + * notification handler. + * + * This method may be called at any time (even if write has not been called). + * In other words, this method may be called when the stream already has + * room for more data. It may also be called when the stream is closed. If + * the stream is already writable or closed when AsyncWait is called, then the + * OnOutputStreamReady event will be dispatched immediately. Otherwise, the + * event will be dispatched when the stream becomes writable or closed. * - * @param aNotify + * @param aCallback * This object is notified when the stream becomes ready. + * @param aFlags + * This parameter specifies optional flags passed in to configure + * the behavior of this method. Pass zero to specify no flags. * @param aRequestedCount - * Wait until at least this many bytes may be written. This is - * only a suggestion to the underlying stream; it may be ignored. - * @param aEventQ - * Specify NULL to receive notification on ANY thread, or specify - * that notification be delivered to a specific event queue. Caller - * must pass a "resolved" event queue (see nsIEventQueueService). + * Wait until at least this many bytes can be written. This is only + * a suggestion to the underlying stream; it may be ignored. The + * caller may pass zero to indicate no preference. + * @param aEventTarget + * Specify NULL to receive notification on ANY thread (possibly even + * recursively on the calling thread -- i.e., synchronously), or + * specify that the notification be delivered to a specific event + * target. */ - void asyncWait(in nsIOutputStreamNotify aNotify, + void asyncWait(in nsIOutputStreamCallback aCallback, + in unsigned long aFlags, in unsigned long aRequestedCount, - in nsIEventQueue aEventQ); + in nsIEventTarget aEventTarget); + + /** + * If passed to asyncWait, this flag overrides the default behavior, + * causing the OnOutputStreamReady notification to be suppressed until the + * stream becomes closed (either as a result of closeWithStatus/close being + * called on the stream or possibly due to some error in the underlying + * stream). + */ + const unsigned long WAIT_CLOSURE_ONLY = (1<<0); }; -[scriptable, uuid(614def11-1d77-409b-8153-ea3ae5babc3c)] -interface nsIOutputStreamNotify : nsISupports +/** + * This is a companion interface for nsIAsyncOutputStream::asyncWait. + */ +[scriptable, uuid(40dbcdff-9053-42c5-a57c-3ec910d0f148)] +interface nsIOutputStreamCallback : nsISupports { + /** + * Called to indicate that the stream is either writable or closed. + * + * @param aStream + * The stream whose asyncWait method was called. + */ void onOutputStreamReady(in nsIAsyncOutputStream aStream); }; diff --git a/mozilla/xpcom/io/nsPipe3.cpp b/mozilla/xpcom/io/nsPipe3.cpp index 4c172c8f5c7..94db7765bc3 100644 --- a/mozilla/xpcom/io/nsPipe3.cpp +++ b/mozilla/xpcom/io/nsPipe3.cpp @@ -36,7 +36,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIPipe.h" -#include "nsIEventQueue.h" +#include "nsIEventTarget.h" #include "nsISeekableStream.h" #include "nsSegmentedBuffer.h" #include "nsStreamUtils.h" @@ -65,6 +65,9 @@ class nsPipeOutputStream; //----------------------------------------------------------------------------- +// this class is used to delay notifications until the end of a particular +// scope. it helps avoid the complexity of issuing callbacks while inside +// a critical section. class nsPipeEvents { public: @@ -72,36 +75,43 @@ public: ~nsPipeEvents(); inline void NotifyInputReady(nsIAsyncInputStream *stream, - nsIInputStreamNotify *notify) + nsIInputStreamCallback *callback) { - NS_ASSERTION(!mInputNotify, "already have an input event"); + NS_ASSERTION(!mInputCallback, "already have an input event"); mInputStream = stream; - mInputNotify = notify; + mInputCallback = callback; } inline void NotifyOutputReady(nsIAsyncOutputStream *stream, - nsIOutputStreamNotify *notify) + nsIOutputStreamCallback *callback) { - NS_ASSERTION(!mOutputNotify, "already have an output event"); + NS_ASSERTION(!mOutputCallback, "already have an output event"); mOutputStream = stream; - mOutputNotify = notify; + mOutputCallback = callback; } private: - nsCOMPtr mInputStream; - nsCOMPtr mInputNotify; - nsCOMPtr mOutputStream; - nsCOMPtr mOutputNotify; + nsCOMPtr mInputStream; + nsCOMPtr mInputCallback; + nsCOMPtr mOutputStream; + nsCOMPtr mOutputCallback; }; //----------------------------------------------------------------------------- +// the input end of a pipe (allocated as a member of the pipe). class nsPipeInputStream : public nsIAsyncInputStream , public nsISeekableStream , public nsISearchableInputStream { public: + // since this class will be allocated as a member of the pipe, we do not + // need our own ref count. instead, we share the lifetime (the ref count) + // of the entire pipe. this macro is just convenience since it does not + // declare a mRefCount variable; however, don't let the name fool you... + // we are not inheriting from nsPipe ;-) NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIINPUTSTREAM NS_DECL_NSIASYNCINPUTSTREAM NS_DECL_NSISEEKABLESTREAM @@ -114,6 +124,7 @@ public: , mBlocking(PR_TRUE) , mBlocked(PR_FALSE) , mAvailable(0) + , mCallbackFlags(0) { } virtual ~nsPipeInputStream() { } @@ -123,7 +134,11 @@ public: PRUint32 Available() { return mAvailable; } void ReduceAvailable(PRUint32 avail) { mAvailable -= avail; } + // synchronously wait for the pipe to become readable. nsresult Wait(); + + // these functions return true to indicate that the pipe's monitor should + // be notified, to wake up a blocked reader if any. PRBool OnInputReadable(PRUint32 bytesWritten, nsPipeEvents &); PRBool OnInputException(nsresult, nsPipeEvents &); @@ -138,16 +153,24 @@ private: // these variables can only be accessed while inside the pipe's monitor PRPackedBool mBlocked; PRUint32 mAvailable; - nsCOMPtr mNotify; + nsCOMPtr mCallback; + PRUint32 mCallbackFlags; }; //----------------------------------------------------------------------------- +// the output end of a pipe (allocated as a member of the pipe). class nsPipeOutputStream : public nsIAsyncOutputStream , public nsISeekableStream { public: + // since this class will be allocated as a member of the pipe, we do not + // need our own ref count. instead, we share the lifetime (the ref count) + // of the entire pipe. this macro is just convenience since it does not + // declare a mRefCount variable; however, don't let the name fool you... + // we are not inheriting from nsPipe ;-) NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIOUTPUTSTREAM NS_DECL_NSIASYNCOUTPUTSTREAM NS_DECL_NSISEEKABLESTREAM @@ -159,13 +182,18 @@ public: , mBlocking(PR_TRUE) , mBlocked(PR_FALSE) , mWritable(PR_TRUE) + , mCallbackFlags(0) { } virtual ~nsPipeOutputStream() { } void SetNonBlocking(PRBool aNonBlocking) { mBlocking = !aNonBlocking; } void SetWritable(PRBool writable) { mWritable = writable; } + // synchronously wait for the pipe to become writable. nsresult Wait(); + + // these functions return true to indicate that the pipe's monitor should + // be notified, to wake up a blocked writer if any. PRBool OnOutputWritable(nsPipeEvents &); PRBool OnOutputException(nsresult, nsPipeEvents &); @@ -180,7 +208,8 @@ private: // these variables can only be accessed while inside the pipe's monitor PRPackedBool mBlocked; PRPackedBool mWritable; - nsCOMPtr mNotify; + nsCOMPtr mCallback; + PRUint32 mCallbackFlags; }; //----------------------------------------------------------------------------- @@ -198,10 +227,6 @@ public: nsPipe(); virtual ~nsPipe(); - PRMonitor *Monitor() { return mMonitor; } - nsPipeInputStream* InputStream() { return &mInput; } - nsPipeOutputStream* OutputStream() { return &mOutput; } - // // methods below may only be called while inside the pipe's monitor // @@ -330,8 +355,8 @@ nsPipe::Init(PRBool nonBlockingIn, if (NS_FAILED(rv)) return rv; - InputStream()->SetNonBlocking(nonBlockingIn); - OutputStream()->SetNonBlocking(nonBlockingOut); + mInput.SetNonBlocking(nonBlockingIn); + mOutput.SetNonBlocking(nonBlockingOut); return NS_OK; } @@ -399,7 +424,7 @@ nsPipe::AdvanceReadCursor(PRUint32 bytesRead) mReadCursor += bytesRead; NS_ASSERTION(mReadCursor <= mReadLimit, "read cursor exceeds limit"); - InputStream()->ReduceAvailable(bytesRead); + mInput.ReduceAvailable(bytesRead); if (mReadCursor == mReadLimit) { // we've reached the limit of how much we can read from this segment. @@ -437,7 +462,7 @@ nsPipe::AdvanceReadCursor(PRUint32 bytesRead) // we've free'd up a segment, so notify output stream that pipe has // room for a new segment. - if (OutputStream()->OnOutputWritable(events)) + if (mOutput.OnOutputWritable(events)) mon.Notify(); } } @@ -507,11 +532,11 @@ nsPipe::AdvanceWriteCursor(PRUint32 bytesWritten) // update the writable flag on the output stream if (mWriteCursor == mWriteLimit) { if (mBuffer.GetSize() >= mBuffer.GetMaxSize()) - OutputStream()->SetWritable(PR_FALSE); + mOutput.SetWritable(PR_FALSE); } // notify input stream that pipe now contains additional data - if (InputStream()->OnInputReadable(bytesWritten, events)) + if (mInput.OnInputReadable(bytesWritten, events)) mon.Notify(); } } @@ -534,14 +559,14 @@ nsPipe::OnPipeException(nsresult reason, PRBool outputOnly) // an output-only exception applies to the input end if the pipe has // zero bytes available. - if (outputOnly && !InputStream()->Available()) + if (outputOnly && !mInput.Available()) outputOnly = PR_FALSE; if (!outputOnly) - if (InputStream()->OnInputException(reason, events)) + if (mInput.OnInputException(reason, events)) mon.Notify(); - if (OutputStream()->OnOutputException(reason, events)) + if (mOutput.OnOutputException(reason, events)) mon.Notify(); } } @@ -554,14 +579,14 @@ nsPipeEvents::~nsPipeEvents() { // dispatch any pending events - if (mInputNotify) { - mInputNotify->OnInputStreamReady(mInputStream); - mInputNotify = 0; + if (mInputCallback) { + mInputCallback->OnInputStreamReady(mInputStream); + mInputCallback = 0; mInputStream = 0; } - if (mOutputNotify) { - mOutputNotify->OnOutputStreamReady(mOutputStream); - mOutputNotify = 0; + if (mOutputCallback) { + mOutputCallback->OnOutputStreamReady(mOutputStream); + mOutputCallback = 0; mOutputStream = 0; } } @@ -575,7 +600,7 @@ nsPipeInputStream::Wait() { NS_ASSERTION(mBlocking, "wait on non-blocking pipe input stream"); - nsAutoMonitor mon(mPipe->Monitor()); + nsAutoMonitor mon(mPipe->mMonitor); while (NS_SUCCEEDED(mPipe->mStatus) && (mAvailable == 0)) { LOG(("III pipe input: waiting for data\n")); @@ -598,9 +623,10 @@ nsPipeInputStream::OnInputReadable(PRUint32 bytesWritten, nsPipeEvents &events) mAvailable += bytesWritten; - if (mNotify) { - events.NotifyInputReady(this, mNotify); - mNotify = 0; + if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) { + events.NotifyInputReady(this, mCallback); + mCallback = 0; + mCallbackFlags = 0; } else if (mBlocked) result = PR_TRUE; @@ -618,9 +644,13 @@ nsPipeInputStream::OnInputException(nsresult reason, nsPipeEvents &events) NS_ASSERTION(NS_FAILED(reason), "huh? successful exception"); - if (mNotify) { - events.NotifyInputReady(this, mNotify); - mNotify = 0; + // force count of available bytes to zero. + mAvailable = 0; + + if (mCallback) { + events.NotifyInputReady(this, mCallback); + mCallback = 0; + mCallbackFlags = 0; } else if (mBlocked) result = PR_TRUE; @@ -650,9 +680,9 @@ NS_IMPL_QUERY_INTERFACE4(nsPipeInputStream, nsISearchableInputStream) NS_IMETHODIMP -nsPipeInputStream::CloseEx(nsresult reason) +nsPipeInputStream::CloseWithStatus(nsresult reason) { - LOG(("III CloseEx [this=%x reason=%x]\n", this, reason)); + LOG(("III CloseWithStatus [this=%x reason=%x]\n", this, reason)); if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED; @@ -664,13 +694,13 @@ nsPipeInputStream::CloseEx(nsresult reason) NS_IMETHODIMP nsPipeInputStream::Close() { - return CloseEx(NS_BASE_STREAM_CLOSED); + return CloseWithStatus(NS_BASE_STREAM_CLOSED); } NS_IMETHODIMP nsPipeInputStream::Available(PRUint32 *result) { - nsAutoMonitor mon(mPipe->Monitor()); + nsAutoMonitor mon(mPipe->mMonitor); // return error if pipe closed if (!mAvailable && NS_FAILED(mPipe->mStatus)) @@ -780,34 +810,38 @@ nsPipeInputStream::IsNonBlocking(PRBool *aNonBlocking) } NS_IMETHODIMP -nsPipeInputStream::AsyncWait(nsIInputStreamNotify *notify, - PRUint32 aRequestedCount, - nsIEventQueue *eventQ) +nsPipeInputStream::AsyncWait(nsIInputStreamCallback *callback, + PRUint32 flags, + PRUint32 requestedCount, + nsIEventTarget *target) { LOG(("III AsyncWait [this=%x]\n", this)); nsPipeEvents pipeEvents; { - nsAutoMonitor mon(mPipe->Monitor()); + nsAutoMonitor mon(mPipe->mMonitor); - // replace a pending notify - mNotify = 0; + // replace a pending callback + mCallback = 0; + mCallbackFlags = 0; - nsCOMPtr proxy; - if (eventQ) { + nsCOMPtr proxy; + if (target) { nsresult rv = NS_NewInputStreamReadyEvent(getter_AddRefs(proxy), - notify, eventQ); + callback, target); if (NS_FAILED(rv)) return rv; - notify = proxy; + callback = proxy; } - if (NS_FAILED(mPipe->mStatus) || mAvailable) { + if (NS_FAILED(mPipe->mStatus) || + (mAvailable && !(flags & WAIT_CLOSURE_ONLY))) { // stream is already closed or readable; post event. - pipeEvents.NotifyInputReady(this, notify); + pipeEvents.NotifyInputReady(this, callback); } else { - // queue up notify object to be notified when data becomes available - mNotify = notify; + // queue up callback object to be notified when data becomes available + mCallback = callback; + mCallbackFlags = flags; } } return NS_OK; @@ -847,7 +881,7 @@ nsPipeInputStream::Search(const char *forString, { LOG(("III Search [for=%s ic=%u]\n", forString, ignoreCase)); - nsAutoMonitor mon(mPipe->Monitor()); + nsAutoMonitor mon(mPipe->mMonitor); char *cursor1, *limit1; PRUint32 index = 0, offset = 0; @@ -924,7 +958,7 @@ nsPipeOutputStream::Wait() { NS_ASSERTION(mBlocking, "wait on non-blocking pipe output stream"); - nsAutoMonitor mon(mPipe->Monitor()); + nsAutoMonitor mon(mPipe->mMonitor); if (NS_SUCCEEDED(mPipe->mStatus) && !mWritable) { LOG(("OOO pipe output: waiting for space\n")); @@ -945,9 +979,10 @@ nsPipeOutputStream::OnOutputWritable(nsPipeEvents &events) mWritable = PR_TRUE; - if (mNotify) { - events.NotifyOutputReady(this, mNotify); - mNotify = 0; + if (mCallback && !(mCallbackFlags & WAIT_CLOSURE_ONLY)) { + events.NotifyOutputReady(this, mCallback); + mCallback = 0; + mCallbackFlags = 0; } else if (mBlocked) result = PR_TRUE; @@ -966,9 +1001,10 @@ nsPipeOutputStream::OnOutputException(nsresult reason, nsPipeEvents &events) NS_ASSERTION(NS_FAILED(reason), "huh? successful exception"); mWritable = PR_FALSE; - if (mNotify) { - events.NotifyOutputReady(this, mNotify); - mNotify = 0; + if (mCallback) { + events.NotifyOutputReady(this, mCallback); + mCallback = 0; + mCallbackFlags = 0; } else if (mBlocked) result = PR_TRUE; @@ -997,9 +1033,9 @@ NS_IMPL_QUERY_INTERFACE2(nsPipeOutputStream, nsIAsyncOutputStream) NS_IMETHODIMP -nsPipeOutputStream::CloseEx(nsresult reason) +nsPipeOutputStream::CloseWithStatus(nsresult reason) { - LOG(("OOO CloseEx [this=%x reason=%x]\n", this, reason)); + LOG(("OOO CloseWithStatus [this=%x reason=%x]\n", this, reason)); if (NS_SUCCEEDED(reason)) reason = NS_BASE_STREAM_CLOSED; @@ -1012,7 +1048,7 @@ nsPipeOutputStream::CloseEx(nsresult reason) NS_IMETHODIMP nsPipeOutputStream::Close() { - return CloseEx(NS_BASE_STREAM_CLOSED); + return CloseWithStatus(NS_BASE_STREAM_CLOSED); } NS_IMETHODIMP @@ -1124,8 +1160,8 @@ nsReadFromInputStream(nsIOutputStream* outStr, NS_IMETHODIMP nsPipeOutputStream::WriteFrom(nsIInputStream* fromStream, - PRUint32 count, - PRUint32 *writeCount) + PRUint32 count, + PRUint32 *writeCount) { return WriteSegments(nsReadFromInputStream, fromStream, count, writeCount); } @@ -1138,34 +1174,38 @@ nsPipeOutputStream::IsNonBlocking(PRBool *aNonBlocking) } NS_IMETHODIMP -nsPipeOutputStream::AsyncWait(nsIOutputStreamNotify *notify, - PRUint32 aRequestedCount, - nsIEventQueue *eventQ) +nsPipeOutputStream::AsyncWait(nsIOutputStreamCallback *callback, + PRUint32 flags, + PRUint32 requestedCount, + nsIEventTarget *target) { LOG(("OOO AsyncWait [this=%x]\n", this)); nsPipeEvents pipeEvents; { - nsAutoMonitor mon(mPipe->Monitor()); + nsAutoMonitor mon(mPipe->mMonitor); - // replace a pending notify - mNotify = 0; + // replace a pending callback + mCallback = 0; + mCallbackFlags = 0; - nsCOMPtr proxy; - if (eventQ) { + nsCOMPtr proxy; + if (target) { nsresult rv = NS_NewOutputStreamReadyEvent(getter_AddRefs(proxy), - notify, eventQ); + callback, target); if (NS_FAILED(rv)) return rv; - notify = proxy; + callback = proxy; } - if (NS_FAILED(mPipe->mStatus) || mWritable) { + if (NS_FAILED(mPipe->mStatus) || + (mWritable && !(flags & WAIT_CLOSURE_ONLY))) { // stream is already closed or writable; post event. - pipeEvents.NotifyOutputReady(this, notify); + pipeEvents.NotifyOutputReady(this, callback); } else { - // queue up notify object to be notified when data becomes available - mNotify = notify; + // queue up callback object to be notified when data becomes available + mCallback = callback; + mCallbackFlags = flags; } } return NS_OK; diff --git a/mozilla/xpcom/io/nsStreamUtils.cpp b/mozilla/xpcom/io/nsStreamUtils.cpp index 1a3c38deef0..abf21fb40fc 100644 --- a/mozilla/xpcom/io/nsStreamUtils.cpp +++ b/mozilla/xpcom/io/nsStreamUtils.cpp @@ -38,41 +38,41 @@ #include "nsStreamUtils.h" #include "nsCOMPtr.h" #include "nsIPipe.h" -#include "nsIEventQueue.h" +#include "nsIEventTarget.h" +#include "nsAutoLock.h" //----------------------------------------------------------------------------- class nsInputStreamReadyEvent : public PLEvent - , public nsIInputStreamNotify + , public nsIInputStreamCallback { public: NS_DECL_ISUPPORTS - nsInputStreamReadyEvent(nsIInputStreamNotify *notify, - nsIEventQueue *eventQ) - : mNotify(notify) - , mEventQ(eventQ) + nsInputStreamReadyEvent(nsIInputStreamCallback *callback, + nsIEventTarget *target) + : mCallback(callback) + , mTarget(target) { - NS_INIT_ISUPPORTS(); } virtual ~nsInputStreamReadyEvent() { - if (mNotify) { + if (mCallback) { nsresult rv; // // whoa!! looks like we never posted this event. take care to - // release mNotify on the correct thread. if mEventQ lives on the + // release mCallback on the correct thread. if mTarget lives on the // calling thread, then we are ok. otherwise, we have to try to // proxy the Release over the right thread. if that thread is dead, // then there's nothing we can do... better to leak than crash. // PRBool val; - rv = mEventQ->IsQueueOnCurrentThread(&val); + rv = mTarget->IsOnCurrentThread(&val); if (NS_FAILED(rv) || !val) { - nsCOMPtr event; - NS_NewInputStreamReadyEvent(getter_AddRefs(event), mNotify, mEventQ); - mNotify = 0; + nsCOMPtr event; + NS_NewInputStreamReadyEvent(getter_AddRefs(event), mCallback, mTarget); + mCallback = 0; if (event) { rv = event->OnInputStreamReady(nsnull); if (NS_FAILED(rv)) { @@ -94,7 +94,7 @@ public: PL_InitEvent(this, nsnull, EventHandler, EventCleanup); - if (mEventQ->PostEvent(this) == PR_FAILURE) { + if (NS_FAILED(mTarget->PostEvent(this))) { NS_WARNING("PostEvent failed"); NS_RELEASE_THIS(); return NS_ERROR_FAILURE; @@ -104,21 +104,21 @@ public: } private: - nsCOMPtr mStream; - nsCOMPtr mNotify; - nsCOMPtr mEventQ; + nsCOMPtr mStream; + nsCOMPtr mCallback; + nsCOMPtr mTarget; - static void *PR_CALLBACK EventHandler(PLEvent *plevent) + PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *plevent) { nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent; // bypass event delivery if this is a cleanup event... - if (ev->mStream) - ev->mNotify->OnInputStreamReady(ev->mStream); - ev->mNotify = 0; + if (ev->mCallback) + ev->mCallback->OnInputStreamReady(ev->mStream); + ev->mCallback = 0; return NULL; } - static void PR_CALLBACK EventCleanup(PLEvent *plevent) + PR_STATIC_CALLBACK(void) EventCleanup(PLEvent *plevent) { nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent; NS_RELEASE(ev); @@ -126,41 +126,40 @@ private: }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsInputStreamReadyEvent, - nsIInputStreamNotify) + nsIInputStreamCallback) //----------------------------------------------------------------------------- class nsOutputStreamReadyEvent : public PLEvent - , public nsIOutputStreamNotify + , public nsIOutputStreamCallback { public: NS_DECL_ISUPPORTS - nsOutputStreamReadyEvent(nsIOutputStreamNotify *notify, - nsIEventQueue *eventQ) - : mNotify(notify) - , mEventQ(eventQ) + nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback, + nsIEventTarget *target) + : mCallback(callback) + , mTarget(target) { - NS_INIT_ISUPPORTS(); } virtual ~nsOutputStreamReadyEvent() { - if (mNotify) { + if (mCallback) { nsresult rv; // // whoa!! looks like we never posted this event. take care to - // release mNotify on the correct thread. if mEventQ lives on the + // release mCallback on the correct thread. if mTarget lives on the // calling thread, then we are ok. otherwise, we have to try to // proxy the Release over the right thread. if that thread is dead, // then there's nothing we can do... better to leak than crash. // PRBool val; - rv = mEventQ->IsQueueOnCurrentThread(&val); + rv = mTarget->IsOnCurrentThread(&val); if (NS_FAILED(rv) || !val) { - nsCOMPtr event; - NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mNotify, mEventQ); - mNotify = 0; + nsCOMPtr event; + NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mCallback, mTarget); + mCallback = 0; if (event) { rv = event->OnOutputStreamReady(nsnull); if (NS_FAILED(rv)) { @@ -173,10 +172,10 @@ public: } } - void Init(nsIOutputStreamNotify *notify, nsIEventQueue *eventQ) + void Init(nsIOutputStreamCallback *callback, nsIEventTarget *target) { - mNotify = notify; - mEventQ = eventQ; + mCallback = callback; + mTarget = target; PL_InitEvent(this, nsnull, EventHandler, EventCleanup); } @@ -190,7 +189,7 @@ public: PL_InitEvent(this, nsnull, EventHandler, EventCleanup); - if (mEventQ->PostEvent(this) == PR_FAILURE) { + if (NS_FAILED(mTarget->PostEvent(this))) { NS_WARNING("PostEvent failed"); NS_RELEASE_THIS(); return NS_ERROR_FAILURE; @@ -200,20 +199,20 @@ public: } private: - nsCOMPtr mStream; - nsCOMPtr mNotify; - nsCOMPtr mEventQ; + nsCOMPtr mStream; + nsCOMPtr mCallback; + nsCOMPtr mTarget; - static void *PR_CALLBACK EventHandler(PLEvent *plevent) + PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *plevent) { nsOutputStreamReadyEvent *ev = (nsOutputStreamReadyEvent *) plevent; - if (ev->mNotify) - ev->mNotify->OnOutputStreamReady(ev->mStream); - ev->mNotify = 0; + if (ev->mCallback) + ev->mCallback->OnOutputStreamReady(ev->mStream); + ev->mCallback = 0; return NULL; } - static void PR_CALLBACK EventCleanup(PLEvent *ev) + PR_STATIC_CALLBACK(void) EventCleanup(PLEvent *ev) { nsOutputStreamReadyEvent *event = (nsOutputStreamReadyEvent *) ev; NS_RELEASE(event); @@ -221,16 +220,16 @@ private: }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsOutputStreamReadyEvent, - nsIOutputStreamNotify) + nsIOutputStreamCallback) //----------------------------------------------------------------------------- NS_COM nsresult -NS_NewInputStreamReadyEvent(nsIInputStreamNotify **event, - nsIInputStreamNotify *notify, - nsIEventQueue *eventQ) +NS_NewInputStreamReadyEvent(nsIInputStreamCallback **event, + nsIInputStreamCallback *callback, + nsIEventTarget *target) { - nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(notify, eventQ); + nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(callback, target); if (!ev) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*event = ev); @@ -238,11 +237,11 @@ NS_NewInputStreamReadyEvent(nsIInputStreamNotify **event, } NS_COM nsresult -NS_NewOutputStreamReadyEvent(nsIOutputStreamNotify **event, - nsIOutputStreamNotify *notify, - nsIEventQueue *eventQ) +NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **event, + nsIOutputStreamCallback *callback, + nsIEventTarget *target) { - nsOutputStreamReadyEvent *ev = new nsOutputStreamReadyEvent(notify, eventQ); + nsOutputStreamReadyEvent *ev = new nsOutputStreamReadyEvent(callback, target); if (!ev) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*event = ev); @@ -252,22 +251,225 @@ NS_NewOutputStreamReadyEvent(nsIOutputStreamNotify **event, //----------------------------------------------------------------------------- // NS_AsyncCopy implementation -// this stream copier assumes the input stream is buffered (ReadSegments OK) -class nsStreamCopierIB : public nsIInputStreamNotify - , public nsIOutputStreamNotify +// abstract stream copier... +class nsAStreamCopier : public nsIInputStreamCallback + , public nsIOutputStreamCallback { public: NS_DECL_ISUPPORTS - nsStreamCopierIB(nsIAsyncInputStream *in, - nsIAsyncOutputStream *out, - PRUint32 chunksize) - : mSource(in) - , mSink(out) - , mChunkSize(chunksize) - { NS_INIT_ISUPPORTS(); } + nsAStreamCopier() + : mLock(nsnull) + , mCallback(nsnull) + , mClosure(nsnull) + , mChunkSize(0) + , mEventInProcess(PR_FALSE) + , mEventIsPending(PR_FALSE) + { + } + + virtual ~nsAStreamCopier() + { + if (mLock) + PR_DestroyLock(mLock); + } + + // kick off the async copy... + nsresult Start(nsIInputStream *source, + nsIOutputStream *sink, + nsIEventTarget *target, + nsAsyncCopyCallbackFun callback, + void *closure, + PRUint32 chunksize) + { + mSource = source; + mSink = sink; + mTarget = target; + mCallback = callback; + mClosure = closure; + mChunkSize = chunksize; + + mLock = PR_NewLock(); + if (!mLock) + return NS_ERROR_OUT_OF_MEMORY; + + mAsyncSource = do_QueryInterface(mSource); + mAsyncSink = do_QueryInterface(mSink); + + return PostContinuationEvent(); + } + + // implemented by subclasses, returns number of bytes copied and + // sets source and sink condition before returning. + virtual PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) = 0; + + void Process() + { + if (!mSource || !mSink) + return; + + nsresult sourceCondition, sinkCondition; + + // ok, copy data from source to sink. + for (;;) { + PRUint32 n = DoCopy(&sourceCondition, &sinkCondition); + if (NS_FAILED(sourceCondition) || NS_FAILED(sinkCondition) || n == 0) { + if (sourceCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSource) { + // need to wait for more data from source. while waiting for + // more source data, be sure to observe failures on output end. + mAsyncSource->AsyncWait(this, 0, 0, nsnull); + + if (mAsyncSink) + mAsyncSink->AsyncWait(this, + nsIAsyncOutputStream::WAIT_CLOSURE_ONLY, + 0, nsnull); + } + else if (sinkCondition == NS_BASE_STREAM_WOULD_BLOCK && mAsyncSink) { + // need to wait for more room in the sink. while waiting for + // more room in the sink, be sure to observer failures on the + // input end. + mAsyncSink->AsyncWait(this, 0, 0, nsnull); + + if (mAsyncSource) + mAsyncSource->AsyncWait(this, + nsIAsyncInputStream::WAIT_CLOSURE_ONLY, + 0, nsnull); + } + else { + // close source + if (mAsyncSource) + mAsyncSource->CloseWithStatus(sinkCondition); + else + mSource->Close(); + mAsyncSource = nsnull; + mSource = nsnull; + + // close sink + if (mAsyncSink) + mAsyncSink->CloseWithStatus(sourceCondition); + else + mSink->Close(); + mAsyncSink = nsnull; + mSink = nsnull; + + // notify state complete... + if (mCallback) { + nsresult status = sourceCondition; + if (NS_SUCCEEDED(status)) + status = sinkCondition; + if (status == NS_BASE_STREAM_CLOSED) + status = NS_OK; + mCallback(mClosure, status); + } + } + break; + } + } + } + + NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *source) + { + PostContinuationEvent(); + return NS_OK; + } + + NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *sink) + { + PostContinuationEvent(); + return NS_OK; + } + + PR_STATIC_CALLBACK(void*) HandleContinuationEvent(PLEvent *event) + { + nsAStreamCopier *self = (nsAStreamCopier *) event->owner; + self->Process(); + + // clear "in process" flag and post any pending continuation event + nsAutoLock lock(self->mLock); + self->mEventInProcess = PR_FALSE; + if (self->mEventIsPending) { + self->mEventIsPending = PR_FALSE; + self->PostContinuationEvent_Locked(); + } + return nsnull; + } + + PR_STATIC_CALLBACK(void) DestroyContinuationEvent(PLEvent *event) + { + nsAStreamCopier *self = (nsAStreamCopier *) event->owner; + NS_RELEASE(self); + delete event; + } + + nsresult PostContinuationEvent() + { + // we cannot post a continuation event if there is currently + // an event in process. doing so could result in Process being + // run simultaneously on multiple threads, so we mark the event + // as pending, and if an event is already in process then we + // just let that existing event take care of posting the real + // continuation event. + + nsAutoLock lock(mLock); + return PostContinuationEvent_Locked(); + } + + nsresult PostContinuationEvent_Locked() + { + nsresult rv = NS_OK; + if (mEventInProcess) + mEventIsPending = PR_TRUE; + else { + PLEvent *event = new PLEvent; + if (!event) + rv = NS_ERROR_OUT_OF_MEMORY; + else { + NS_ADDREF_THIS(); + PL_InitEvent(event, this, + HandleContinuationEvent, + DestroyContinuationEvent); + + rv = mTarget->PostEvent(event); + if (NS_SUCCEEDED(rv)) + mEventInProcess = PR_TRUE; + else { + NS_ERROR("unable to post continuation event"); + PL_DestroyEvent(event); + } + } + } + return rv; + } + +protected: + nsCOMPtr mSource; + nsCOMPtr mSink; + nsCOMPtr mAsyncSource; + nsCOMPtr mAsyncSink; + nsCOMPtr mTarget; + PRLock *mLock; + nsAsyncCopyCallbackFun mCallback; + void *mClosure; + PRUint32 mChunkSize; + PRPackedBool mEventInProcess; + PRPackedBool mEventIsPending; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS2(nsAStreamCopier, + nsIInputStreamCallback, + nsIOutputStreamCallback) + +class nsStreamCopierIB : public nsAStreamCopier +{ +public: + nsStreamCopierIB() : nsAStreamCopier() {} virtual ~nsStreamCopierIB() {} + struct ReadSegmentsState { + nsIOutputStream *mSink; + nsresult mSinkCondition; + }; + static NS_METHOD ConsumeInputBuffer(nsIInputStream *inStr, void *closure, const char *buffer, @@ -275,78 +477,42 @@ public: PRUint32 count, PRUint32 *countWritten) { - nsStreamCopierIB *self = (nsStreamCopierIB *) closure; + ReadSegmentsState *state = (ReadSegmentsState *) closure; - nsresult rv = self->mSink->Write(buffer, count, countWritten); + nsresult rv = state->mSink->Write(buffer, count, countWritten); if (NS_FAILED(rv)) - self->mSinkCondition = rv; + state->mSinkCondition = rv; else if (*countWritten == 0) - self->mSinkCondition = NS_BASE_STREAM_CLOSED; + state->mSinkCondition = NS_BASE_STREAM_CLOSED; - return self->mSinkCondition; + return state->mSinkCondition; } - // called on some random thread - NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *in) + PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) { - NS_ASSERTION(in == mSource, "unexpected stream"); - // Do all of our work from OnOutputStreamReady. This - // way we're more likely to always be working on the - // same thread. - return mSink->AsyncWait(this, 0, nsnull); - } + ReadSegmentsState state; + state.mSink = mSink; + state.mSinkCondition = NS_OK; - // called on some random thread - NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *out) - { - NS_ASSERTION(out == mSink, "unexpected stream"); - for (;;) { - mSinkCondition = NS_OK; // reset - PRUint32 n; - nsresult rv = mSource->ReadSegments(ConsumeInputBuffer, this, mChunkSize, &n); - if (NS_FAILED(rv) || (n == 0)) { - if (rv == NS_BASE_STREAM_WOULD_BLOCK) - mSource->AsyncWait(this, 0, nsnull); - else if (mSinkCondition == NS_BASE_STREAM_WOULD_BLOCK) - mSink->AsyncWait(this, 0, nsnull); - else { - mSink = 0; - mSource->CloseEx(mSinkCondition); - mSource = 0; - } - break; - } - } - return NS_OK; + PRUint32 n; + *sourceCondition = + mSource->ReadSegments(ConsumeInputBuffer, &state, mChunkSize, &n); + *sinkCondition = state.mSinkCondition; + return n; } - -private: - nsCOMPtr mSource; - nsCOMPtr mSink; - PRUint32 mChunkSize; - nsresult mSinkCondition; }; -NS_IMPL_THREADSAFE_ISUPPORTS2(nsStreamCopierIB, - nsIInputStreamNotify, - nsIOutputStreamNotify) - -// this stream copier assumes the output stream is buffered (WriteSegments OK) -class nsStreamCopierOB : public nsIInputStreamNotify - , public nsIOutputStreamNotify +class nsStreamCopierOB : public nsAStreamCopier { public: - NS_DECL_ISUPPORTS - - nsStreamCopierOB(nsIAsyncInputStream *in, - nsIAsyncOutputStream *out, - PRUint32 chunksize) - : mSource(in) - , mSink(out) - , mChunkSize(chunksize) - { NS_INIT_ISUPPORTS(); } + nsStreamCopierOB() : nsAStreamCopier() {} virtual ~nsStreamCopierOB() {} + struct WriteSegmentsState { + nsIInputStream *mSource; + nsresult mSourceCondition; + }; + static NS_METHOD FillOutputBuffer(nsIOutputStream *outStr, void *closure, char *buffer, @@ -354,121 +520,59 @@ public: PRUint32 count, PRUint32 *countRead) { - nsStreamCopierOB *self = (nsStreamCopierOB *) closure; + WriteSegmentsState *state = (WriteSegmentsState *) closure; - nsresult rv = self->mSource->Read(buffer, count, countRead); + nsresult rv = state->mSource->Read(buffer, count, countRead); if (NS_FAILED(rv)) - self->mSourceCondition = rv; + state->mSourceCondition = rv; else if (*countRead == 0) - self->mSourceCondition = NS_BASE_STREAM_CLOSED; + state->mSourceCondition = NS_BASE_STREAM_CLOSED; - return self->mSourceCondition; + return state->mSourceCondition; } - // called on some random thread - NS_IMETHOD OnInputStreamReady(nsIAsyncInputStream *in) + PRUint32 DoCopy(nsresult *sourceCondition, nsresult *sinkCondition) { - NS_ASSERTION(in == mSource, "unexpected stream"); - for (;;) { - mSourceCondition = NS_OK; // reset - PRUint32 n; - nsresult rv = mSink->WriteSegments(FillOutputBuffer, this, mChunkSize, &n); - if (NS_FAILED(rv) || (n == 0)) { - if (rv == NS_BASE_STREAM_WOULD_BLOCK) - mSink->AsyncWait(this, 0, nsnull); - else if (mSourceCondition == NS_BASE_STREAM_WOULD_BLOCK) - mSource->AsyncWait(this, 0, nsnull); - else { - mSource = 0; - mSink->CloseEx(mSourceCondition); - mSink = 0; - } - break; - } - } - return NS_OK; - } + WriteSegmentsState state; + state.mSource = mSource; + state.mSourceCondition = NS_OK; - // called on some random thread - NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *out) - { - NS_ASSERTION(out == mSink, "unexpected stream"); - // Do all of our work from OnInputStreamReady. This - // way we're more likely to always be working on the - // same thread. - return mSource->AsyncWait(this, 0, nsnull); + PRUint32 n; + *sinkCondition = + mSink->WriteSegments(FillOutputBuffer, &state, mChunkSize, &n); + *sourceCondition = state.mSourceCondition; + return n; } - -private: - nsCOMPtr mSource; - nsCOMPtr mSink; - PRUint32 mChunkSize; - nsresult mSourceCondition; }; -NS_IMPL_THREADSAFE_ISUPPORTS2(nsStreamCopierOB, - nsIInputStreamNotify, - nsIOutputStreamNotify) - //----------------------------------------------------------------------------- NS_COM nsresult -NS_AsyncCopy(nsIAsyncInputStream *source, - nsIAsyncOutputStream *sink, - PRBool bufferedSource, - PRBool bufferedSink, - PRUint32 segmentSize, - PRUint32 segmentCount, - nsIMemory *segmentAlloc) +NS_AsyncCopy(nsIInputStream *source, + nsIOutputStream *sink, + nsIEventTarget *target, + nsAsyncCopyMode mode, + PRUint32 chunkSize, + nsAsyncCopyCallbackFun callback, + void *closure) { + NS_ASSERTION(target, "non-null target required"); + nsresult rv; + nsAStreamCopier *copier; - // we need to insert a pipe if both the source and sink are not buffered. - if (!bufferedSource && !bufferedSink) { - nsCOMPtr pipeIn; - nsCOMPtr pipeOut; + if (mode == NS_ASYNCCOPY_VIA_READSEGMENTS) + copier = new nsStreamCopierIB(); + else + copier = new nsStreamCopierOB(); - rv = NS_NewPipe2(getter_AddRefs(pipeIn), - getter_AddRefs(pipeOut), - PR_TRUE, PR_TRUE, - segmentSize, segmentCount, segmentAlloc); - if (NS_FAILED(rv)) return rv; + if (!copier) + return NS_ERROR_OUT_OF_MEMORY; - // - // fire off two async copies :-) - // - rv = NS_AsyncCopy(source, pipeOut, PR_FALSE, PR_TRUE, segmentSize, 1, segmentAlloc); - if (NS_FAILED(rv)) return rv; - - rv = NS_AsyncCopy(pipeIn, sink, PR_TRUE, PR_FALSE, segmentSize, 1, segmentAlloc); - - // maybe calling NS_AsyncCopy twice is a bad idea! - NS_ASSERTION(NS_SUCCEEDED(rv), "uh-oh"); - - return rv; - } - - if (bufferedSource) { - // copy assuming ReadSegments OK - nsStreamCopierIB *copier = new nsStreamCopierIB(source, sink, segmentSize); - if (!copier) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(copier); - // wait on the sink - rv = sink->AsyncWait(copier, 0, nsnull); - NS_RELEASE(copier); - } - else { - // copy assuming WriteSegments OK - nsStreamCopierOB *copier = new nsStreamCopierOB(source, sink, segmentSize); - if (!copier) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(copier); - // wait on the source since the sink is buffered and should therefore - // already have room. - rv = source->AsyncWait(copier, 0, nsnull); - NS_RELEASE(copier); - } + // Start() takes an owning ref to the copier... + NS_ADDREF(copier); + rv = copier->Start(source, sink, target, callback, closure, chunkSize); + NS_RELEASE(copier); return rv; } diff --git a/mozilla/xpcom/io/nsStreamUtils.h b/mozilla/xpcom/io/nsStreamUtils.h index 3aaae9ff068..3d33f1c1c4b 100644 --- a/mozilla/xpcom/io/nsStreamUtils.h +++ b/mozilla/xpcom/io/nsStreamUtils.h @@ -40,66 +40,72 @@ #include "nscore.h" -class nsIAsyncInputStream; -class nsIAsyncOutputStream; -class nsIInputStreamNotify; -class nsIOutputStreamNotify; -class nsIEventQueue; -class nsIMemory; +class nsIInputStream; +class nsIOutputStream; +class nsIInputStreamCallback; +class nsIOutputStreamCallback; +class nsIEventTarget; /** * A "one-shot" proxy of the OnInputStreamReady callback. The resulting * proxy object's OnInputStreamReady function may only be called once! The * proxy object ensures that the real notify object will be free'd on the - * thread owning the given event queue regardless of what thread the proxy - * object is destroyed on. + * thread corresponding to the given event target regardless of what thread + * the proxy object is destroyed on. * * This function is designed to be used to implement AsyncWait when the - * aEventQ parameter is non-null. + * aEventTarget parameter is non-null. */ extern NS_COM nsresult -NS_NewInputStreamReadyEvent(nsIInputStreamNotify **aEvent, - nsIInputStreamNotify *aNotify, - nsIEventQueue *aEventQ); +NS_NewInputStreamReadyEvent(nsIInputStreamCallback **aEvent, + nsIInputStreamCallback *aNotify, + nsIEventTarget *aEventTarget); /** * A "one-shot" proxy of the OnOutputStreamReady callback. The resulting * proxy object's OnOutputStreamReady function may only be called once! The * proxy object ensures that the real notify object will be free'd on the - * thread owning the given event queue regardless of what thread the proxy - * object is destroyed on. + * thread corresponding to the given event target regardless of what thread + * the proxy object is destroyed on. * * This function is designed to be used to implement AsyncWait when the - * aEventQ parameter is non-null. + * aEventTarget parameter is non-null. */ extern NS_COM nsresult -NS_NewOutputStreamReadyEvent(nsIOutputStreamNotify **aEvent, - nsIOutputStreamNotify *aNotify, - nsIEventQueue *aEventQ); +NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **aEvent, + nsIOutputStreamCallback *aNotify, + nsIEventTarget *aEventTarget); + +/* ------------------------------------------------------------------------- */ + +enum nsAsyncCopyMode { + NS_ASYNCCOPY_VIA_READSEGMENTS, + NS_ASYNCCOPY_VIA_WRITESEGMENTS +}; /** - * Asynchronously copy data from an input stream to an output stream. + * This function is called when the async copy process completes. The reported + * status is NS_OK on success and some error code on failure. + */ +typedef void (* nsAsyncCopyCallbackFun)(void *closure, nsresult status); + +/** + * This function asynchronously copies data from the source to the sink. All + * data transfer occurs on the thread corresponding to the given event target. + * A null event target is not permitted. * - * @param aSource - * the source input stream containing the data to copy. - * @param aSink - * the data is copied to this output stream. - * @param aChunkSize - * read/write at most this many bytes at one time. - * @param aBufferedSource - * true if source implements ReadSegments. - * @param aBufferedSink - * true if sink implements WriteSegments. - * - * NOTE: the implementation does not rely on any event queues. + * The copier handles blocking or non-blocking streams transparently. If a + * stream operation returns NS_BASE_STREAM_WOULD_BLOCK, then the stream will + * be QI'd to nsIAsync{In,Out}putStream and its AsyncWait method will be used + * to determine when to resume copying. */ extern NS_COM nsresult -NS_AsyncCopy(nsIAsyncInputStream *aSource, - nsIAsyncOutputStream *aSink, - PRBool aBufferedSource = PR_TRUE, - PRBool aBufferedSink = PR_TRUE, - PRUint32 aSegmentSize = 4096, - PRUint32 aSegmentCount = 1, - nsIMemory *aSegmentAlloc = nsnull); +NS_AsyncCopy(nsIInputStream *aSource, + nsIOutputStream *aSink, + nsIEventTarget *aEventTarget, + nsAsyncCopyMode aMode = NS_ASYNCCOPY_VIA_READSEGMENTS, + PRUint32 aChunkSize = 4096, + nsAsyncCopyCallbackFun aCallbackFun = nsnull, + void *aCallbackClosure = nsnull); #endif // !nsStreamUtils_h__ diff --git a/mozilla/xpcom/proxy/public/nsProxyRelease.h b/mozilla/xpcom/proxy/public/nsProxyRelease.h index ba5f6895553..1802b459bb4 100644 --- a/mozilla/xpcom/proxy/public/nsProxyRelease.h +++ b/mozilla/xpcom/proxy/public/nsProxyRelease.h @@ -42,60 +42,21 @@ #include "pratom.h" #include "prmem.h" -// Ensures that the delete of a nsISupports object occurs on the main thread. - -static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); - -static void* PR_CALLBACK -ReleaseDestructorEventHandler(PLEvent *self) -{ - nsISupports* owner = (nsISupports*) PL_GetEventOwner(self); - NS_RELEASE(owner); - return nsnull; -} - -static void PR_CALLBACK -ReleaseDestructorDestroyHandler(PLEvent *self) -{ - delete self; -} - -static void -NS_ProxyRelease(nsIEventQueue *eventQ, nsISupports *doomed, PRBool alwaysProxy=PR_FALSE) -{ - if (!doomed) - return; - - if (!eventQ) { - NS_RELEASE(doomed); - return; - } - - if (!alwaysProxy) { - PRBool onCurrentThread = PR_FALSE; - eventQ->IsQueueOnCurrentThread(&onCurrentThread); - if (onCurrentThread) { - NS_RELEASE(doomed); - return; - } - } - - PLEvent *ev = new PLEvent; - if (!ev) { - NS_ERROR("failed to allocate PLEvent"); - // we do not release doomed here since it may cause a delete on the the - // wrong thread. better to leak than crash. - return; - } - - PL_InitEvent(ev, - (void *) doomed, - ReleaseDestructorEventHandler, - ReleaseDestructorDestroyHandler); - - PRStatus rv = eventQ->PostEvent(ev); - NS_ASSERTION(rv == PR_SUCCESS, "PostEvent failed"); -} +/** + * Ensures that the delete of a nsISupports object occurs on the target thread. + * + * @param target + * the target thread where the doomed object should be released. + * @param doomed + * the doomed object; the object to be released on the target thread. + * @param alwaysProxy + * normally, if NS_ProxyRelease is called on the target thread, then the + * doomed object will released directly. however, if this parameter is + * true, then a PLEvent will always be posted to the target thread and + * the release will happen when that PLEvent is handled. + */ +NS_COM nsresult NS_ProxyRelease + (nsIEventTarget *target, nsISupports *doomed, PRBool alwaysProxy=PR_FALSE); #define NS_IMPL_PROXY_RELEASE(_class) \ @@ -109,6 +70,7 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) { \ mRefCnt = 1; /* stabilize */ \ PRBool callDirectly = PR_TRUE; \ + static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); \ nsCOMPtr eventQService \ = do_GetService(kEventQueueServiceCID); \ NS_ASSERTION(eventQService, "event queue service is unavailable"); \ @@ -117,7 +79,7 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) if (eventQService) { \ eventQService->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eventQ)); \ if (eventQ) \ - eventQ->IsQueueOnCurrentThread(&callDirectly); \ + eventQ->IsOnCurrentThread(&callDirectly); \ } \ \ if (callDirectly) \ diff --git a/mozilla/xpcom/proxy/src/Makefile.in b/mozilla/xpcom/proxy/src/Makefile.in index 74b55c633a1..8bd20d736db 100644 --- a/mozilla/xpcom/proxy/src/Makefile.in +++ b/mozilla/xpcom/proxy/src/Makefile.in @@ -37,6 +37,7 @@ CPPSRCS = \ nsProxyEventClass.cpp \ nsProxyEventObject.cpp \ nsProxyObjectManager.cpp \ + nsProxyRelease.cpp \ $(NULL) DEFINES += -D_IMPL_NS_COM -DEXPORT_XPTC_API -DEXPORT_XPTI_API diff --git a/mozilla/xpcom/proxy/src/nsProxyEvent.cpp b/mozilla/xpcom/proxy/src/nsProxyEvent.cpp index f20d613c04b..e3e5262a935 100644 --- a/mozilla/xpcom/proxy/src/nsProxyEvent.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyEvent.cpp @@ -309,7 +309,7 @@ nsProxyObject::Release(void) mRefCnt = 1; /* stabilize */ PRBool callDirectly; - mDestQueue->IsQueueOnCurrentThread(&callDirectly); + mDestQueue->IsOnCurrentThread(&callDirectly); if (callDirectly) { @@ -466,7 +466,7 @@ nsProxyObject::Post( PRUint32 methodIndex, // as the destination event queue. if ( (methodIndex == 0) || (mProxyType & PROXY_SYNC && - NS_SUCCEEDED(mDestQueue->IsQueueOnCurrentThread(&callDirectly)) && + NS_SUCCEEDED(mDestQueue->IsOnCurrentThread(&callDirectly)) && callDirectly)) { diff --git a/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp b/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp index c0ba9700917..208f9c3d9d6 100644 --- a/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp @@ -209,7 +209,7 @@ nsProxyObjectManager::GetProxyForObject(nsIEventQueue *destQueue, if (postQ && !(proxyType & PROXY_ASYNC) && !(proxyType & PROXY_ALWAYS)) { PRBool aResult; - postQ->IsQueueOnCurrentThread(&aResult); + postQ->IsOnCurrentThread(&aResult); if (aResult) { diff --git a/mozilla/xpcom/proxy/src/nsProxyRelease.cpp b/mozilla/xpcom/proxy/src/nsProxyRelease.cpp new file mode 100644 index 00000000000..3ceeb6674bf --- /dev/null +++ b/mozilla/xpcom/proxy/src/nsProxyRelease.cpp @@ -0,0 +1,55 @@ +#include "nsProxyRelease.h" + +PR_STATIC_CALLBACK(void*) +HandleProxyReleaseEvent(PLEvent *self) +{ + nsISupports* owner = (nsISupports*) self->owner; + NS_RELEASE(owner); + return nsnull; +} + +PR_STATIC_CALLBACK(void) +DestroyProxyReleaseEvent(PLEvent *self) +{ + delete self; +} + +NS_COM nsresult +NS_ProxyRelease(nsIEventTarget *target, nsISupports *doomed, PRBool alwaysProxy) +{ + nsresult rv; + + if (!target) { + NS_RELEASE(doomed); + return NS_OK; + } + + if (!alwaysProxy) { + PRBool onCurrentThread = PR_FALSE; + rv = target->IsOnCurrentThread(&onCurrentThread); + if (NS_SUCCEEDED(rv) && onCurrentThread) { + NS_RELEASE(doomed); + return NS_OK; + } + } + + PLEvent *ev = new PLEvent; + if (!ev) { + // we do not release doomed here since it may cause a delete on the + // wrong thread. better to leak than crash. + return NS_ERROR_OUT_OF_MEMORY; + } + + PL_InitEvent(ev, doomed, + HandleProxyReleaseEvent, + DestroyProxyReleaseEvent); + + rv = target->PostEvent(ev); + if (NS_FAILED(rv)) { + NS_WARNING("failed to post proxy release event"); + PL_DestroyEvent(ev); + // again, it is better to leak the doomed object than risk crashing as + // a result of deleting it on the wrong thread. + } + return rv; +} diff --git a/mozilla/xpcom/tests/TestThreads.cpp b/mozilla/xpcom/tests/TestThreads.cpp index 15eedbef385..795acac30c2 100644 --- a/mozilla/xpcom/tests/TestThreads.cpp +++ b/mozilla/xpcom/tests/TestThreads.cpp @@ -37,7 +37,6 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIThread.h" -#include "nsIThreadPool.h" #include "nsIRunnable.h" #include #include @@ -45,49 +44,6 @@ #include "nsCOMPtr.h" #include "nsIServiceManager.h" -class nsConcurrentRunner : public nsIRunnable { -public: - NS_DECL_ISUPPORTS - - NS_IMETHOD Run() { - nsCOMPtr thread; - nsresult rv = nsIThread::GetCurrent(getter_AddRefs(thread)); - if (NS_FAILED(rv)) { - printf("failed to get current thread\n"); - return rv; - } - - mTestInt = 666; - PR_Sleep(PR_SecondsToInterval(1)); - - if (mTestInt != 666) - printf("Illegal access. Data corruption detected.\n"); - - mTestInt = 0; - PR_Sleep(PR_SecondsToInterval(2)); - - if (mTestInt != 0) - printf("Illegal access. Data corruption detected.\n"); - - // if we don't do something slow, we'll never see the other - // worker threads run - PR_Sleep(PR_MillisecondsToInterval(1)); - - return rv; - } - - nsConcurrentRunner(){ - mTestInt = 0; - } - -private: - PRInt32 mTestInt; - -}; - -NS_IMPL_THREADSAFE_ISUPPORTS1(nsConcurrentRunner, nsIRunnable) - - class nsRunner : public nsIRunnable { public: NS_DECL_ISUPPORTS @@ -175,48 +131,6 @@ TestThreads() return NS_OK; } -nsresult -TestThreadPools(PRUint32 poolMinSize, PRUint32 poolMaxSize, - PRUint32 nRequests, PRIntervalTime dispatchWaitInterval = 0) -{ - nsCOMPtr pool; - nsresult rv = NS_NewThreadPool(getter_AddRefs(pool), poolMinSize, poolMaxSize); - if (NS_FAILED(rv)) { - printf("failed to create thead pool\n"); - return rv; - } - - for (PRUint32 i = 0; i < nRequests; i++) { - rv = pool->DispatchRequest(new nsRunner(i+2)); - if (dispatchWaitInterval && i % poolMaxSize == poolMaxSize - 1) { - PR_Sleep(dispatchWaitInterval); - } - } - rv = pool->Shutdown(); - return rv; -} - -nsresult -TestThreadPoolsConcurrent(PRUint32 poolMinSize, - PRUint32 poolMaxSize, - PRUint32 nRequests) -{ - nsCOMPtr pool; - nsresult rv = NS_NewThreadPool(getter_AddRefs(pool), poolMinSize, poolMaxSize); - if (NS_FAILED(rv)) { - printf("failed to create thead pool\n"); - return rv; - } - - nsConcurrentRunner* runner = new nsConcurrentRunner(); - - for (PRUint32 i = 0; i < nRequests; i++) { - rv = pool->DispatchRequest(runner); - } - rv = pool->Shutdown(); - return rv; -} - class nsStressRunner : public nsIRunnable { public: NS_DECL_ISUPPORTS @@ -357,20 +271,6 @@ main(int argc, char** argv) } else { rv = TestThreads(); if (NS_FAILED(rv)) return -1; - - rv = TestThreadPools(1, 4, 100); - if (NS_FAILED(rv)) return -1; - - rv = TestThreadPools(4, 16, 100); - if (NS_FAILED(rv)) return -1; - - // this test delays between each request to give threads a chance to - // decide to go away: - rv = TestThreadPools(4, 8, 32, PR_MillisecondsToInterval(1000)); - if (NS_FAILED(rv)) return -1; - - rv = TestThreadPoolsConcurrent(4, 32, 1000); - if (NS_FAILED(rv)) return -1; } rv = NS_ShutdownXPCOM(nsnull); diff --git a/mozilla/xpcom/threads/Makefile.in b/mozilla/xpcom/threads/Makefile.in index 0fc43ba6bcd..b209cf8970c 100644 --- a/mozilla/xpcom/threads/Makefile.in +++ b/mozilla/xpcom/threads/Makefile.in @@ -61,11 +61,11 @@ EXPORTS = \ XPIDLSRCS = \ nsIThread.idl \ - nsIThreadPool.idl \ nsITimer.idl \ nsITimerInternal.idl \ nsITimerManager.idl \ nsIRunnable.idl \ + nsIEventTarget.idl \ nsIEventQueue.idl \ nsIEventQueueService.idl \ nsIProcess.idl \ diff --git a/mozilla/xpcom/threads/nsEventQueue.cpp b/mozilla/xpcom/threads/nsEventQueue.cpp index 1965f3fee6b..7d1b2a7a5f6 100644 --- a/mozilla/xpcom/threads/nsEventQueue.cpp +++ b/mozilla/xpcom/threads/nsEventQueue.cpp @@ -189,8 +189,9 @@ nsEventQueueImpl::InitFromPLQueue(PLEventQueue* aQueue) } /* nsISupports interface implementation... */ -NS_IMPL_THREADSAFE_ISUPPORTS2(nsEventQueueImpl, +NS_IMPL_THREADSAFE_ISUPPORTS3(nsEventQueueImpl, nsIEventQueue, + nsIEventTarget, nsPIEventQueueChain) /* nsIEventQueue interface implementation... */ @@ -240,8 +241,7 @@ nsEventQueueImpl::InitEvent(PLEvent* aEvent, } - -NS_IMETHODIMP_(PRStatus) +NS_IMETHODIMP nsEventQueueImpl::PostEvent(PLEvent* aEvent) { if (!mAcceptingEvents) { @@ -251,7 +251,7 @@ nsEventQueueImpl::PostEvent(PLEvent* aEvent) (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents)); ++gEventQueueLogCount; #endif - PRStatus rv = PR_FAILURE; + nsresult rv = NS_ERROR_FAILURE; NS_ASSERTION(mElderQueue, "event dropped because event chain is dead"); if (mElderQueue) { nsCOMPtr elder(do_QueryInterface(mElderQueue)); @@ -265,7 +265,7 @@ nsEventQueueImpl::PostEvent(PLEvent* aEvent) ("EventQueue: Posting event [queue=%lx]", (long)mEventQueue)); ++gEventQueueLogCount; #endif - return PL_PostEvent(mEventQueue, aEvent); + return PL_PostEvent(mEventQueue, aEvent) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP @@ -340,7 +340,7 @@ nsEventQueueImpl::GetPLEventQueue(PLEventQueue** aEventQueue) } NS_IMETHODIMP -nsEventQueueImpl::IsQueueOnCurrentThread(PRBool *aResult) +nsEventQueueImpl::IsOnCurrentThread(PRBool *aResult) { *aResult = PL_IsQueueOnCurrentThread( mEventQueue ); return NS_OK; diff --git a/mozilla/xpcom/threads/nsEventQueue.h b/mozilla/xpcom/threads/nsEventQueue.h index 515c692d261..63a6abebd22 100644 --- a/mozilla/xpcom/threads/nsEventQueue.h +++ b/mozilla/xpcom/threads/nsEventQueue.h @@ -47,9 +47,8 @@ public: nsEventQueueImpl(); virtual ~nsEventQueueImpl(); - // nsISupports interface... NS_DECL_ISUPPORTS - // nsIEventQueue interface... + NS_DECL_NSIEVENTTARGET NS_DECL_NSIEVENTQUEUE // Helpers diff --git a/mozilla/xpcom/threads/nsIEventQueue.idl b/mozilla/xpcom/threads/nsIEventQueue.idl index e478d6f43ea..2153e47e406 100644 --- a/mozilla/xpcom/threads/nsIEventQueue.idl +++ b/mozilla/xpcom/threads/nsIEventQueue.idl @@ -42,11 +42,10 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsISupports.idl" +#include "nsIEventTarget.idl" %{C++ #include "prthread.h" -#include "plevent.h" // {13D86C61-00A9-11d3-9F2A-00400553EEF0} #define NS_EVENTQUEUE_CID \ @@ -60,7 +59,6 @@ // some forward decls // [ptr] native PLEventQueuePtr(PLEventQueue); -[ptr] native PLEventPtr(PLEvent); [ptr] native PRThreadPtr(PRThread); native PRStatus(PRStatus); [ref] native PRBoolRef(PRBool); @@ -68,14 +66,13 @@ native PLHandleEventProc(PLHandleEventProc); native PLDestroyEventProc(PLDestroyEventProc); [scriptable, uuid(176AFB41-00A4-11d3-9F2A-00400553EEF0)] -interface nsIEventQueue : nsISupports +interface nsIEventQueue : nsIEventTarget { [noscript] void initEvent(in PLEventPtr aEvent, in voidPtr owner, in PLHandleEventProc handler, in PLDestroyEventProc destructor); - [notxpcom] PRStatus postEvent(in PLEventPtr aEvent); [noscript] void postSynchronousEvent(in PLEventPtr aEvent, out voidPtr aResult); @@ -101,7 +98,6 @@ interface nsIEventQueue : nsISupports [noscript] void revokeEvents(in voidPtr owner); [noscript] PLEventQueuePtr getPLEventQueue(); - boolean isQueueOnCurrentThread(); boolean isQueueNative(); // effectively kill the queue. warning: the queue is allowed to delete diff --git a/mozilla/xpcom/threads/nsIEventTarget.idl b/mozilla/xpcom/threads/nsIEventTarget.idl new file mode 100644 index 00000000000..3b19d9ca0a7 --- /dev/null +++ b/mozilla/xpcom/threads/nsIEventTarget.idl @@ -0,0 +1,67 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +%{C++ +#include "plevent.h" +%} + +[ptr] native PLEventPtr(PLEvent); + +/** + * nsIEventTarget + * + * This interface is used to dispatch events to a particular thread. In many + * cases the event target also supports nsIEventQueue. + */ +[scriptable, uuid(ea99ad5b-cc67-4efb-97c9-2ef620a59f2a)] +interface nsIEventTarget : nsISupports +{ + /** + * Method for posting an asynchronous event to the event target. If this + * method succeeds, then the event will be dispatched on the target thread. + * + * @param aEvent + * The event to dispatched. + */ + [noscript] void postEvent(in PLEventPtr aEvent); + + /** + * This method returns true if the event target is the current thread. + */ + boolean isOnCurrentThread(); +}; diff --git a/mozilla/xpcom/threads/nsIThreadPool.idl b/mozilla/xpcom/threads/nsIThreadPool.idl deleted file mode 100644 index 6fae528be3f..00000000000 --- a/mozilla/xpcom/threads/nsIThreadPool.idl +++ /dev/null @@ -1,82 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: NPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Netscape Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/NPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the NPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the NPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "nsISupports.idl" -%{C++ -#include "prthread.h" - -#define NS_THREADPOOL_CID \ -{ /* CC530631-7808-11d3-A181-0050041CAF44 */ \ - 0xcc530631, \ - 0x7808, \ - 0x11d3, \ - {0xa1, 0x81, 0x00, 0x50, 0x04, 0x1c, 0xaf, 0x44} \ -} - -#define NS_THREADPOOL_CONTRACTID "@mozilla.org/thread-pool;1" -#define NS_THREADPOOL_CLASSNAME "Thread Pool" -#if 0 -%} -typedef PRUint32 PRThreadPriority; -typedef PRUint32 PRThreadScope; -%{C++ -#endif -%} - -interface nsIRunnable; - -[scriptable, uuid(0c728db0-6887-11d3-9382-00104ba0fd40)] -interface nsIThreadPool : nsISupports -{ - void DispatchRequest(in nsIRunnable runnable); - void ProcessPendingRequests(); - void Shutdown(); - void Init(in unsigned long aPoolMinThreadCount, - in unsigned long aPoolMaxThreadCount, - in unsigned long aThreadStackSize, - in PRThreadPriority aThreadPriority, - in PRThreadScope aThreadScope); -}; - -%{C++ -extern NS_COM nsresult -NS_NewThreadPool(nsIThreadPool* *result, - PRUint32 minThreads, PRUint32 maxThreads, - PRUint32 stackSize = 0, - PRThreadPriority priority = PR_PRIORITY_NORMAL, - PRThreadScope scope = PR_GLOBAL_THREAD); -%} diff --git a/mozilla/xpcom/threads/nsThread.cpp b/mozilla/xpcom/threads/nsThread.cpp index 54ca71c56ef..ff0ce1b217e 100644 --- a/mozilla/xpcom/threads/nsThread.cpp +++ b/mozilla/xpcom/threads/nsThread.cpp @@ -443,470 +443,3 @@ nsThread::Shutdown() } //////////////////////////////////////////////////////////////////////////////// - -/* nsThreadPoolBusyBody implements an increment/decrement of - nsThreadPool's mBusyThreads member variable -*/ -class nsThreadPoolBusyBody { -public: - nsThreadPoolBusyBody(nsThreadPool *aPool); - ~nsThreadPoolBusyBody(); - -private: - void* operator new(size_t) CPP_THROW_NEW { return 0; } // local variable, only! - nsThreadPool *mPool; -}; - -inline nsThreadPoolBusyBody::nsThreadPoolBusyBody(nsThreadPool *aPool) { - - nsAutoLock lock(aPool->mLock); -#ifdef DEBUG - PRUint32 threadCount; - if (NS_SUCCEEDED(aPool->mThreads->Count(&threadCount))) - NS_ASSERTION(aPool->mBusyThreads < threadCount, "thread busy count exceeded thread count"); -#endif - aPool->mBusyThreads++; - mPool = aPool; -} - -inline nsThreadPoolBusyBody::~nsThreadPoolBusyBody() { - - nsAutoLock lock(mPool->mLock); - NS_ASSERTION(mPool->mBusyThreads > 0, "thread busy count < 0"); - mPool->mBusyThreads--; -} - -//////////////////////////////////////////////////////////////////////////////// - -nsThreadPool::nsThreadPool() - : mLock(nsnull), mThreadExit(nsnull), mPendingRequestAdded(nsnull), - mPendingRequestsAtZero(nsnull), mMinThreads(0), mMaxThreads(0), - mBusyThreads(0), mShuttingDown(PR_TRUE) -{ -} - -nsThreadPool::~nsThreadPool() -{ - if (mThreads) { - Shutdown(); - } - - if (mLock) - PR_DestroyLock(mLock); - if (mThreadExit) - PR_DestroyCondVar(mThreadExit); - if (mPendingRequestAdded) - PR_DestroyCondVar(mPendingRequestAdded); - if (mPendingRequestsAtZero) - PR_DestroyCondVar(mPendingRequestsAtZero); -} - -NS_IMPL_THREADSAFE_ISUPPORTS1(nsThreadPool, nsIThreadPool) - -NS_IMETHODIMP -nsThreadPool::DispatchRequest(nsIRunnable* runnable) -{ - nsresult rv; - nsAutoLock lock(mLock); - -#if defined(PR_LOGGING) - nsCOMPtr th; - nsIThread::GetCurrent(getter_AddRefs(th)); -#endif - - NS_ASSERTION(mMinThreads > 0, "forgot to call Init"); - if (mShuttingDown) { - rv = NS_ERROR_FAILURE; - } - else { - PRUint32 requestCnt, threadCount; - - requestCnt = mPendingRequests.Count(); - - rv = mThreads->Count(&threadCount); - if (NS_FAILED(rv)) goto exit; - - if (requestCnt >= threadCount-mBusyThreads && threadCount < mMaxThreads) { - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p: %d threads in pool, max = %d, requests = %d, creating new thread...\n", - th.get(), threadCount, mMaxThreads, requestCnt)); - rv = AddThread(); - if (NS_FAILED(rv)) goto exit; - } - - rv = mPendingRequests.AppendObject(runnable) ? NS_OK : NS_ERROR_FAILURE; - if (NS_SUCCEEDED(rv)) { - if (PR_FAILURE == PR_NotifyCondVar(mPendingRequestAdded)) - goto exit; - } - } - -exit: - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p dispatched %p status %x\n", th.get(), runnable, rv)); - return rv; -} - -nsresult -nsThreadPool::RemoveThread(nsIThread* currentThread) -{ - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p being removed\n", - currentThread)); - - nsresult rv = mThreads->DeleteLastElement(currentThread); - - PR_NotifyCondVar(mThreadExit); - return rv; -} - -void -nsThreadPool::RequestDone(nsIRunnable* request) -{ - nsAutoLock lock(mLock); - mRunningRequests.RemoveObject(request); -} - -nsIRunnable* -nsThreadPool::GetRequest(nsIThread* currentThread) -{ - nsresult rv = NS_OK; - nsCOMPtr request; - nsAutoLock lock(mLock); - - PRInt32 requestCnt; - while (PR_TRUE) { - requestCnt = mPendingRequests.Count(); - - if (requestCnt > 0) { - PRInt32 pendingThread; - PRInt32 runningPos; - for (pendingThread = 0; pendingThread < requestCnt; ++pendingThread) { - request = mPendingRequests.ObjectAt(pendingThread); - - if (!request) { - NS_ERROR("Bad request in pending request list"); - // Make it look like we just found no runnable requests - pendingThread = requestCnt; - break; - } - - // check to see if the request is not running. - runningPos = mRunningRequests.IndexOf(request); - if (runningPos == -1) - break; - - } - - if (pendingThread < requestCnt) { - // Found something to run - PRBool removed = mPendingRequests.RemoveObjectAt(pendingThread); - NS_ASSERTION(removed, "nsCOMArray broken"); - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p got request %p\n", - currentThread, request.get())); - - if (removed && requestCnt == 1) - PR_NotifyCondVar(mPendingRequestsAtZero); - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p got request %p\n", - currentThread, request.get())); - - mRunningRequests.AppendObject(request); - return request; - } - } - - if (mShuttingDown) - break; - - // no requests, and we're not shutting down yet... - // if we have more than the minimum required threads already then - // then we may be able to go away. - PRUint32 threadCnt; - rv = mThreads->Count(&threadCnt); - if (NS_FAILED(rv)) break; - - if (threadCnt > mMinThreads) { - // to avoid multiple thread spawns/exits, we need to - // wait for some period of time while waiting for any - // additional requests. If this this wait yeilds no - // request, then we can exit. - // - // TODO: determine what the optimal timeout value is. - // For now, just use 5 seconds. - - PRIntervalTime interval = PR_SecondsToInterval(5); - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p waiting for %d seconds before exiting (%d threads in pool)\n", - currentThread, interval, threadCnt)); - - (void) PR_WaitCondVar( mPendingRequestAdded, interval); - - requestCnt = mPendingRequests.Count(); - if (requestCnt == 0) { - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p: %d threads in pool, min = %d, exiting...\n", - currentThread, threadCnt, mMinThreads)); - RemoveThread(currentThread); - return nsnull; // causes nsThreadPoolRunnable::Run to quit - } - } - else - { - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p waiting (%d threads in pool)\n", - currentThread, threadCnt)); - - (void)PR_WaitCondVar(mPendingRequestAdded, PR_INTERVAL_NO_TIMEOUT); - } - } - // no requests, we are going to dump the thread. - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p -- no more requests, exiting...\n", - currentThread)); - RemoveThread(currentThread); - return nsnull; -} - -NS_METHOD -nsThreadPool::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) -{ - nsThreadPool* pool = new nsThreadPool(); - if (!pool) return NS_ERROR_OUT_OF_MEMORY; - nsresult rv = pool->QueryInterface(aIID, aResult); - if (NS_FAILED(rv)) delete pool; - return rv; -} - -NS_IMETHODIMP -nsThreadPool::ProcessPendingRequests() -{ - while (mPendingRequests.Count() != 0) { - (void)PR_WaitCondVar(mPendingRequestsAtZero, PR_INTERVAL_NO_TIMEOUT); - } - return NS_OK; -} - -PRBool -nsThreadPool::InterruptThreads(nsISupports* aElement, void *aData) -{ - nsCOMPtr thread = do_QueryInterface(aElement); - NS_ASSERTION(thread, "bad thread in array"); - (void) thread->Interrupt(); - return PR_TRUE; -} - -NS_IMETHODIMP -nsThreadPool::Shutdown() -{ - nsresult rv = NS_OK; - PRUint32 count = 0; - -#if defined(PR_LOGGING) - nsCOMPtr th; - nsIThread::GetCurrent(getter_AddRefs(th)); -#endif - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p shutting down\n", th.get())); - - nsAutoLock lock(mLock); - if (mShuttingDown) { - NS_ERROR("Bug 166371 - Was Shutdown() called more than once," - " or did someone forget to call Init()?"); - return NS_OK; - } - mShuttingDown = PR_TRUE; - rv = ProcessPendingRequests(); - NS_ASSERTION(NS_SUCCEEDED(rv), "ProcessPendingRequests failed"); - // keep trying... don't bail with an error here - - // fix Add assert that there are no more requests to be handled. - - // then interrupt the threads - rv = mThreads->EnumerateForwards(nsThreadPool::InterruptThreads, nsnull); - - NS_ASSERTION(NS_SUCCEEDED(rv), "Interruption failed"); - if (NS_FAILED(rv)) return rv; - - while (PR_TRUE) { - rv = mThreads->Count(&count); - NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); - if (NS_FAILED(rv)) return rv; - if (count == 0 ) - break; - PR_WaitCondVar(mThreadExit, PR_INTERVAL_NO_TIMEOUT); - } - - mThreads = nsnull; - return rv; -} - -NS_IMETHODIMP -nsThreadPool::Init(PRUint32 minThreadCount, - PRUint32 maxThreadCount, - PRUint32 stackSize, - PRThreadPriority priority, - PRThreadScope scope) -{ - nsresult rv; - - mStackSize = stackSize; - mPriority = priority; - mScope = scope; - NS_ASSERTION(minThreadCount > 0 && minThreadCount <= maxThreadCount, "bad min/max values"); - mMinThreads = minThreadCount; - mMaxThreads = maxThreadCount; - - mShuttingDown = PR_FALSE; - - rv = NS_NewISupportsArray(getter_AddRefs(mThreads)); - if (NS_FAILED(rv)) return rv; - - mLock = PR_NewLock(); - if (mLock == nsnull) - goto cleanup; - - mPendingRequestAdded = PR_NewCondVar(mLock); - if (mPendingRequestAdded == nsnull) - goto cleanup; - - mThreadExit = PR_NewCondVar(mLock); - if (mThreadExit == nsnull) - goto cleanup; - - mPendingRequestsAtZero = PR_NewCondVar(mLock); - if (mPendingRequestsAtZero == nsnull) - goto cleanup; - - return NS_OK; - - cleanup: - if (mLock) { - PR_DestroyLock(mLock); - mLock = nsnull; - } - if (mThreadExit) { - PR_DestroyCondVar(mThreadExit); - mThreadExit = nsnull; - } - if (mPendingRequestAdded) { - PR_DestroyCondVar(mPendingRequestAdded); - mPendingRequestAdded = nsnull; - } - if (mPendingRequestsAtZero) { - PR_DestroyCondVar(mPendingRequestsAtZero); - mPendingRequestsAtZero = nsnull; - } - return NS_ERROR_OUT_OF_MEMORY; -} - -nsresult -nsThreadPool::AddThread() -{ - nsresult rv; - -#if defined(DEBUG) || defined(FORCE_PR_LOG) - PRUint32 cnt; - rv = mThreads->Count(&cnt); - if (NS_FAILED(rv)) return rv; -#endif -#ifdef DEBUG - if (cnt >= mMaxThreads) - return NS_ERROR_FAILURE; -#endif - - nsThreadPoolRunnable* runnable = new nsThreadPoolRunnable(this); - if (runnable == nsnull) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(runnable); - - nsCOMPtr thread; - - rv = NS_NewThread(getter_AddRefs(thread), - runnable, - mStackSize, - PR_UNJOINABLE_THREAD, - mPriority, - mScope); - - // Let the thread own the runnable. - NS_RELEASE(runnable); - if (NS_FAILED(rv)) return rv; - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool adding new thread %p (%d total)\n", - thread.get(), cnt + 1)); - - rv = mThreads->AppendElement(thread) ? NS_OK : NS_ERROR_FAILURE; - return rv; -} - -NS_COM nsresult -NS_NewThreadPool(nsIThreadPool* *result, - PRUint32 minThreads, PRUint32 maxThreads, - PRUint32 stackSize, - PRThreadPriority priority, - PRThreadScope scope) -{ - nsresult rv; - nsThreadPool* pool = new nsThreadPool(); - if (pool == nsnull) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(pool); - - rv = pool->Init(minThreads, maxThreads, stackSize, priority, scope); - if (NS_FAILED(rv)) { - NS_RELEASE(pool); - return rv; - } - - *result = pool; - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// - -nsThreadPoolRunnable::nsThreadPoolRunnable(nsThreadPool* pool) - : mPool(pool) -{ -} - -nsThreadPoolRunnable::~nsThreadPoolRunnable() -{ -} - -NS_IMPL_THREADSAFE_ISUPPORTS1(nsThreadPoolRunnable, nsIRunnable) - -NS_IMETHODIMP -nsThreadPoolRunnable::Run() -{ - nsresult rv = NS_OK; - nsCOMPtr request; - - nsCOMPtr currentThread; - nsIThread::GetCurrent(getter_AddRefs(currentThread)); - - while ((request = mPool->GetRequest(currentThread)) != nsnull) { - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p running %p\n", - currentThread.get(), request.get())); - nsThreadPoolBusyBody bumpBusyCount(mPool); - rv = request->Run(); - NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed"); - - // let the pool know that the request has finished running. - mPool->RequestDone(request); - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p completed %p status=%x\n", - currentThread.get(), request.get(), rv)); - } - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThreadPool thread %p quitting %p\n", - currentThread.get(), this)); - return rv; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/xpcom/threads/nsThread.h b/mozilla/xpcom/threads/nsThread.h index e8dae937581..d0f051a41b3 100644 --- a/mozilla/xpcom/threads/nsThread.h +++ b/mozilla/xpcom/threads/nsThread.h @@ -35,14 +35,8 @@ #include "nsIRunnable.h" #include "nsIThread.h" -#include "nsIThreadPool.h" -#include "nsISupportsArray.h" -#include "nsCOMArray.h" -#include "prcvar.h" #include "nsCOMPtr.h" -class nsThreadPoolBusyBody; - class nsThread : public nsIThread { public: @@ -77,71 +71,4 @@ protected: //////////////////////////////////////////////////////////////////////////////// -class nsThreadPool : public nsIThreadPool -{ -friend class nsThreadPoolBusyBody; -public: - NS_DECL_ISUPPORTS - - // nsIThreadPool methods: - NS_DECL_NSITHREADPOOL - - // nsThreadPool methods: - nsThreadPool(); - virtual ~nsThreadPool(); - - nsIRunnable* GetRequest(nsIThread* currentThread); - void RequestDone(nsIRunnable *request); - - static PRBool InterruptThreads(nsISupports* aElement, - void *aData); - - static NS_METHOD - Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); - -protected: - nsresult AddThread(); - nsresult RemoveThread(nsIThread* currentThread); - - nsCOMPtr mThreads; - nsCOMArray mPendingRequests; - nsCOMArray mRunningRequests; - - PRLock* mLock; - PRCondVar* mThreadExit; - PRCondVar* mPendingRequestAdded; - PRCondVar* mPendingRequestsAtZero; - - - PRUint32 mStackSize; - PRThreadPriority mPriority; - PRThreadScope mScope; - - PRUint32 mMinThreads; - PRUint32 mMaxThreads; - PRUint32 mBusyThreads; - PRBool mShuttingDown; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class nsThreadPoolRunnable : public nsIRunnable -{ -public: - NS_DECL_ISUPPORTS - - // nsIRunnable methods: - NS_DECL_NSIRUNNABLE - - // nsThreadPoolRunnable methods: - nsThreadPoolRunnable(nsThreadPool* pool); - virtual ~nsThreadPoolRunnable(); - -protected: - nsCOMPtr mPool; - -}; - -//////////////////////////////////////////////////////////////////////////////// - #endif // nsThread_h__ diff --git a/mozilla/xpfe/appshell/src/nsAppShellService.cpp b/mozilla/xpfe/appshell/src/nsAppShellService.cpp index 59298995b60..2fdc1703e72 100644 --- a/mozilla/xpfe/appshell/src/nsAppShellService.cpp +++ b/mozilla/xpfe/appshell/src/nsAppShellService.cpp @@ -624,27 +624,24 @@ nsAppShellService::Quit(PRUint32 aFerocity) rv = svc->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(queue)); if (NS_SUCCEEDED(rv)) { - ExitEvent* event = new ExitEvent; + PLEvent* event = new PLEvent; if (event) { - PL_InitEvent(NS_REINTERPRET_CAST(PLEvent*, event), - nsnull, + NS_ADDREF_THIS(); + PL_InitEvent(event, + this, HandleExitEvent, DestroyExitEvent); - event->mService = this; - NS_ADDREF(event->mService); - + // XXXdf why enter the queue's critical section? rv = queue->EnterMonitor(); if (NS_SUCCEEDED(rv)) - rv = queue->PostEvent(NS_REINTERPRET_CAST(PLEvent*, event)); + rv = queue->PostEvent(event); if (NS_SUCCEEDED(rv)) postedExitEvent = PR_TRUE; queue->ExitMonitor(); - if (NS_FAILED(rv)) { - NS_RELEASE(event->mService); - delete event; - } + if (NS_FAILED(rv)) + PL_DestroyEvent(event); } else rv = NS_ERROR_OUT_OF_MEMORY; } @@ -661,13 +658,14 @@ nsAppShellService::Quit(PRUint32 aFerocity) void* PR_CALLBACK nsAppShellService::HandleExitEvent(PLEvent* aEvent) { - ExitEvent* event = NS_REINTERPRET_CAST(ExitEvent*, aEvent); + nsAppShellService *service = + NS_REINTERPRET_CAST(nsAppShellService*, aEvent->owner); // Tell the appshell to exit - event->mService->mAppShell->Exit(); + service->mAppShell->Exit(); // We're done "shutting down". - event->mService->mShuttingDown = PR_FALSE; + service->mShuttingDown = PR_FALSE; return nsnull; } @@ -675,9 +673,10 @@ nsAppShellService::HandleExitEvent(PLEvent* aEvent) void PR_CALLBACK nsAppShellService::DestroyExitEvent(PLEvent* aEvent) { - ExitEvent* event = NS_REINTERPRET_CAST(ExitEvent*, aEvent); - NS_RELEASE(event->mService); - delete event; + nsAppShellService *service = + NS_REINTERPRET_CAST(nsAppShellService*, aEvent->owner); + NS_RELEASE(service); + delete aEvent; } /* diff --git a/mozilla/xpfe/appshell/src/nsAppShellService.h b/mozilla/xpfe/appshell/src/nsAppShellService.h index 28fe6d23444..91e3ea36bf0 100644 --- a/mozilla/xpfe/appshell/src/nsAppShellService.h +++ b/mozilla/xpfe/appshell/src/nsAppShellService.h @@ -90,13 +90,8 @@ protected: PRPackedBool mAttemptingQuit; // Quit(eAttemptQuit) still trying // A "last event" that is used to flush the appshell's event queue. - struct ExitEvent { - PLEvent mEvent; - nsAppShellService* mService; - }; - - static void* PR_CALLBACK HandleExitEvent(PLEvent* aEvent); - static void PR_CALLBACK DestroyExitEvent(PLEvent* aEvent); + PR_STATIC_CALLBACK(void*) HandleExitEvent(PLEvent* aEvent); + PR_STATIC_CALLBACK(void) DestroyExitEvent(PLEvent* aEvent); private: nsresult CheckAndRemigrateDefunctProfile();