fixes crash in js_FreeStack caused by a race condition in necko. see 139556 for the details. r=rpotts, sr=darin

This commit is contained in:
dougt%netscape.com 2002-05-31 20:40:11 +00:00
parent 11c77022cb
commit 5f7d72f9e8
13 changed files with 176 additions and 24 deletions

View File

@ -49,6 +49,7 @@
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsIProxyObjectManager.h" #include "nsIProxyObjectManager.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "nsProxyRelease.h"
static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID); static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID);
@ -520,6 +521,10 @@ nsFileTransport::AsyncRead(nsIStreamListener *aListener,
mTransferAmount = aTransferCount; mTransferAmount = aTransferCount;
mXferState = OPEN_FOR_READ; mXferState = OPEN_FOR_READ;
nsIEventQueueService* eqService = mService->GetCachedEventQueueService();
eqService->GetSpecialEventQueue(nsIEventQueueService::CURRENT_THREAD_EVENT_QUEUE, getter_AddRefs(mEventQ));
NS_ASSERTION(mEventQ, "No Event Queue on calling thread");
LOG(("nsFileTransport: AsyncRead [this=%x %s] mOffset=%d mTransferAmount=%d\n", LOG(("nsFileTransport: AsyncRead [this=%x %s] mOffset=%d mTransferAmount=%d\n",
this, mStreamName.get(), mOffset, mTransferAmount)); this, mStreamName.get(), mOffset, mTransferAmount));
@ -563,6 +568,10 @@ nsFileTransport::AsyncWrite(nsIStreamProvider *aProvider,
mTransferAmount = aTransferCount; mTransferAmount = aTransferCount;
mXferState = OPEN_FOR_WRITE; mXferState = OPEN_FOR_WRITE;
nsIEventQueueService* eqService = mService->GetCachedEventQueueService();
eqService->GetSpecialEventQueue(nsIEventQueueService::CURRENT_THREAD_EVENT_QUEUE, getter_AddRefs(mEventQ));
NS_ASSERTION(mEventQ, "No Event Queue on calling thread");
LOG(("nsFileTransport: AsyncWrite [this=%x %s] mOffset=%d mTransferAmount=%d\n", LOG(("nsFileTransport: AsyncWrite [this=%x %s] mOffset=%d mTransferAmount=%d\n",
this, mStreamName.get(), mOffset, mTransferAmount)); this, mStreamName.get(), mOffset, mTransferAmount));
@ -823,27 +832,38 @@ nsFileTransport::Process(nsIProgressEventSink *progressSink)
// is reusing the stream. // is reusing the stream.
mXferState = CLOSING; mXferState = CLOSING;
DoClose(); DoClose();
nsCOMPtr <nsISupports> saveContext = mContext;
nsCOMPtr <nsIStreamListener> saveListener = mListener;
mListener = nsnull;
mContext = nsnull;
// close the data source // close the data source
NS_IF_RELEASE(mSourceWrapper); NS_IF_RELEASE(mSourceWrapper);
mSourceWrapper = nsnull; mSourceWrapper = nsnull;
if (progressSink) { if (progressSink) {
progressSink->OnStatus(this, saveContext, progressSink->OnStatus(this,
NS_NET_STATUS_READ_FROM, mContext,
NS_ConvertASCIItoUCS2(mStreamName).get()); NS_NET_STATUS_READ_FROM,
NS_ConvertASCIItoUCS2(mStreamName).get());
} }
if (saveListener) { if (mListener) {
saveListener->OnStopRequest(this, saveContext, mStatus); mListener->OnStopRequest(this, mContext, mStatus);
saveListener = 0; mListener = 0;
} }
saveContext = 0;
// if we have a context, we have to ensure that it is released on the
// proper thread.
if (mContext) {
if (mEventQ) {
// see http://bugzilla.mozilla.org/show_bug.cgi?id=139556#c64
// for the reason behind this evil reference counting.
nsISupports* doomed = mContext.get();
NS_ADDREF(doomed);
mContext = 0;
NS_ProxyRelease(mEventQ, doomed);
}
else {
mContext = nsnull;
}
}
break; break;
} }
@ -1002,16 +1022,31 @@ nsFileTransport::Process(nsIProgressEventSink *progressSink)
NS_IF_RELEASE(mSinkWrapper); NS_IF_RELEASE(mSinkWrapper);
mSinkWrapper = nsnull; mSinkWrapper = nsnull;
if (mProvider) {
mProvider->OnStopRequest(this, mContext, mStatus);
mProvider = 0;
}
if (progressSink) if (progressSink)
progressSink->OnStatus(this, mContext, progressSink->OnStatus(this, mContext,
NS_NET_STATUS_WROTE_TO, NS_NET_STATUS_WROTE_TO,
NS_ConvertASCIItoUCS2(mStreamName).get()); NS_ConvertASCIItoUCS2(mStreamName).get());
mContext = 0;
if (mProvider) {
mProvider->OnStopRequest(this, mContext, mStatus);
mProvider = 0;
}
// if we have a context, we have to ensure that it is released on the
// proper thread.
if (mContext) {
if (mEventQ) {
// see http://bugzilla.mozilla.org/show_bug.cgi?id=139556#c64
// for the reason behind this evil reference counting.
nsISupports* doomed = mContext.get();
NS_ADDREF(doomed);
mContext = 0;
NS_ProxyRelease(mEventQ, doomed);
}
else {
mContext = nsnull;
}
}
mXferState = CLOSING; mXferState = CLOSING;
break; break;
} }

View File

@ -131,6 +131,9 @@ protected:
nsCOMPtr<nsISupports> mContext; nsCOMPtr<nsISupports> mContext;
// Queue where release of context, listener, and/or provider should occur.
nsCOMPtr<nsIEventQueue> mEventQ;
// mXferState is only changed by the file transport thread: // mXferState is only changed by the file transport thread:
XferState mXferState; XferState mXferState;

View File

@ -122,6 +122,15 @@ nsFileTransportService::GetCachedMimeService()
return mMimeService.get(); return mMimeService.get();
} }
nsIEventQueueService*
nsFileTransportService::GetCachedEventQueueService()
{
if (!mEventQService) {
mEventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
}
return mEventQService.get();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP NS_IMETHODIMP

View File

@ -42,6 +42,7 @@
#include "nsIThreadPool.h" #include "nsIThreadPool.h"
#include "nsSupportsArray.h" #include "nsSupportsArray.h"
#include "nsIMIMEService.h" #include "nsIMIMEService.h"
#include "nsIEventQueueService.h"
#define NS_FILE_TRANSPORT_WORKER_COUNT_MIN 1 #define NS_FILE_TRANSPORT_WORKER_COUNT_MIN 1
#define NS_FILE_TRANSPORT_WORKER_COUNT_MAX 4//16 #define NS_FILE_TRANSPORT_WORKER_COUNT_MAX 4//16
@ -64,6 +65,7 @@ public:
static nsFileTransportService *GetInstance() { return mInstance; } static nsFileTransportService *GetInstance() { return mInstance; }
nsIMIMEService* GetCachedMimeService(); nsIMIMEService* GetCachedMimeService();
nsIEventQueueService* GetCachedEventQueueService();
PRInt32 mConnectedTransports; PRInt32 mConnectedTransports;
PRInt32 mTotalTransports; PRInt32 mTotalTransports;
@ -79,6 +81,7 @@ protected:
nsCOMPtr<nsIThreadPool> mPool; nsCOMPtr<nsIThreadPool> mPool;
PRLock* mLock; PRLock* mLock;
nsCOMPtr<nsIMIMEService> mMimeService; nsCOMPtr<nsIMIMEService> mMimeService;
nsCOMPtr<nsIEventQueueService> mEventQService;
static nsFileTransportService* mInstance; static nsFileTransportService* mInstance;
}; };

View File

@ -61,6 +61,7 @@
#include "nsITransportSecurityInfo.h" #include "nsITransportSecurityInfo.h"
#include "nsMemory.h" #include "nsMemory.h"
#include "nsIProxyInfo.h" #include "nsIProxyInfo.h"
#include "nsProxyRelease.h"
#if defined(PR_LOGGING) #if defined(PR_LOGGING)
static PRLogModuleInfo *gSocketTransportLog = nsnull; static PRLogModuleInfo *gSocketTransportLog = nsnull;
@ -2637,6 +2638,12 @@ nsSocketRequest::SetTransport(nsSocketTransport *aTransport)
// //
NS_IF_RELEASE(mTransport); NS_IF_RELEASE(mTransport);
NS_IF_ADDREF(mTransport = aTransport); NS_IF_ADDREF(mTransport = aTransport);
//
// Set the event queue
//
nsIEventQueueService* eqService = aTransport->mService->GetCachedEventQueueService();
eqService->GetSpecialEventQueue(nsIEventQueueService::CURRENT_THREAD_EVENT_QUEUE, getter_AddRefs(mEventQ));
} }
nsresult nsresult
@ -2657,9 +2664,21 @@ nsSocketRequest::OnStop()
mObserver->OnStartRequest(this, mContext); mObserver->OnStartRequest(this, mContext);
mStartFired = PR_TRUE; mStartFired = PR_TRUE;
} }
mObserver->OnStopRequest(this, mContext, mStatus); mObserver->OnStopRequest(this, mContext, mStatus);
mObserver = 0; mObserver = 0;
mContext = 0;
if (mContext) {
if (mEventQ) {
nsISupports* doomed = mContext.get();
NS_ADDREF(doomed);
mContext = 0;
NS_ProxyRelease(mEventQ, doomed);
}
else {
mContext = 0;
}
}
mStopFired = PR_TRUE; mStopFired = PR_TRUE;
} }
return NS_OK; return NS_OK;

View File

@ -309,6 +309,8 @@ protected:
nsSocketBOS *mBOS; // weak reference nsSocketBOS *mBOS; // weak reference
nsSocketReadRequest *mReadRequest; nsSocketReadRequest *mReadRequest;
nsSocketWriteRequest *mWriteRequest; nsSocketWriteRequest *mWriteRequest;
friend nsSocketRequest;
}; };
/** /**
@ -453,6 +455,8 @@ protected:
nsSocketTransport *mTransport; nsSocketTransport *mTransport;
nsCOMPtr<nsIRequestObserver> mObserver; nsCOMPtr<nsIRequestObserver> mObserver;
nsCOMPtr<nsISupports> mContext; nsCOMPtr<nsISupports> mContext;
// Queue where release of context, listener, and/or provider should occur.
nsCOMPtr<nsIEventQueue> mEventQ;
nsresult mStatus; nsresult mStatus;
PRIntn mSuspendCount; PRIntn mSuspendCount;
PRPackedBool mCanceled; PRPackedBool mCanceled;

View File

@ -211,6 +211,15 @@ nsSocketTransportService::Init(void)
rv = NS_ERROR_UNEXPECTED; rv = NS_ERROR_UNEXPECTED;
} }
} }
if (NS_SUCCEEDED(rv) && !mEventQService) {
mEventQService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
if (!mEventQService) {
rv = NS_ERROR_UNEXPECTED;
}
}
return rv; return rv;
} }
@ -791,22 +800,22 @@ nsSocketTransportService::GetNeckoStringByName (const char *aName, PRUnichar **a
nsresult res; nsresult res;
nsAutoString resultString; resultString.AssignWithConversion(aName); nsAutoString resultString; resultString.AssignWithConversion(aName);
if (!m_stringBundle) { if (!mStringBundle) {
const char propertyURL[] = NECKO_MSGS_URL; const char propertyURL[] = NECKO_MSGS_URL;
// make sure that we get this service on the UI thread. // make sure that we get this service on the UI thread.
NS_WITH_PROXIED_SERVICE(nsIStringBundleService, sBundleService, kStringBundleServiceCID, NS_WITH_PROXIED_SERVICE(nsIStringBundleService, sBundleService, kStringBundleServiceCID,
NS_UI_THREAD_EVENTQ, &res); NS_UI_THREAD_EVENTQ, &res);
if (NS_SUCCEEDED (res) && (nsnull != sBundleService)) { if (NS_SUCCEEDED (res) && (nsnull != sBundleService)) {
res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle)); res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(mStringBundle));
} }
} }
if (m_stringBundle) if (mStringBundle)
{ {
nsAutoString unicodeName; unicodeName.AssignWithConversion(aName); nsAutoString unicodeName; unicodeName.AssignWithConversion(aName);
PRUnichar *ptrv = nsnull; PRUnichar *ptrv = nsnull;
res = m_stringBundle->GetStringFromName(unicodeName.get(), &ptrv); res = mStringBundle->GetStringFromName(unicodeName.get(), &ptrv);
if (NS_FAILED(res)) if (NS_FAILED(res))
{ {

View File

@ -46,6 +46,7 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIStringBundle.h" #include "nsIStringBundle.h"
#include "nsIDNSService.h" #include "nsIDNSService.h"
#include "nsIEventQueueService.h"
#if defined(XP_PC) || defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_MAC) #if defined(XP_PC) || defined(XP_UNIX) || defined(XP_BEOS) || defined(XP_MAC)
// //
@ -99,6 +100,7 @@ public:
nsresult GetNeckoStringByName (const char *aName, PRUnichar **aString); nsresult GetNeckoStringByName (const char *aName, PRUnichar **aString);
nsIDNSService* GetCachedDNSService() { return mDNSService.get(); } nsIDNSService* GetCachedDNSService() { return mDNSService.get(); }
nsIEventQueueService* GetCachedEventQueueService() { return mEventQService.get(); }
protected: protected:
nsIThread* mThread; nsIThread* mThread;
@ -111,8 +113,9 @@ protected:
PRInt32 mSelectFDSetCount; PRInt32 mSelectFDSetCount;
PRPollDesc* mSelectFDSet; PRPollDesc* mSelectFDSet;
nsSocketTransport** mActiveTransportList; nsSocketTransport** mActiveTransportList;
nsCOMPtr<nsIStringBundle> m_stringBundle; nsCOMPtr<nsIStringBundle> mStringBundle;
nsCOMPtr<nsIDNSService> mDNSService; nsCOMPtr<nsIDNSService> mDNSService;
nsCOMPtr<nsIEventQueueService> mEventQService;
}; };

View File

@ -1026,6 +1026,15 @@ nsDNSService::Init()
rv = InstallPrefObserver(); rv = InstallPrefObserver();
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
// install xpcom shutdown observer
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
if (NS_FAILED(rv)) return rv;
mState = DNS_ONLINE; mState = DNS_ONLINE;
return NS_OK; return NS_OK;
@ -1265,6 +1274,13 @@ nsDNSService::Observe(nsISupports * subject,
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
if (!nsCRT::strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic))
{
// we need to shutdown!
ShutdownInternal();
return NS_OK;
}
if (nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) if (nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic))
return NS_OK; return NS_OK;
@ -1302,7 +1318,6 @@ nsDNSService::Observe(nsISupports * subject,
mIDNConverter = nsnull; mIDNConverter = nsnull;
} }
} }
return rv; return rv;
} }
@ -1829,6 +1844,15 @@ nsDNSService::ShutdownInternal()
(void) RemovePrefObserver(); (void) RemovePrefObserver();
// remove xpcom shutdown observer
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_FAILED(rv)) return rv;
rv = observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
if (NS_FAILED(rv)) return rv;
// reset hashtable // reset hashtable
// XXX assert hashtable is empty // XXX assert hashtable is empty
PL_DHashTableFinish(&mHashTable); PL_DHashTableFinish(&mHashTable);

View File

@ -3,4 +3,5 @@
# #
nsProxyEvent.h nsProxyEvent.h
nsProxyRelease.h
nsProxiedService.h nsProxiedService.h

View File

@ -35,6 +35,7 @@ endif
EXPORTS = \ EXPORTS = \
nsProxyEvent.h \ nsProxyEvent.h \
nsProxyRelease.h \
nsProxiedService.h \ nsProxiedService.h \
$(NULL) $(NULL)

View File

@ -25,6 +25,7 @@ MODULE=xpcom
EXPORTS = \ EXPORTS = \
nsProxyEvent.h \ nsProxyEvent.h \
nsProxyRelease.h \
nsProxiedService.h \ nsProxiedService.h \
$(NULL) $(NULL)

View File

@ -50,7 +50,7 @@ static void* PR_CALLBACK
ReleaseDestructorEventHandler(PLEvent *self) ReleaseDestructorEventHandler(PLEvent *self)
{ {
nsISupports* owner = (nsISupports*) PL_GetEventOwner(self); nsISupports* owner = (nsISupports*) PL_GetEventOwner(self);
NS_DELETEXPCOM(owner); NS_RELEASE(owner);
return nsnull; return nsnull;
} }
@ -60,6 +60,44 @@ ReleaseDestructorDestroyHandler(PLEvent *self)
PR_DELETE(self); PR_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");
}
#define NS_IMPL_PROXY_RELEASE(_class) \ #define NS_IMPL_PROXY_RELEASE(_class) \
NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \ NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \
{ \ { \
@ -106,5 +144,7 @@ NS_IMETHODIMP_(nsrefcnt) _class::Release(void)
return count; \ return count; \
} \ } \
#endif #endif