зеркало из https://github.com/mozilla/gecko-dev.git
fixes bug 210125 "need to be able to AsyncWait for closure only" r=dougt sr=bzbarsky
This commit is contained in:
Родитель
c881ba323d
Коммит
77642ae698
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<nsIRequest> m_WriteRequest;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mAsyncOutStream;
|
||||
nsCOMPtr<nsIOutputStreamNotify> mProvider;
|
||||
nsCOMPtr<nsIEventQueue> mProviderEventQ;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mAsyncOutStream;
|
||||
nsCOMPtr<nsIOutputStreamCallback> mProvider;
|
||||
nsCOMPtr<nsIEventQueue> 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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<nsIAsyncStreamCopier> 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);
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ REQUIRES = xpcom \
|
|||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsIOThreadPool.cpp \
|
||||
nsTransportUtils.cpp \
|
||||
nsAsyncStreamCopier.cpp \
|
||||
nsAsyncStreamListener.cpp \
|
||||
nsBufferedStreams.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<nsIRequestObserver> observer;
|
||||
nsCOMPtr<nsISupports> 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<nsIAsyncInputStream> asyncSource = do_QueryInterface(mSource);
|
||||
if (asyncSource)
|
||||
asyncSource->CloseWithStatus(status);
|
||||
else
|
||||
mSource->Close();
|
||||
|
||||
nsCOMPtr<nsIAsyncOutputStream> 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);
|
||||
}
|
||||
|
|
|
@ -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<nsIInputStream> mSource;
|
||||
nsCOMPtr<nsIAsyncInputStream> 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<nsIInputStreamNotify> 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<nsIOutputStream> mSink;
|
||||
nsCOMPtr<nsIAsyncOutputStream> 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<nsIOutputStreamNotify> mNotify;
|
||||
};
|
||||
|
||||
private:
|
||||
nsInputWrapper mInput;
|
||||
nsOutputWrapper mOutput;
|
||||
|
||||
static void OnAsyncCopyComplete(void *, nsresult);
|
||||
|
||||
nsCOMPtr<nsIInputStream> mSource;
|
||||
nsCOMPtr<nsIOutputStream> mSink;
|
||||
|
||||
nsCOMPtr<nsIRequestObserver> mObserver;
|
||||
nsCOMPtr<nsISupports> mObserverContext;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
|
||||
PRLock *mLock;
|
||||
|
||||
nsAsyncCopyMode mMode;
|
||||
PRUint32 mChunkSize;
|
||||
nsresult mStatus;
|
||||
PRBool mIsPending;
|
||||
PRPackedBool mIsPending;
|
||||
};
|
||||
|
||||
#endif // !nsAsyncStreamCopier_h__
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<nsISocketTransportService> mSocketTransportService;
|
||||
nsCOMPtr<nsIStreamTransportService> mStreamTransportService;
|
||||
nsCOMPtr<nsIDNSService> mDNSService;
|
||||
nsCOMPtr<nsIProtocolProxyService> mProxyService;
|
||||
nsCOMPtr<nsIEventQueueService> mEventQueueService;
|
||||
|
|
|
@ -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<nsIObserverService> 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; i<MAX_THREADS; ++i)
|
||||
NS_ASSERTION(mThreads[i] == nsnull, "leaking thread");
|
||||
#endif
|
||||
|
||||
if (mCV)
|
||||
PR_DestroyCondVar(mCV);
|
||||
if (mLock)
|
||||
PR_DestroyLock(mLock);
|
||||
}
|
||||
|
||||
void
|
||||
nsIOThreadPool::Shutdown()
|
||||
{
|
||||
LOG(("nsIOThreadPool::Shutdown\n"));
|
||||
|
||||
// synchronize with background threads...
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
mShutdown = PR_TRUE;
|
||||
PR_NotifyAllCondVar(mCV);
|
||||
}
|
||||
|
||||
for (int i=0; i<MAX_THREADS; ++i) {
|
||||
if (mThreads[i]) {
|
||||
PR_JoinThread(mThreads[i]);
|
||||
mThreads[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nsIOThreadPool::GetCurrentThreadIndex()
|
||||
{
|
||||
PRThread *current = PR_GetCurrentThread();
|
||||
|
||||
for (int i=0; i<MAX_THREADS; ++i)
|
||||
if (current == mThreads[i])
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOThreadPool::PostEvent(PLEvent *event)
|
||||
{
|
||||
LOG(("nsIOThreadPool::PostEvent [event=%p]\n", event));
|
||||
|
||||
nsAutoLock lock(mLock);
|
||||
|
||||
if (mShutdown)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
PR_APPEND_LINK(&event->link, &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; i<MAX_THREADS; ++i) {
|
||||
if (!mThreads[i]) {
|
||||
NS_ADDREF_THIS(); // the thread owns a reference to us
|
||||
mThreads[i] = PR_CreateThread(PR_USER_THREAD,
|
||||
ThreadFunc,
|
||||
this,
|
||||
PR_PRIORITY_NORMAL,
|
||||
PR_GLOBAL_THREAD,
|
||||
PR_JOINABLE_THREAD,
|
||||
0);
|
||||
if (!mThreads[i])
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOThreadPool::IsOnCurrentThread(PRBool *result)
|
||||
{
|
||||
// fudging this a bit since we actually cover several threads...
|
||||
*result = (GetCurrentThreadIndex() != -1);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsIOThreadPool::Observe(nsISupports *, const char *topic, const PRUnichar *)
|
||||
{
|
||||
NS_ASSERTION(strcmp(topic, "xpcom-shutdown") == 0, "unexpected topic");
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsIOThreadPool::ThreadFunc(void *arg)
|
||||
{
|
||||
nsIOThreadPool *pool = (nsIOThreadPool *) arg;
|
||||
|
||||
LOG(("entering ThreadFunc\n"));
|
||||
|
||||
// XXX not using a nsAutoLock here because we need to temporarily unlock
|
||||
// and relock in the inner loop. nsAutoLock has a bug, causing it to warn
|
||||
// of a bogus deadlock, which makes us avoid it here.
|
||||
PR_Lock(pool->mLock);
|
||||
|
||||
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;
|
||||
}
|
|
@ -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__
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<nsISupports> mParam;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
//#define TEST_CONNECT_ERRORS
|
||||
#ifdef TEST_CONNECT_ERRORS
|
||||
#include <stdlib.h>
|
||||
|
@ -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<nsIInputStreamNotify> notify;
|
||||
nsCOMPtr<nsIInputStreamCallback> 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<nsIInputStreamNotify> temp;
|
||||
nsCOMPtr<nsIInputStreamCallback> 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<nsIOutputStreamNotify> notify;
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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<nsIOutputStreamNotify> temp;
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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<nsIDNSService> 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<nsIDNSService> 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<nsITransportEventSink> 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;
|
||||
}
|
||||
|
|
|
@ -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<nsIInputStreamNotify> mNotify;
|
||||
PRUint32 mByteCount;
|
||||
nsresult mCondition;
|
||||
nsCOMPtr<nsIInputStreamCallback> 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<nsIOutputStreamNotify> mNotify;
|
||||
PRUint32 mByteCount;
|
||||
nsresult mCondition;
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<nsISocketEventHandler> 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<nsISocketEventHandler> mHandler;
|
||||
PRUint32 mMsg;
|
||||
struct PendingSocket *mNext;
|
||||
};
|
||||
PendingSocket *mPendingQHead;
|
||||
PendingSocket *mPendingQTail;
|
||||
PRCList mPendingSocketQ; // list of PLEvent objects
|
||||
};
|
||||
|
||||
extern nsSocketTransportService *gSocketTransportService;
|
||||
|
|
|
@ -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<nsIAsyncInputStream> mPipeIn;
|
||||
|
||||
nsCOMPtr<nsIAsyncOutputStream> mPipeOut;
|
||||
// while the copy is active, these members may only be accessed from the
|
||||
// nsIInputStream implementation.
|
||||
nsCOMPtr<nsITransportEventSink> mEventSink;
|
||||
nsCOMPtr<nsIInputStream> 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<nsISeekableStream> 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<nsIEventTarget> 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<nsIAsyncOutputStream> 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<nsISeekableStream> 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<nsIAsyncOutputStream> mPipeOut;
|
||||
|
||||
nsCOMPtr<nsIAsyncInputStream> mPipeIn;
|
||||
// while the copy is active, these members may only be accessed from the
|
||||
// nsIOutputStream implementation.
|
||||
nsCOMPtr<nsITransportEventSink> mEventSink;
|
||||
nsCOMPtr<nsIOutputStream> 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<nsISeekableStream> 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<nsIEventTarget> 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<nsIAsyncInputStream> 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<nsISeekableStream> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<nsIThreadPool> mPool;
|
||||
PRLock *mLock;
|
||||
nsStreamTransportService() {}
|
||||
virtual ~nsStreamTransportService() {}
|
||||
};
|
||||
|
|
|
@ -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 <darinf@us.ibm.com>
|
||||
*
|
||||
* 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<nsIEventTarget> 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<nsITransport> 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;
|
||||
}
|
|
@ -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 <darinf@us.ibm.com>
|
||||
*
|
||||
* 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__
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -438,6 +438,7 @@ nsFileChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
|
|||
//
|
||||
nsCOMPtr<nsIAsyncStreamCopier> 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;
|
||||
|
|
|
@ -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<nsIEventTarget> stEventTarget = do_GetService(kSocketTransportServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) return FTP_ERROR;
|
||||
|
||||
nsCOMPtr<nsIAsyncStreamCopier> 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;
|
||||
|
|
|
@ -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<nsIProxyObjectManager> mgr;
|
||||
gHttpHandler->GetProxyObjectManager(getter_AddRefs(mgr));
|
||||
if (mgr) {
|
||||
nsCOMPtr<nsIRequestObserver> 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<nsIRequestObserver> 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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -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<nsISocketTransportService> mSTS; // cached reference to STS
|
||||
PRInt32 mRef;
|
||||
PRMonitor *mMonitor;
|
||||
nsCOMPtr<nsIEventTarget> 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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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<nsIIOService> mIOService;
|
||||
nsCOMPtr<nsIProxyObjectManager> mProxyMgr;
|
||||
nsCOMPtr<nsIEventQueueService> mEventQueueService;
|
||||
nsCOMPtr<nsIStreamConverterService> mStreamConvSvc;
|
||||
nsCOMPtr<nsIObserverService> mObserverService;
|
||||
|
|
|
@ -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<nsIEventTarget> 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<nsISocketTransportService> 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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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<nsIEventTarget> 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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<nsIAsyncInputStream> mInputStream;
|
||||
nsCOMPtr<nsIInputStreamNotify> mInputNotify;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
|
||||
nsCOMPtr<nsIOutputStreamNotify> mOutputNotify;
|
||||
nsCOMPtr<nsIAsyncInputStream> mInputStream;
|
||||
nsCOMPtr<nsIInputStreamCallback> mInputCallback;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mOutputStream;
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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<nsIInputStreamNotify> mNotify;
|
||||
nsCOMPtr<nsIInputStreamCallback> 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<nsIOutputStreamNotify> mNotify;
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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<nsIInputStreamNotify> proxy;
|
||||
if (eventQ) {
|
||||
nsCOMPtr<nsIInputStreamCallback> 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<nsIOutputStreamNotify> proxy;
|
||||
if (eventQ) {
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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;
|
||||
|
|
|
@ -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<nsIInputStreamNotify> event;
|
||||
NS_NewInputStreamReadyEvent(getter_AddRefs(event), mNotify, mEventQ);
|
||||
mNotify = 0;
|
||||
nsCOMPtr<nsIInputStreamCallback> 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<nsIAsyncInputStream> mStream;
|
||||
nsCOMPtr<nsIInputStreamNotify> mNotify;
|
||||
nsCOMPtr<nsIEventQueue> mEventQ;
|
||||
nsCOMPtr<nsIAsyncInputStream> mStream;
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> 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<nsIOutputStreamNotify> event;
|
||||
NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mNotify, mEventQ);
|
||||
mNotify = 0;
|
||||
nsCOMPtr<nsIOutputStreamCallback> 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<nsIAsyncOutputStream> mStream;
|
||||
nsCOMPtr<nsIOutputStreamNotify> mNotify;
|
||||
nsCOMPtr<nsIEventQueue> mEventQ;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mStream;
|
||||
nsCOMPtr<nsIOutputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> 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<nsIInputStream> mSource;
|
||||
nsCOMPtr<nsIOutputStream> mSink;
|
||||
nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
|
||||
nsCOMPtr<nsIEventTarget> 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<nsIAsyncInputStream> mSource;
|
||||
nsCOMPtr<nsIAsyncOutputStream> 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<nsIAsyncInputStream> mSource;
|
||||
nsCOMPtr<nsIAsyncOutputStream> 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<nsIAsyncInputStream> pipeIn;
|
||||
nsCOMPtr<nsIAsyncOutputStream> 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;
|
||||
}
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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<nsIEventQueueService> 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) \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
{
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -37,7 +37,6 @@
|
|||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIThread.h"
|
||||
#include "nsIThreadPool.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -45,49 +44,6 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsIServiceManager.h"
|
||||
|
||||
class nsConcurrentRunner : public nsIRunnable {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
nsCOMPtr<nsIThread> 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<nsIThreadPool> 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<nsIThreadPool> 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);
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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<nsIEventQueue> 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;
|
||||
|
|
|
@ -47,9 +47,8 @@ public:
|
|||
nsEventQueueImpl();
|
||||
virtual ~nsEventQueueImpl();
|
||||
|
||||
// nsISupports interface...
|
||||
NS_DECL_ISUPPORTS
|
||||
// nsIEventQueue interface...
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_NSIEVENTQUEUE
|
||||
|
||||
// Helpers
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
|
@ -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);
|
||||
%}
|
|
@ -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<nsIThread> 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<nsIRunnable> 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<nsIThread> 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<nsIThread> 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<nsIThread> 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<nsIRunnable> request;
|
||||
|
||||
nsCOMPtr<nsIThread> 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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<nsISupportsArray> mThreads;
|
||||
nsCOMArray<nsIRunnable> mPendingRequests;
|
||||
nsCOMArray<nsIRunnable> 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<nsThreadPool> mPool;
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // nsThread_h__
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче