fixes bug 210125 "need to be able to AsyncWait for closure only" r=dougt sr=bzbarsky

This commit is contained in:
darin%meer.net 2003-10-06 01:46:31 +00:00
Родитель c881ba323d
Коммит 77642ae698
73 изменённых файлов: 2530 добавлений и 2745 удалений

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

@ -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();