diff --git a/mozilla/xpcom/base/nsConsoleService.cpp b/mozilla/xpcom/base/nsConsoleService.cpp index 0f4c1eba0a4..3a9993405b6 100644 --- a/mozilla/xpcom/base/nsConsoleService.cpp +++ b/mozilla/xpcom/base/nsConsoleService.cpp @@ -46,6 +46,7 @@ #include "nsIServiceManager.h" #include "nsIProxyObjectManager.h" #include "nsSupportsArray.h" +#include "nsThreadManager.h" #include "nsConsoleService.h" #include "nsConsoleMessage.h" @@ -322,7 +323,11 @@ nsConsoleService::GetProxyForListener(nsIConsoleListener* aListener, * * Would it be better to catch that case and leave the listener unproxied? */ - rv = proxyManager->GetProxyForObject(NS_CURRENT_EVENTQ, + + nsCOMPtr thread; + nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); + + rv = proxyManager->GetProxyForObject(thread, NS_GET_IID(nsIConsoleListener), aListener, PROXY_ASYNC | PROXY_ALWAYS, diff --git a/mozilla/xpcom/base/nsMemoryImpl.cpp b/mozilla/xpcom/base/nsMemoryImpl.cpp index 36c2ad191a2..e0a210a0479 100644 --- a/mozilla/xpcom/base/nsMemoryImpl.cpp +++ b/mozilla/xpcom/base/nsMemoryImpl.cpp @@ -37,8 +37,8 @@ #include "nsXPCOM.h" #include "nsMemoryImpl.h" +#include "nsThreadManager.h" -#include "nsIEventQueueService.h" #include "nsIObserverService.h" #include "nsIRunnable.h" #include "nsIServiceManager.h" @@ -46,7 +46,7 @@ #include "nsIThread.h" #include "prmem.h" -#include "plevent.h" +#include "prcvar.h" #include "nsAlgorithm.h" #include "nsAutoLock.h" @@ -237,20 +237,7 @@ nsMemoryImpl::FlushMemory(const PRUnichar* aReason, PRBool aImmediate) // They've asked us to run the flusher *immediately*. We've // got to be on the UI main thread for us to be able to do // that...are we? - PRBool isOnUIThread = PR_FALSE; - - nsCOMPtr main; - rv = nsIThread::GetMainThread(getter_AddRefs(main)); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr current; - rv = nsIThread::GetCurrent(getter_AddRefs(current)); - if (NS_SUCCEEDED(rv)) { - if (current == main) - isOnUIThread = PR_TRUE; - } - } - - if (! isOnUIThread) { + if (!nsThreadManager::IsMainThread()) { NS_ERROR("can't synchronously flush memory: not on UI thread"); return NS_ERROR_FAILURE; } @@ -272,16 +259,11 @@ nsMemoryImpl::FlushMemory(const PRUnichar* aReason, PRBool aImmediate) rv = RunFlushers(aReason); } else { - nsCOMPtr eqs = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv); - if (eqs) { - nsCOMPtr eq; - rv = eqs->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eq)); - if (NS_SUCCEEDED(rv)) { - PL_InitEvent(&sFlushEvent.mEvent, this, HandleFlushEvent, DestroyFlushEvent); - sFlushEvent.mReason = aReason; - - rv = eq->PostEvent(NS_REINTERPRET_CAST(PLEvent*, &sFlushEvent)); - } + nsCOMPtr thread; + nsThreadManager::get()->GetMainThread(getter_AddRefs(thread)); + if (thread) { + sFlushEvent.mReason = aReason; + rv = thread->Dispatch(&sFlushEvent, NS_DISPATCH_NORMAL); } } @@ -305,19 +287,16 @@ nsMemoryImpl::RunFlushers(const PRUnichar* aReason) return NS_OK; } -void* -nsMemoryImpl::HandleFlushEvent(PLEvent* aEvent) -{ - FlushEvent* event = NS_REINTERPRET_CAST(FlushEvent*, aEvent); +// XXX need NS_IMPL_STATIC_ADDREF/RELEASE +NS_IMETHODIMP_(nsrefcnt) nsMemoryImpl::FlushEvent::AddRef() { return 2; } +NS_IMETHODIMP_(nsrefcnt) nsMemoryImpl::FlushEvent::Release() { return 1; } +NS_IMPL_QUERY_INTERFACE1(nsMemoryImpl::FlushEvent, nsIRunnable) - sGlobalMemory.RunFlushers(event->mReason); - return 0; -} - -void -nsMemoryImpl::DestroyFlushEvent(PLEvent* aEvent) +NS_IMETHODIMP +nsMemoryImpl::FlushEvent::Run() { - // no-op, since mEvent is a member of nsMemoryImpl + sGlobalMemory.RunFlushers(mReason); + return NS_OK; } PRLock* @@ -421,10 +400,8 @@ MemoryFlusher::Init() sCVar = PR_NewCondVar(sLock); if (!sCVar) return NS_ERROR_FAILURE; - return NS_NewThread(&sThread, - this, - 0, /* XXX use default stack size? */ - PR_JOINABLE_THREAD); + return nsThreadManager::NewThread(NS_LITERAL_CSTRING("xpcom.memoryflusher"), + this, &sThread); } void @@ -438,7 +415,7 @@ MemoryFlusher::StopAndJoin() } if (sThread) - sThread->Join(); + sThread->Shutdown(); } NS_IF_RELEASE(sThread); diff --git a/mozilla/xpcom/base/nsMemoryImpl.h b/mozilla/xpcom/base/nsMemoryImpl.h index e179b1d52ba..3e53c332461 100644 --- a/mozilla/xpcom/base/nsMemoryImpl.h +++ b/mozilla/xpcom/base/nsMemoryImpl.h @@ -39,8 +39,8 @@ #define nsMemoryImpl_h__ #include "nsIMemory.h" +#include "nsIRunnable.h" #include "prlock.h" -#include "plevent.h" // nsMemoryImpl is a static object. We can do this because it doesn't have // a constructor/destructor or any instance members. Please don't add @@ -64,14 +64,13 @@ public: NS_HIDDEN_(nsresult) FlushMemory(const PRUnichar* aReason, PRBool aImmediate); protected: - struct FlushEvent { - PLEvent mEvent; + struct FlushEvent : public nsIRunnable { + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIRUNNABLE const PRUnichar* mReason; }; NS_HIDDEN_(nsresult) RunFlushers(const PRUnichar* aReason); - static NS_HIDDEN_(void*) PR_CALLBACK HandleFlushEvent(PLEvent* aEvent); - static NS_HIDDEN_(void) PR_CALLBACK DestroyFlushEvent(PLEvent* aEvent); static PRLock* sFlushLock; static PRBool sIsFlushing; diff --git a/mozilla/xpcom/build/dlldeps.cpp b/mozilla/xpcom/build/dlldeps.cpp index 362620e5f3d..792264dbfac 100644 --- a/mozilla/xpcom/build/dlldeps.cpp +++ b/mozilla/xpcom/build/dlldeps.cpp @@ -45,7 +45,6 @@ #include "nsIAtom.h" #include "nsFixedSizeAllocator.h" #include "nsRecyclingAllocator.h" -#include "nsIThread.h" #include "nsDeque.h" #include "nsTraceRefcnt.h" #include "nsTraceRefcntImpl.h" @@ -138,7 +137,6 @@ void XXXNeverCalled() a.Init(0, 0, 0, 0, 0); a.Alloc(0); a.Free(0, 0); - nsIThread::GetCurrent(nsnull); nsDeque d(nsnull); nsDequeIterator di(d); nsTraceRefcnt::LogAddCOMPtr(nsnull, nsnull); diff --git a/mozilla/xpcom/build/nsXPComInit.cpp b/mozilla/xpcom/build/nsXPComInit.cpp index b5a32a395ac..cae448b97d7 100644 --- a/mozilla/xpcom/build/nsXPComInit.cpp +++ b/mozilla/xpcom/build/nsXPComInit.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 ci et: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -70,9 +71,7 @@ #include "nsIServiceManager.h" #include "nsGenericFactory.h" -#include "nsEventQueueService.h" -#include "nsEventQueue.h" -#include "nsEventQueueUtils.h" +#include "nsThreadManager.h" #include "nsIProxyObjectManager.h" #include "nsProxyEventPrivate.h" // access to the impl of nsProxyObjectManager for the generic factory registration. @@ -149,7 +148,6 @@ static NS_DEFINE_CID(kMemoryCID, NS_MEMORY_CID); static NS_DEFINE_CID(kINIParserFactoryCID, NS_INIPARSERFACTORY_CID); NS_GENERIC_FACTORY_CONSTRUCTOR(nsProcess) -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsEventQueueServiceImpl, Init) #define NS_ENVIRONMENT_CLASSNAME "Environment Service" @@ -217,6 +215,19 @@ NS_GENERIC_AGGREGATED_CONSTRUCTOR_INIT(nsProperties, Init) NS_GENERIC_FACTORY_CONSTRUCTOR(nsUUIDGenerator) +static NS_METHOD +nsThreadManagerGetSingleton(nsISupports* outer, + const nsIID& aIID, + void* *aInstancePtr) +{ + NS_ASSERTION(aInstancePtr, "null outptr"); + NS_ENSURE_TRUE(!outer, NS_ERROR_NO_AGGREGATION); + + return nsThreadManager::get()->QueryInterface(aIID, aInstancePtr); +} +NS_DECL_CLASSINFO(nsThreadManager) +NS_DECL_CLASSINFO(nsThread) + static NS_METHOD nsXPTIInterfaceInfoManagerGetSingleton(nsISupports* outer, const nsIID& aIID, @@ -349,9 +360,6 @@ static const nsModuleComponentInfo components[] = { #endif COMPONENT(OBSERVERSERVICE, nsObserverService::Create), COMPONENT(GENERICFACTORY, nsGenericFactory::Create), - COMPONENT(EVENTQUEUESERVICE, nsEventQueueServiceImplConstructor), - COMPONENT(EVENTQUEUE, nsEventQueueImpl::Create), - COMPONENT(THREAD, nsThread::Create), #define NS_XPCOMPROXY_CID NS_PROXYEVENT_MANAGER_CID COMPONENT(XPCOMPROXY, nsProxyObjectManager::Create), @@ -388,6 +396,10 @@ static const nsModuleComponentInfo components[] = { COMPONENT(PROCESS, nsProcessConstructor), COMPONENT(ENVIRONMENT, nsEnvironment::Create), + COMPONENT_CI_FLAGS(THREADMANAGER, nsThreadManagerGetSingleton, + nsThreadManager, + nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON), + COMPONENT_CI_FLAGS(STRINGINPUTSTREAM, nsStringInputStreamConstructor, nsStringInputStream, nsIClassInfo::THREADSAFE), COMPONENT(MULTIPLEXINPUTSTREAM, nsMultiplexInputStreamConstructor), @@ -471,7 +483,7 @@ NS_InitXPCOM3(nsIServiceManager* *result, NS_LogInit(); // Establish the main thread here. - rv = nsIThread::SetMainThread(); + rv = nsThreadManager::get()->Init(); if (NS_FAILED(rv)) return rv; // Set up the timer globals/timer thread @@ -659,14 +671,8 @@ NS_ShutdownXPCOM(nsIServiceManager* servMgr) // Block it so that the COMPtr will get deleted before we hit // servicemanager shutdown - // grab the event queue service so that we can process events and - // manage event queues - nsRefPtr eqs; - CallGetService(NS_EVENTQUEUESERVICE_CONTRACTID, - (nsEventQueueServiceImpl**) getter_AddRefs(eqs)); - - nsCOMPtr currentQ; - NS_GetCurrentEventQ(getter_AddRefs(currentQ), eqs); + nsCOMPtr thread; + nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); nsCOMPtr observerService = do_GetService("@mozilla.org/observer-service;1"); @@ -683,22 +689,21 @@ NS_ShutdownXPCOM(nsIServiceManager* servMgr) } } - if (currentQ) - currentQ->ProcessPendingEvents(); + if (thread) + NS_RunPendingTasks(thread); if (observerService) (void) observerService-> NotifyObservers(nsnull, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, nsnull); - if (currentQ) - currentQ->ProcessPendingEvents(); + if (thread) + NS_RunPendingTasks(thread); - if (eqs) - eqs->Shutdown(); + nsThreadManager::get()->Shutdown(); - if (currentQ) - currentQ->ProcessPendingEvents(); + if (thread) + NS_RunPendingTasks(thread); // We save the "xpcom-shutdown-loaders" observers to notify after // the observerservice is gone. @@ -793,7 +798,6 @@ NS_ShutdownXPCOM(nsIServiceManager* servMgr) EmptyEnumeratorImpl::Shutdown(); nsMemoryImpl::Shutdown(); - nsThread::Shutdown(); NS_PurgeAtomTable(); NS_IF_RELEASE(gDebug); diff --git a/mozilla/xpcom/components/nsCategoryManager.cpp b/mozilla/xpcom/components/nsCategoryManager.cpp index e2864268c8d..01c32be5d92 100644 --- a/mozilla/xpcom/components/nsCategoryManager.cpp +++ b/mozilla/xpcom/components/nsCategoryManager.cpp @@ -59,6 +59,7 @@ #include "nsQuickSort.h" #include "nsEnumeratorUtils.h" #include "nsIProxyObjectManager.h" +#include "nsThreadManager.h" class nsIComponentLoaderManager; @@ -544,13 +545,17 @@ nsCategoryManager::NotifyObservers( const char *aTopic, if (!observerService) return; + nsCOMPtr mainThread; + nsThreadManager::get()->GetMainThread(getter_AddRefs(mainThread)); + nsCOMPtr obsProxy; - NS_GetProxyForObject(NS_UI_THREAD_EVENTQ, + NS_GetProxyForObject(mainThread, NS_GET_IID(nsIObserverService), observerService, PROXY_ASYNC, getter_AddRefs(obsProxy)); - if (!obsProxy) return; + if (!obsProxy) + return; if (aEntryName) { nsCOMPtr entry diff --git a/mozilla/xpcom/io/nsIAsyncInputStream.idl b/mozilla/xpcom/io/nsIAsyncInputStream.idl index 6ce0bb30fff..e1d4a7fd015 100644 --- a/mozilla/xpcom/io/nsIAsyncInputStream.idl +++ b/mozilla/xpcom/io/nsIAsyncInputStream.idl @@ -38,7 +38,7 @@ #include "nsIInputStream.idl" interface nsIInputStreamCallback; -interface nsIEventTarget; +interface nsIDispatchTarget; /** * If an input stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK @@ -52,7 +52,7 @@ interface nsIEventTarget; * necessary that a non-blocking nsIInputStream implementation also implement * nsIAsyncInputStream. */ -[scriptable, uuid(15a15329-00de-44e8-ab06-0d0b0d43dc5b)] +[scriptable, uuid(a5f255ab-4801-4161-8816-277ac92f6ad1)] interface nsIAsyncInputStream : nsIInputStream { /** @@ -108,7 +108,7 @@ interface nsIAsyncInputStream : nsIInputStream void asyncWait(in nsIInputStreamCallback aCallback, in unsigned long aFlags, in unsigned long aRequestedCount, - in nsIEventTarget aEventTarget); + in nsIDispatchTarget aDispatchTarget); /** * If passed to asyncWait, this flag overrides the default behavior, diff --git a/mozilla/xpcom/io/nsIAsyncOutputStream.idl b/mozilla/xpcom/io/nsIAsyncOutputStream.idl index b0dd9ec0323..bb658f18625 100644 --- a/mozilla/xpcom/io/nsIAsyncOutputStream.idl +++ b/mozilla/xpcom/io/nsIAsyncOutputStream.idl @@ -38,7 +38,7 @@ #include "nsIOutputStream.idl" interface nsIOutputStreamCallback; -interface nsIEventTarget; +interface nsIDispatchTarget; /** * If an output stream is non-blocking, it may return NS_BASE_STREAM_WOULD_BLOCK @@ -52,7 +52,7 @@ interface nsIEventTarget; * necessary that a non-blocking nsIOutputStream implementation also implement * nsIAsyncOutputStream. */ -[scriptable, uuid(10dc9c94-8aff-49c6-8af9-d7fdb7339dae)] +[scriptable, uuid(beb632d3-d77a-4e90-9134-f9ece69e8200)] interface nsIAsyncOutputStream : nsIOutputStream { /** @@ -99,7 +99,7 @@ interface nsIAsyncOutputStream : nsIOutputStream * 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 + * @param aDispatchTarget * 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 @@ -108,7 +108,7 @@ interface nsIAsyncOutputStream : nsIOutputStream void asyncWait(in nsIOutputStreamCallback aCallback, in unsigned long aFlags, in unsigned long aRequestedCount, - in nsIEventTarget aEventTarget); + in nsIDispatchTarget aDispatchTarget); /** * If passed to asyncWait, this flag overrides the default behavior, diff --git a/mozilla/xpcom/io/nsPipe3.cpp b/mozilla/xpcom/io/nsPipe3.cpp index 4d33cb0166e..92907163b0d 100644 --- a/mozilla/xpcom/io/nsPipe3.cpp +++ b/mozilla/xpcom/io/nsPipe3.cpp @@ -36,7 +36,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsIPipe.h" -#include "nsIEventTarget.h" +#include "nsIDispatchTarget.h" #include "nsISeekableStream.h" #include "nsSegmentedBuffer.h" #include "nsStreamUtils.h" @@ -801,7 +801,7 @@ NS_IMETHODIMP nsPipeInputStream::AsyncWait(nsIInputStreamCallback *callback, PRUint32 flags, PRUint32 requestedCount, - nsIEventTarget *target) + nsIDispatchTarget *target) { LOG(("III AsyncWait [this=%x]\n", this)); @@ -1172,7 +1172,7 @@ NS_IMETHODIMP nsPipeOutputStream::AsyncWait(nsIOutputStreamCallback *callback, PRUint32 flags, PRUint32 requestedCount, - nsIEventTarget *target) + nsIDispatchTarget *target) { LOG(("OOO AsyncWait [this=%x]\n", this)); diff --git a/mozilla/xpcom/io/nsStreamUtils.cpp b/mozilla/xpcom/io/nsStreamUtils.cpp index b0f396a1b59..b93f1b436f4 100644 --- a/mozilla/xpcom/io/nsStreamUtils.cpp +++ b/mozilla/xpcom/io/nsStreamUtils.cpp @@ -39,20 +39,21 @@ #include "nsStreamUtils.h" #include "nsCOMPtr.h" #include "nsIPipe.h" -#include "nsIEventTarget.h" +#include "nsIDispatchTarget.h" +#include "nsIRunnable.h" #include "nsAutoLock.h" #include "nsString.h" //----------------------------------------------------------------------------- -class nsInputStreamReadyEvent : public PLEvent +class nsInputStreamReadyEvent : public nsIRunnable , public nsIInputStreamCallback { public: NS_DECL_ISUPPORTS nsInputStreamReadyEvent(nsIInputStreamCallback *callback, - nsIEventTarget *target) + nsIDispatchTarget *target) : mCallback(callback) , mTarget(target) { @@ -61,28 +62,28 @@ public: private: ~nsInputStreamReadyEvent() { - if (mCallback) { - nsresult rv; - // - // whoa!! looks like we never posted this event. take care to - // 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 = mTarget->IsOnCurrentThread(&val); - if (NS_FAILED(rv) || !val) { - nsCOMPtr event; - NS_NewInputStreamReadyEvent(getter_AddRefs(event), mCallback, mTarget); - mCallback = 0; - if (event) { - rv = event->OnInputStreamReady(nsnull); - if (NS_FAILED(rv)) { - NS_NOTREACHED("leaking stream event"); - nsISupports *sup = event; - NS_ADDREF(sup); - } + if (!mCallback) + return; + // + // whoa!! looks like we never posted this event. take care to + // 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; + nsresult rv = mTarget->IsOnCurrentThread(&val); + if (NS_FAILED(rv) || !val) { + nsCOMPtr event; + NS_NewInputStreamReadyEvent(getter_AddRefs(event), mCallback, + mTarget); + mCallback = 0; + if (event) { + rv = event->OnInputStreamReady(nsnull); + if (NS_FAILED(rv)) { + NS_NOTREACHED("leaking stream event"); + nsISupports *sup = event; + NS_ADDREF(sup); } } } @@ -93,55 +94,44 @@ public: { mStream = stream; - // will be released when event is handled - NS_ADDREF_THIS(); - - PL_InitEvent(this, nsnull, EventHandler, EventCleanup); - - if (NS_FAILED(mTarget->PostEvent(this))) { - NS_WARNING("PostEvent failed"); - NS_RELEASE_THIS(); + nsresult rv = + mTarget->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_FAILED(rv)) { + NS_WARNING("Dispatch failed"); return NS_ERROR_FAILURE; } return NS_OK; } + NS_IMETHOD Run() + { + if (mCallback) { + mCallback->OnInputStreamReady(mStream); + mCallback = nsnull; + } + return NS_OK; + } + private: nsCOMPtr mStream; nsCOMPtr mCallback; - nsCOMPtr mTarget; - - PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *plevent) - { - nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent; - // bypass event delivery if this is a cleanup event... - if (ev->mCallback) - ev->mCallback->OnInputStreamReady(ev->mStream); - ev->mCallback = 0; - return NULL; - } - - PR_STATIC_CALLBACK(void) EventCleanup(PLEvent *plevent) - { - nsInputStreamReadyEvent *ev = (nsInputStreamReadyEvent *) plevent; - NS_RELEASE(ev); - } + nsCOMPtr mTarget; }; -NS_IMPL_THREADSAFE_ISUPPORTS1(nsInputStreamReadyEvent, +NS_IMPL_THREADSAFE_ISUPPORTS2(nsInputStreamReadyEvent, nsIRunnable, nsIInputStreamCallback) //----------------------------------------------------------------------------- -class nsOutputStreamReadyEvent : public PLEvent +class nsOutputStreamReadyEvent : public nsIRunnable , public nsIOutputStreamCallback { public: NS_DECL_ISUPPORTS nsOutputStreamReadyEvent(nsIOutputStreamCallback *callback, - nsIEventTarget *target) + nsIDispatchTarget *target) : mCallback(callback) , mTarget(target) { @@ -150,82 +140,64 @@ public: private: ~nsOutputStreamReadyEvent() { - if (mCallback) { - nsresult rv; - // - // whoa!! looks like we never posted this event. take care to - // 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 = mTarget->IsOnCurrentThread(&val); - if (NS_FAILED(rv) || !val) { - nsCOMPtr event; - NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mCallback, mTarget); - mCallback = 0; - if (event) { - rv = event->OnOutputStreamReady(nsnull); - if (NS_FAILED(rv)) { - NS_NOTREACHED("leaking stream event"); - nsISupports *sup = event; - NS_ADDREF(sup); - } + if (!mCallback) + return; + // + // whoa!! looks like we never posted this event. take care to + // 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; + nsresult rv = mTarget->IsOnCurrentThread(&val); + if (NS_FAILED(rv) || !val) { + nsCOMPtr event; + NS_NewOutputStreamReadyEvent(getter_AddRefs(event), mCallback, + mTarget); + mCallback = 0; + if (event) { + rv = event->OnOutputStreamReady(nsnull); + if (NS_FAILED(rv)) { + NS_NOTREACHED("leaking stream event"); + nsISupports *sup = event; + NS_ADDREF(sup); } } } } public: - void Init(nsIOutputStreamCallback *callback, nsIEventTarget *target) - { - mCallback = callback; - mTarget = target; - - PL_InitEvent(this, nsnull, EventHandler, EventCleanup); - } - NS_IMETHOD OnOutputStreamReady(nsIAsyncOutputStream *stream) { mStream = stream; - // this will be released when the event is handled - NS_ADDREF_THIS(); - - PL_InitEvent(this, nsnull, EventHandler, EventCleanup); - - if (NS_FAILED(mTarget->PostEvent(this))) { + nsresult rv = + mTarget->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_FAILED(rv)) { NS_WARNING("PostEvent failed"); - NS_RELEASE_THIS(); return NS_ERROR_FAILURE; } return NS_OK; } + NS_IMETHOD Run() + { + if (mCallback) { + mCallback->OnOutputStreamReady(mStream); + mCallback = nsnull; + } + return NS_OK; + } + private: nsCOMPtr mStream; nsCOMPtr mCallback; - nsCOMPtr mTarget; - - PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *plevent) - { - nsOutputStreamReadyEvent *ev = (nsOutputStreamReadyEvent *) plevent; - if (ev->mCallback) - ev->mCallback->OnOutputStreamReady(ev->mStream); - ev->mCallback = 0; - return NULL; - } - - PR_STATIC_CALLBACK(void) EventCleanup(PLEvent *ev) - { - nsOutputStreamReadyEvent *event = (nsOutputStreamReadyEvent *) ev; - NS_RELEASE(event); - } + nsCOMPtr mTarget; }; -NS_IMPL_THREADSAFE_ISUPPORTS1(nsOutputStreamReadyEvent, +NS_IMPL_THREADSAFE_ISUPPORTS2(nsOutputStreamReadyEvent, nsIRunnable, nsIOutputStreamCallback) //----------------------------------------------------------------------------- @@ -233,7 +205,7 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsOutputStreamReadyEvent, NS_COM nsresult NS_NewInputStreamReadyEvent(nsIInputStreamCallback **event, nsIInputStreamCallback *callback, - nsIEventTarget *target) + nsIDispatchTarget *target) { nsInputStreamReadyEvent *ev = new nsInputStreamReadyEvent(callback, target); if (!ev) @@ -245,7 +217,7 @@ NS_NewInputStreamReadyEvent(nsIInputStreamCallback **event, NS_COM nsresult NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **event, nsIOutputStreamCallback *callback, - nsIEventTarget *target) + nsIDispatchTarget *target) { nsOutputStreamReadyEvent *ev = new nsOutputStreamReadyEvent(callback, target); if (!ev) @@ -260,6 +232,7 @@ NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **event, // abstract stream copier... class nsAStreamCopier : public nsIInputStreamCallback , public nsIOutputStreamCallback + , public nsIRunnable { public: NS_DECL_ISUPPORTS @@ -284,7 +257,7 @@ public: // kick off the async copy... nsresult Start(nsIInputStream *source, nsIOutputStream *sink, - nsIEventTarget *target, + nsIDispatchTarget *target, nsAsyncCopyCallbackFun callback, void *closure, PRUint32 chunksize) @@ -386,26 +359,20 @@ public: return NS_OK; } - PR_STATIC_CALLBACK(void*) HandleContinuationEvent(PLEvent *event) + // continuation event handler + NS_IMETHOD Run() { - nsAStreamCopier *self = (nsAStreamCopier *) event->owner; - self->Process(); + 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(); + nsAutoLock lock(mLock); + mEventInProcess = PR_FALSE; + if (mEventIsPending) { + mEventIsPending = PR_FALSE; + PostContinuationEvent_Locked(); } - return nsnull; - } - PR_STATIC_CALLBACK(void) DestroyContinuationEvent(PLEvent *event) - { - nsAStreamCopier *self = (nsAStreamCopier *) event->owner; - NS_RELEASE(self); - delete event; + return NS_OK; } nsresult PostContinuationEvent() @@ -427,23 +394,11 @@ public: 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); - } - } + rv = mTarget->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_SUCCEEDED(rv)) + mEventInProcess = PR_TRUE; + else + NS_WARNING("unable to post continuation event"); } return rv; } @@ -453,7 +408,7 @@ protected: nsCOMPtr mSink; nsCOMPtr mAsyncSource; nsCOMPtr mAsyncSink; - nsCOMPtr mTarget; + nsCOMPtr mTarget; PRLock *mLock; nsAsyncCopyCallbackFun mCallback; void *mClosure; @@ -462,9 +417,10 @@ protected: PRPackedBool mEventIsPending; }; -NS_IMPL_THREADSAFE_ISUPPORTS2(nsAStreamCopier, +NS_IMPL_THREADSAFE_ISUPPORTS3(nsAStreamCopier, nsIInputStreamCallback, - nsIOutputStreamCallback) + nsIOutputStreamCallback, + nsIRunnable) class nsStreamCopierIB : public nsAStreamCopier { @@ -557,7 +513,7 @@ public: NS_COM nsresult NS_AsyncCopy(nsIInputStream *source, nsIOutputStream *sink, - nsIEventTarget *target, + nsIDispatchTarget *target, nsAsyncCopyMode mode, PRUint32 chunkSize, nsAsyncCopyCallbackFun callback, diff --git a/mozilla/xpcom/io/nsStreamUtils.h b/mozilla/xpcom/io/nsStreamUtils.h index 0db75b12d69..cf08e49fc9b 100644 --- a/mozilla/xpcom/io/nsStreamUtils.h +++ b/mozilla/xpcom/io/nsStreamUtils.h @@ -44,7 +44,7 @@ class nsIInputStream; class nsIOutputStream; class nsIInputStreamCallback; class nsIOutputStreamCallback; -class nsIEventTarget; +class nsIDispatchTarget; /** * A "one-shot" proxy of the OnInputStreamReady callback. The resulting @@ -59,7 +59,7 @@ class nsIEventTarget; extern NS_COM nsresult NS_NewInputStreamReadyEvent(nsIInputStreamCallback **aEvent, nsIInputStreamCallback *aNotify, - nsIEventTarget *aEventTarget); + nsIDispatchTarget *aEventTarget); /** * A "one-shot" proxy of the OnOutputStreamReady callback. The resulting @@ -74,7 +74,7 @@ NS_NewInputStreamReadyEvent(nsIInputStreamCallback **aEvent, extern NS_COM nsresult NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback **aEvent, nsIOutputStreamCallback *aNotify, - nsIEventTarget *aEventTarget); + nsIDispatchTarget *aEventTarget); /* ------------------------------------------------------------------------- */ @@ -102,7 +102,7 @@ typedef void (* nsAsyncCopyCallbackFun)(void *closure, nsresult status); extern NS_COM nsresult NS_AsyncCopy(nsIInputStream *aSource, nsIOutputStream *aSink, - nsIEventTarget *aEventTarget, + nsIDispatchTarget *aEventTarget, nsAsyncCopyMode aMode = NS_ASYNCCOPY_VIA_READSEGMENTS, PRUint32 aChunkSize = 4096, nsAsyncCopyCallbackFun aCallbackFun = nsnull, diff --git a/mozilla/xpcom/proxy/public/Makefile.in b/mozilla/xpcom/proxy/public/Makefile.in index d6950b9a751..cedb16188d6 100644 --- a/mozilla/xpcom/proxy/public/Makefile.in +++ b/mozilla/xpcom/proxy/public/Makefile.in @@ -55,9 +55,9 @@ EXPORTS = \ nsProxiedService.h \ $(NULL) -XPIDLSRCS = nsIProxyCreateInstance.idl \ - nsIProxyObjectManager.idl \ - $(NULL) +XPIDLSRCS = \ + nsIProxyObjectManager.idl \ + $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mozilla/xpcom/proxy/public/nsIProxyObjectManager.idl b/mozilla/xpcom/proxy/public/nsIProxyObjectManager.idl index b8de77e198d..eb5f9629ed7 100644 --- a/mozilla/xpcom/proxy/public/nsIProxyObjectManager.idl +++ b/mozilla/xpcom/proxy/public/nsIProxyObjectManager.idl @@ -36,23 +36,27 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ + #include "nsISupports.idl" -interface nsIEventQueue; + +interface nsIDispatchTarget; /** * See http://www.mozilla.org/projects/xpcom/Proxies.html */ -[scriptable, uuid(eea90d43-b059-11d2-915e-c12b696c9333)] +[scriptable, uuid(ee8ce1e3-0319-4bd9-8f70-7258b21c7733)] interface nsIProxyObjectManager : nsISupports { /** * Constants for proxyType */ + /** * Synchronous: Block until result available, like function call. */ const long INVOKE_SYNC = 0x0001; + /** * Asynchronous: Return without waiting for result. * (Warning: do not pass &pointers into stack when using this flag.) @@ -64,20 +68,11 @@ interface nsIProxyObjectManager : nsISupports */ const long FORCE_PROXY_CREATION = 0x0004; - - void getProxyForObject(in nsIEventQueue destQueue, + void getProxyForObject(in nsIDispatchTarget target, in nsIIDRef iid, in nsISupports object, in PRInt32 proxyType, [iid_is(iid),retval] out nsQIResult result); - - void getProxy(in nsIEventQueue destQueue, - in nsIIDRef cid, - in nsISupports aOuter, - in nsIIDRef iid, - in PRInt32 proxyType, - [iid_is(iid),retval] out nsQIResult result); - }; @@ -102,7 +97,7 @@ interface nsIProxyObjectManager : nsISupports * readable. */ extern NS_COM nsresult -NS_GetProxyForObject(nsIEventQueue *destQueue, +NS_GetProxyForObject(nsIDispatchTarget *target, REFNSIID aIID, nsISupports* aObj, PRInt32 proxyType, diff --git a/mozilla/xpcom/proxy/public/nsProxiedService.h b/mozilla/xpcom/proxy/public/nsProxiedService.h index 7ab4adb8808..6889264b583 100644 --- a/mozilla/xpcom/proxy/public/nsProxiedService.h +++ b/mozilla/xpcom/proxy/public/nsProxiedService.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef nsProxiedService_h_ -#define nsProxiedService_h_ +#ifndef nsProxiedService_h__ +#define nsProxiedService_h__ #include "nsIServiceManager.h" #include "nsIProxyObjectManager.h" @@ -57,10 +57,10 @@ // if(NS_FAILED(rv)) // return; // nsIMyService pIProxiedObject = NULL; -// rv = pIProxyObjectManager->GetProxyForObject(pIProxyQueue, -// NS_GET_IID(nsIMyService), -// pIMyService, PROXY_SYNC, -// (void**)&pIProxiedObject); +// rv = pIProxyObjectManager->GetProxyForObject(pIDispatchTarget, +// NS_GET_IID(nsIMyService), +// pIMyService, PROXY_SYNC, +// (void**)&pIProxiedObject); // pIProxiedObject->DoIt(...); // Executed on same thread as pIProxyQueue // ... // pIProxiedObject->Release(); // Must be done as not managed for you. @@ -69,7 +69,7 @@ // { // nsresult rv; // NS_WITH_PROXIED_SERVICE(nsIMyService, pIMyService, kMyServiceCID, -// pIProxyQueue, &rv); +// pIDispatchTarget, &rv); // if(NS_FAILED(rv)) // return; // pIMyService->DoIt(...); // Executed on the same thread as pIProxyQueue @@ -96,19 +96,19 @@ class nsProxiedService { public: nsProxiedService(const nsCID &aClass, const nsIID &aIID, - nsIEventQueue* aEventQ, PRBool always, nsresult* rv) + nsIDispatchTarget* aTarget, PRBool always, nsresult* rv) { nsCOMPtr svc = do_GetService(aClass, rv); if (NS_SUCCEEDED(*rv)) - InitProxy(svc, aIID, aEventQ, always, rv); + InitProxy(svc, aIID, aTarget, always, rv); } nsProxiedService(const char* aContractID, const nsIID &aIID, - nsIEventQueue* aEventQ, PRBool always, nsresult* rv) + nsIDispatchTarget* aTarget, PRBool always, nsresult* rv) { nsCOMPtr svc = do_GetService(aContractID, rv); if (NS_SUCCEEDED(*rv)) - InitProxy(svc, aIID, aEventQ, always, rv); + InitProxy(svc, aIID, aTarget, always, rv); } operator nsISupports*() const @@ -119,13 +119,13 @@ public: private: void InitProxy(nsISupports *aObj, const nsIID &aIID, - nsIEventQueue* aEventQ, PRBool always, nsresult*rv) + nsIDispatchTarget* aTarget, PRBool always, nsresult*rv) { PRInt32 proxyType = PROXY_SYNC; if (always) proxyType |= PROXY_ALWAYS; - *rv = NS_GetProxyForObject(aEventQ, + *rv = NS_GetProxyForObject(aTarget, aIID, aObj, proxyType, @@ -135,4 +135,4 @@ private: nsCOMPtr mProxiedService; }; -#endif // nsProxiedService_h_ +#endif // nsProxiedService_h__ diff --git a/mozilla/xpcom/proxy/public/nsProxyEvent.h b/mozilla/xpcom/proxy/public/nsProxyEvent.h index 1cff90ba832..ddfd38de6b7 100644 --- a/mozilla/xpcom/proxy/public/nsProxyEvent.h +++ b/mozilla/xpcom/proxy/public/nsProxyEvent.h @@ -35,23 +35,22 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef __nsProxyEvent_h_ -#define __nsProxyEvent_h_ +#ifndef nsProxyEvent_h__ +#define nsProxyEvent_h__ #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nscore.h" #include "nsISupports.h" #include "nsIFactory.h" +#include "nsIDispatchTarget.h" -#include "nsIEventQueueService.h" -#include "nsIEventQueue.h" -#include "plevent.h" #include "prtypes.h" #include "xptcall.h" #include "xptinfo.h" class nsProxyObjectCallInfo; +class nsIRunnable; #define PROXY_SYNC 0x0001 // acts just like a function call. #define PROXY_ASYNC 0x0002 // fire and forget. This will return immediately and you will lose all return information. @@ -85,15 +84,16 @@ class nsProxyObjectCallInfo; // change your interface, or contact me about this feature request. - - class nsProxyObject { public: - nsProxyObject(nsIEventQueue *destQueue, PRInt32 proxyType, nsISupports *realObject, - nsIEventQueueService* eventQService); - nsProxyObject(nsIEventQueue *destQueue, PRInt32 proxyType, const nsCID &aClass, - nsISupports *aDelegate, const nsIID &aIID, nsIEventQueueService* eventQService); + nsProxyObject(nsIDispatchTarget *destQueue, PRInt32 proxyType, + nsISupports *realObject); + /* XXX + nsProxyObject(nsIDispatchTarget *destQueue, PRInt32 proxyType, + const nsCID &aClass, nsISupports *aDelegate, + const nsIID &aIID); + */ void AddRef(); void Release(); @@ -107,7 +107,7 @@ public: nsresult PostAndWait(nsProxyObjectCallInfo *proxyInfo); nsISupports* GetRealObject() const { return mRealObject; } - nsIEventQueue* GetQueue() const { return mDestQueue; } + nsIDispatchTarget* GetTarget() const { return mTarget; } PRInt32 GetProxyType() const { return mProxyType; } friend class nsProxyEventObject; @@ -117,17 +117,15 @@ private: PRInt32 mProxyType; - nsCOMPtr mDestQueue; /* destination queue */ + nsCOMPtr mTarget; /* dispatch target */ nsCOMPtr mRealObject; /* the non-proxy object that this event is referring to. This is a strong ref. */ - nsCOMPtr mEventQService; nsresult convertMiniVariantToVariant(nsXPTMethodInfo * methodInfo, nsXPTCMiniVariant * params, nsXPTCVariant **fullParam, uint8 *paramCount); - }; @@ -140,14 +138,14 @@ public: PRUint32 methodIndex, nsXPTCVariant* parameterList, PRUint32 parameterCount, - PLEvent *event); + nsIRunnable *event); ~nsProxyObjectCallInfo(); PRUint32 GetMethodIndex() const { return mMethodIndex; } nsXPTCVariant* GetParameterList() const { return mParameterList; } PRUint32 GetParameterCount() const { return mParameterCount; } - PLEvent* GetPLEvent() const { return mEvent; } + nsIRunnable* GetEvent() const { return mEvent; } nsresult GetResult() const { return mResult; } nsProxyObject* GetProxyObject() const { return mOwner; } @@ -155,10 +153,10 @@ public: void SetCompleted(); void PostCompleted(); - void SetResult(nsresult rv) {mResult = rv; } + void SetResult(nsresult rv) { mResult = rv; } - nsIEventQueue* GetCallersQueue(); - void SetCallersQueue(nsIEventQueue* queue); + nsIDispatchTarget* GetCallersTarget(); + void SetCallersTarget(nsIDispatchTarget* target); private: @@ -167,19 +165,16 @@ private: PRUint32 mMethodIndex; /* which method to be called? */ nsXPTCVariant *mParameterList; /* marshalled in parameter buffer */ PRUint32 mParameterCount; /* number of params */ - PLEvent *mEvent; /* the current plevent */ + nsIRunnable *mEvent; /* the current runnable */ PRInt32 mCompleted; /* is true when the method has been called. */ - nsCOMPtr mCallersEventQ; /* this is the eventQ that we must post a message back to - when we are done invoking the method (only PROXY_SYNC). - */ + nsCOMPtr mCallersTarget; /* this is the dispatch target that we must post a message back to + when we are done invoking the method (only PROXY_SYNC). */ nsRefPtr mOwner; /* this is the strong referenced nsProxyObject */ void RefCountInInterfacePointers(PRBool addRef); void CopyStrings(PRBool copy); - }; - -#endif +#endif // nsProxyEvent_h__ diff --git a/mozilla/xpcom/proxy/public/nsProxyRelease.h b/mozilla/xpcom/proxy/public/nsProxyRelease.h index 9792736b380..b4bc4ab10ec 100644 --- a/mozilla/xpcom/proxy/public/nsProxyRelease.h +++ b/mozilla/xpcom/proxy/public/nsProxyRelease.h @@ -38,7 +38,7 @@ #ifndef nsProxyRelease_h_ #define nsProxyRelease_h__ -#include "nsIEventQueueService.h" +#include "nsIDispatchTarget.h" #include "pratom.h" #include "prmem.h" @@ -56,57 +56,6 @@ * the release will happen when that PLEvent is handled. */ NS_COM nsresult NS_ProxyRelease - (nsIEventTarget *target, nsISupports *doomed, PRBool alwaysProxy=PR_FALSE); + (nsIDispatchTarget *target, nsISupports *doomed, PRBool alwaysProxy=PR_FALSE); - -#define NS_IMPL_PROXY_RELEASE(_class) \ -NS_IMETHODIMP_(nsrefcnt) _class::Release(void) \ -{ \ - NS_PRECONDITION(0 != mRefCnt, "dup release"); \ - nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); \ - NS_LOG_RELEASE(this, count, #_class); \ - \ - if (count == 0) \ - { \ - mRefCnt = 1; /* stabilize */ \ - PRBool callDirectly = PR_TRUE; \ - static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); \ - nsCOMPtr eventQService \ - = do_GetService(kEventQueueServiceCID); \ - NS_ASSERTION(eventQService, "event queue service is unavailable"); \ - \ - nsCOMPtr eventQ; \ - if (eventQService) { \ - eventQService->GetThreadEventQueue(NS_UI_THREAD, getter_AddRefs(eventQ)); \ - if (eventQ) \ - eventQ->IsOnCurrentThread(&callDirectly); \ - } \ - \ - if (callDirectly) \ - { \ - NS_RELEASE(this); \ - return 0; \ - } \ - PLEvent *event = new PLEvent; \ - if (event == nsnull) \ - { \ - NS_ASSERTION(0, "Could not create a plevent. Deleting on wrong thread!"); \ - NS_DELETEXPCOM(this); \ - return 0; \ - } \ - \ - PL_InitEvent(event, \ - NS_STATIC_CAST(nsISupports*, this), \ - ReleaseDestructorEventHandler, \ - ReleaseDestructorDestroyHandler); \ - \ - eventQ->PostEvent(event); \ - return 0; \ - } \ - return count; \ -} \ - - - - #endif diff --git a/mozilla/xpcom/proxy/src/nsProxyEvent.cpp b/mozilla/xpcom/proxy/src/nsProxyEvent.cpp index cf93367abdb..1fb3dfbc6de 100644 --- a/mozilla/xpcom/proxy/src/nsProxyEvent.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyEvent.cpp @@ -1,4 +1,6 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 ci et: */ +/* * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 @@ -51,6 +53,7 @@ #include "nsProxyEvent.h" #include "nsProxyEventPrivate.h" #include "nsIProxyObjectManager.h" +#include "nsRunnable.h" #include "nsCRT.h" #include "pratom.h" @@ -59,8 +62,7 @@ #include "nsIComponentManager.h" #include "nsMemory.h" -#include "nsIEventQueueService.h" -#include "nsIThread.h" +#include "nsThreadManager.h" #include "nsIAtom.h" //hack! Need a way to define a component as threadsafe (ie. sta). @@ -72,19 +74,108 @@ #define nsAUTF8String nsACString #define nsUTF8String nsCString +/* static void* PR_CALLBACK EventHandler(PLEvent *self); static void PR_CALLBACK DestroyHandler(PLEvent *self); static void* PR_CALLBACK CompletedEventHandler(PLEvent *self); static void PR_CALLBACK CompletedDestroyHandler(PLEvent *self) ; static void* PR_CALLBACK ProxyDestructorEventHandler(PLEvent *self); static void PR_CALLBACK ProxyDestructorDestroyHandler(PLEvent *self) ; +*/ -nsProxyObjectCallInfo::nsProxyObjectCallInfo( nsProxyObject* owner, - nsXPTMethodInfo *methodInfo, - PRUint32 methodIndex, - nsXPTCVariant* parameterList, - PRUint32 parameterCount, - PLEvent *event) +class nsProxyCallEvent : public nsRunnable +{ +public: + nsProxyCallEvent() + : mInfo(nsnull) + {} + + void SetInfo(nsProxyObjectCallInfo *info) + { + mInfo = info; + } + + NS_IMETHOD Run() + { + Invoke(); + + nsProxyObject *proxyObject = mInfo->GetProxyObject(); + if (proxyObject == nsnull) + return NS_OK; + + if (proxyObject->GetProxyType() & PROXY_ASYNC) + { + delete mInfo; + } + else + { + mInfo->PostCompleted(); + } + return NS_OK; + } + + void Invoke() + { + nsProxyObject *proxyObject = mInfo->GetProxyObject(); + if (proxyObject) + { + // invoke the magic of xptc... + nsresult rv = XPTC_InvokeByIndex(proxyObject->GetRealObject(), + mInfo->GetMethodIndex(), + mInfo->GetParameterCount(), + mInfo->GetParameterList()); + mInfo->SetResult(rv); + } + else + { + mInfo->SetResult(NS_ERROR_OUT_OF_MEMORY); + } + } + +private: + nsProxyObjectCallInfo *mInfo; +}; + +class nsProxyCallCompletedEvent : public nsRunnable +{ +public: + nsProxyCallCompletedEvent(nsProxyObjectCallInfo *info) + : mInfo(info) + {} + + NS_IMETHOD Run() + { + mInfo->SetCompleted(); + return NS_OK; + } + +private: + nsProxyObjectCallInfo *mInfo; +}; + +class nsProxyDestructorEvent : public nsRunnable +{ +public: + nsProxyDestructorEvent(nsProxyObject *doomed) + : mDoomed(doomed) + {} + + NS_IMETHOD Run() + { + delete mDoomed; + return NS_OK; + } + +private: + nsProxyObject *mDoomed; +}; + +nsProxyObjectCallInfo::nsProxyObjectCallInfo(nsProxyObject* owner, + nsXPTMethodInfo *methodInfo, + PRUint32 methodIndex, + nsXPTCVariant* parameterList, + PRUint32 parameterCount, + nsIRunnable *event) { NS_ASSERTION(owner, "No nsProxyObject!"); NS_ASSERTION(methodInfo, "No nsXPTMethodInfo!"); @@ -94,9 +185,9 @@ nsProxyObjectCallInfo::nsProxyObjectCallInfo( nsProxyObject* owner, mMethodIndex = methodIndex; mParameterList = parameterList; mParameterCount = parameterCount; - mEvent = event; + mEvent = event; // XXX strong or weak ref? mMethodInfo = methodInfo; - mCallersEventQ = nsnull; + mCallersTarget = nsnull; mOwner = owner; @@ -117,7 +208,7 @@ nsProxyObjectCallInfo::~nsProxyObjectCallInfo() PR_FREEIF(mEvent); if (mParameterList) - free( (void*) mParameterList); + free((void*) mParameterList); } void @@ -137,7 +228,7 @@ nsProxyObjectCallInfo::RefCountInInterfacePointers(PRBool addRef) if (anInterface) { - if(addRef) + if (addRef) anInterface->AddRef(); else anInterface->Release(); @@ -155,7 +246,7 @@ nsProxyObjectCallInfo::CopyStrings(PRBool copy) { const nsXPTParamInfo paramInfo = mMethodInfo->GetParam(i); - if(paramInfo.IsIn()) + if (paramInfo.IsIn()) { const nsXPTType& type = paramInfo.GetType(); uint8 type_tag = type.TagPart(); @@ -236,53 +327,46 @@ nsProxyObjectCallInfo::SetCompleted() void nsProxyObjectCallInfo::PostCompleted() { - if (mCallersEventQ) + if (mCallersTarget) { - PLEvent *event = PR_NEW(PLEvent); - - PL_InitEvent(event, - this, - CompletedEventHandler, - CompletedDestroyHandler); - - mCallersEventQ->PostSynchronousEvent(event, nsnull); - PR_FREEIF(event); + nsCOMPtr event = new nsProxyCallCompletedEvent(this); + // XXX check for out-of-memory! + // XXX dispatch sync will spin the current thread's event queue, which + // may not be what we want! + mCallersTarget->Dispatch(event, NS_DISPATCH_SYNC); } else { - // caller does not have an eventQ? This is an error! + // caller does not have a target? This is an error! SetCompleted(); } } -nsIEventQueue* -nsProxyObjectCallInfo::GetCallersQueue() +nsIDispatchTarget* +nsProxyObjectCallInfo::GetCallersTarget() { - return mCallersEventQ; -} + return mCallersTarget; +} + void -nsProxyObjectCallInfo::SetCallersQueue(nsIEventQueue* queue) +nsProxyObjectCallInfo::SetCallersTarget(nsIDispatchTarget* target) { - mCallersEventQ = queue; + mCallersTarget = target; } - -nsProxyObject::nsProxyObject(nsIEventQueue *destQueue, PRInt32 proxyType, nsISupports *realObject, - nsIEventQueueService* eventQService) +nsProxyObject::nsProxyObject(nsIDispatchTarget *target, PRInt32 proxyType, + nsISupports *realObject) { - mEventQService = eventQService; - mRealObject = realObject; - mDestQueue = do_QueryInterface(destQueue); + mTarget = target; mProxyType = proxyType; } - -nsProxyObject::nsProxyObject(nsIEventQueue *destQueue, PRInt32 proxyType, const nsCID &aClass, - nsISupports *aDelegate, const nsIID &aIID, nsIEventQueueService* eventQService) +#if 0 +nsProxyObject::nsProxyObject(nsIDispatchTarget *target, PRInt32 proxyType, + const nsCID &aClass, nsISupports *aDelegate, + const nsIID &aIID) { - mEventQService = eventQService; - nsCOMPtr compMgr; NS_GetComponentManager(getter_AddRefs(compMgr)); compMgr->CreateInstance(aClass, @@ -290,9 +374,10 @@ nsProxyObject::nsProxyObject(nsIEventQueue *destQueue, PRInt32 proxyType, const aIID, getter_AddRefs(mRealObject)); - mDestQueue = do_QueryInterface(destQueue); + mTarget = do_QueryInterface(destQueue); mProxyType = proxyType; } +#endif nsProxyObject::~nsProxyObject() { @@ -300,7 +385,7 @@ nsProxyObject::~nsProxyObject() // do not remove assignments. mRealObject = 0; - mDestQueue = 0; + mTarget = 0; } @@ -314,91 +399,108 @@ nsProxyObject::AddRef() void nsProxyObject::Release(void) { - NS_PRECONDITION(0 != mRefCnt, "dup release"); + NS_PRECONDITION(0 != mRefCnt, "dup release"); - nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); - NS_LOG_RELEASE(this, count, "nsProxyObject"); + nsrefcnt count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); + NS_LOG_RELEASE(this, count, "nsProxyObject"); - if (count == 0) - { - mRefCnt = 1; /* stabilize */ + if (count == 0) + { + mRefCnt = 1; /* stabilize */ - PRBool callDirectly; - mDestQueue->IsOnCurrentThread(&callDirectly); + PRBool callDirectly; + mTarget->IsOnCurrentThread(&callDirectly); - if (callDirectly) - { - delete this; - return; - } + if (callDirectly) + { + delete this; + return; + } - // need to do something special here so that - // the real object will always be deleted on - // the correct thread.. + // need to do something special here so that + // the real object will always be deleted on + // the correct thread.. - PLEvent *event = PR_NEW(PLEvent); - if (event == nsnull) - { - NS_ASSERTION(0, "Could not create a plevent. Leaking nsProxyObject!"); - return; // if this happens we are going to leak. - } - - PL_InitEvent(event, - this, - ProxyDestructorEventHandler, - ProxyDestructorDestroyHandler); + /* + PLEvent *event = PR_NEW(PLEvent); + if (event == nsnull) + { + NS_ASSERTION(0, "Could not create a plevent. Leaking nsProxyObject!"); + return; // if this happens we are going to leak. + } - mDestQueue->PostEvent(event); - } -} + PL_InitEvent(event, + this, + ProxyDestructorEventHandler, + ProxyDestructorDestroyHandler); + */ + + nsCOMPtr event = new nsProxyDestructorEvent(this); + if (event == nsnull) + { + NS_NOTREACHED("Leaking nsProxyObject!"); + return; // if this happens we are going to leak. + } + + mTarget->Dispatch(event, NS_DISPATCH_NORMAL); + } +} nsresult nsProxyObject::PostAndWait(nsProxyObjectCallInfo *proxyInfo) { - if (proxyInfo == nsnull || mEventQService == nsnull) + if (proxyInfo == nsnull) return NS_ERROR_NULL_POINTER; PRBool eventLoopCreated = PR_FALSE; nsresult rv; - nsCOMPtr eventQ; - rv = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQ)); - if (NS_FAILED(rv)) + nsCOMPtr thread; + nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); + if (!thread) { - rv = mEventQService->CreateMonitoredThreadEventQueue(); - eventLoopCreated = PR_TRUE; - if (NS_FAILED(rv)) - return rv; + // XXX create a nsIThread wrapper for this thread. + + /* + static int sWrapperCount = 0; + nsCString name; + name.AssignLiteral("xpcom.proxy-"); + name.AppendInt(sDummyCount++); + + nsThread *dummy = new nsThread(name); + if (!dummy) + return NS_ERROR_OUT_OF_MEMORY; + dummy->InitCurrentThread(); - rv = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQ)); + thread = dummy; + */ } if (NS_FAILED(rv)) return rv; - proxyInfo->SetCallersQueue(eventQ); + proxyInfo->SetCallersTarget(thread); - PLEvent* event = proxyInfo->GetPLEvent(); + nsIRunnable* event = proxyInfo->GetEvent(); if (!event) return NS_ERROR_NULL_POINTER; - mDestQueue->PostEvent(event); + mTarget->Dispatch(event, NS_DISPATCH_NORMAL); - while (! proxyInfo->GetCompleted()) + while (!proxyInfo->GetCompleted()) { - PLEvent *nextEvent; - rv = eventQ->WaitForEvent(&nextEvent); + rv = thread->RunNextTask(nsIThread::RUN_NORMAL); if (NS_FAILED(rv)) break; - - eventQ->HandleEvent(nextEvent); } + /* if (eventLoopCreated) { mEventQService->DestroyThreadEventQueue(); eventQ = 0; } + */ return rv; } @@ -444,7 +546,7 @@ nsProxyObject::Post( PRUint32 methodIndex, { nsresult rv = NS_OK; - if (! mDestQueue || ! mRealObject) + if (! mTarget || ! mRealObject) return NS_ERROR_OUT_OF_MEMORY; if (methodInfo->IsNotXPCOM()) @@ -461,10 +563,10 @@ nsProxyObject::Post( PRUint32 methodIndex, // see if we should call into the method directly. Either it is a QI function call // (methodIndex == 0), or it is a sync proxy and this code is running on the same thread - // as the destination event queue. + // as the destination dispatch target. if ( (methodIndex == 0) || (mProxyType & PROXY_SYNC && - NS_SUCCEEDED(mDestQueue->IsOnCurrentThread(&callDirectly)) && + NS_SUCCEEDED(mTarget->IsOnCurrentThread(&callDirectly)) && callDirectly)) { @@ -479,6 +581,7 @@ nsProxyObject::Post( PRUint32 methodIndex, return rv; } + /* PLEvent *event = PR_NEW(PLEvent); if (event == nsnull) { @@ -486,26 +589,30 @@ nsProxyObject::Post( PRUint32 methodIndex, free(fullParam); return NS_ERROR_OUT_OF_MEMORY; } - - nsProxyObjectCallInfo *proxyInfo = new nsProxyObjectCallInfo(this, - methodInfo, - methodIndex, - fullParam, // will be deleted by ~() - paramCount, - event); // will be deleted by ~() +*/ + nsRefPtr event = new nsProxyCallEvent(); + if (!event) { + if (fullParam) + free(fullParam); + return NS_ERROR_OUT_OF_MEMORY; + } - if (proxyInfo == nsnull) { - PR_DELETE(event); + nsProxyObjectCallInfo *proxyInfo = + new nsProxyObjectCallInfo(this, + methodInfo, + methodIndex, + fullParam, // will be deleted by ~() + paramCount, + event); // will be deleted by ~() + + if (!proxyInfo) { if (fullParam) free(fullParam); return NS_ERROR_OUT_OF_MEMORY; } - PL_InitEvent(event, - proxyInfo, - EventHandler, - DestroyHandler); - + event->SetInfo(proxyInfo); + if (mProxyType & PROXY_SYNC) { rv = PostAndWait(proxyInfo); @@ -518,76 +625,8 @@ nsProxyObject::Post( PRUint32 methodIndex, if (mProxyType & PROXY_ASYNC) { - mDestQueue->PostEvent(event); + mTarget->Dispatch(event, NS_DISPATCH_NORMAL); return NS_OK; } return NS_ERROR_UNEXPECTED; } - - - -static void DestroyHandler(PLEvent *self) -{ - nsProxyObjectCallInfo* owner = (nsProxyObjectCallInfo*)PL_GetEventOwner(self); - nsProxyObject* proxyObject = owner->GetProxyObject(); - - if (proxyObject == nsnull) - return; - - if (proxyObject->GetProxyType() & PROXY_ASYNC) - { - delete owner; - } - else - { - owner->PostCompleted(); - } - -} - -static void* EventHandler(PLEvent *self) -{ - nsProxyObjectCallInfo *info = (nsProxyObjectCallInfo*)PL_GetEventOwner(self); - NS_ASSERTION(info, "No nsProxyObjectCallInfo!"); - - nsProxyObject *proxyObject = info->GetProxyObject(); - - if (proxyObject) - { - // invoke the magic of xptc... - nsresult rv = XPTC_InvokeByIndex( proxyObject->GetRealObject(), - info->GetMethodIndex(), - info->GetParameterCount(), - info->GetParameterList()); - info->SetResult(rv); - } - else - { - info->SetResult(NS_ERROR_OUT_OF_MEMORY); - } - return NULL; -} - -static void CompletedDestroyHandler(PLEvent *self) -{ -} - -static void* CompletedEventHandler(PLEvent *self) -{ - nsProxyObjectCallInfo* owner = (nsProxyObjectCallInfo*)PL_GetEventOwner(self); - owner->SetCompleted(); - return nsnull; -} - -static void* ProxyDestructorEventHandler(PLEvent *self) -{ - nsProxyObject* owner = (nsProxyObject*) PL_GetEventOwner(self); - NS_DELETEXPCOM(owner); - return nsnull; -} - -static void ProxyDestructorDestroyHandler(PLEvent *self) -{ - PR_DELETE(self); -} - diff --git a/mozilla/xpcom/proxy/src/nsProxyEventClass.cpp b/mozilla/xpcom/proxy/src/nsProxyEventClass.cpp index 769a973fb7e..bf30578a9b5 100644 --- a/mozilla/xpcom/proxy/src/nsProxyEventClass.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyEventClass.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 ci et: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -76,12 +77,13 @@ nsProxyEventClass::GetNewOrUsedClass(REFNSIID aIID) /* find in our hash table */ nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance(); - if (manager == nsnull) return nsnull; + if (manager == nsnull) + return nsnull; - // don't need to lock the map, because this is only called - // by nsProxyEventClass::GetNewOrUsed which locks it for us. + // don't need to lock the map, because this is only called by + // nsProxyEventClass::GetNewOrUsed which locks it for us. - nsHashtable *iidToClassMap = manager->GetIIDToProxyClassMap(); + nsHashtable *iidToClassMap = manager->GetIIDToProxyClassMap(); if (iidToClassMap == nsnull) { @@ -94,7 +96,7 @@ nsProxyEventClass::GetNewOrUsedClass(REFNSIID aIID) #ifdef PROXYEVENTCLASS_DEBUG char* iidStr = aIID.ToString(); printf("GetNewOrUsedClass %s\n", iidStr); - nsCRT::free(iidStr); + PR_Free(iidStr); #endif clazz = (nsProxyEventClass*) iidToClassMap->Get(&key); @@ -104,7 +106,7 @@ nsProxyEventClass::GetNewOrUsedClass(REFNSIID aIID) #ifdef PROXYEVENTCLASS_DEBUG char* iidStr = aIID.ToString(); printf("GetNewOrUsedClass %s hit\n", iidStr); - nsCRT::free(iidStr); + PR_Free(iidStr); #endif } else @@ -142,7 +144,7 @@ nsProxyEventClass::GetNewOrUsedClass(REFNSIID aIID) if (isISupportsDescendent) { clazz = new nsProxyEventClass(aIID, info); - if(!clazz->mDescriptors) + if (!clazz->mDescriptors) NS_RELEASE(clazz); // sets clazz to NULL } } @@ -153,12 +155,12 @@ nsProxyEventClass::GetNewOrUsedClass(REFNSIID aIID) nsProxyEventClass::nsProxyEventClass() { - NS_WARNING("This constructor should never be called"); + NS_NOTREACHED("This constructor should never be called"); } nsProxyEventClass::nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo) -: mIID(aIID), - mDescriptors(NULL) + : mIID(aIID), + mDescriptors(NULL) { NS_ADDREF_THIS(); @@ -168,7 +170,8 @@ nsProxyEventClass::nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo) nsIDKey key(aIID); nsProxyObjectManager *manager = nsProxyObjectManager::GetInstance(); - if (manager == nsnull) return; + if (manager == nsnull) + return; // don't need to lock the map, because this is only called // by GetNewOrUsed which locks it for us. @@ -184,7 +187,7 @@ nsProxyEventClass::nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo) #ifdef PROXYEVENTCLASS_DEBUG char* iidStr = aIID.ToString(); printf("GetNewOrUsedClass %s put\n", iidStr); - nsCRT::free(iidStr); + PR_Free(iidStr); #endif } @@ -208,7 +211,7 @@ nsProxyEventClass::nsProxyEventClass(REFNSIID aIID, nsIInterfaceInfo* aInfo) nsProxyEventClass::~nsProxyEventClass() { - if(mDescriptors && mDescriptors != &zero_methods_descriptor) + if (mDescriptors && mDescriptors != &zero_methods_descriptor) delete [] mDescriptors; if (nsProxyObjectManager::IsManagerShutdown()) @@ -227,7 +230,7 @@ nsProxyEventClass::~nsProxyEventClass() #ifdef PROXYEVENTCLASS_DEBUG char* iidStr = mIID.ToString(); printf("GetNewOrUsedClass %s remove\n", iidStr); - nsCRT::free(iidStr); + PR_Free(iidStr); #endif } #endif @@ -242,7 +245,6 @@ nsProxyEventClass::CallQueryInterfaceOnProxy(nsProxyEventObject* self, REFNSIID *aInstancePtr = nsnull; // in case of error. - // The functions we will call: QueryInterface(REFNSIID aIID, void** aInstancePtr) nsXPTCMiniVariant var[2]; @@ -278,7 +280,7 @@ nsProxyEventClass::CallQueryInterfaceOnProxy(nsProxyEventObject* self, REFNSIID return NS_ERROR_FAILURE; } - rv = manager->GetProxyForObject(self->GetQueue(), + rv = manager->GetProxyForObject(self->GetTarget(), aIID, self->GetRealObject(), self->GetProxyType(), @@ -311,7 +313,7 @@ public: static ProxyEventClassIdentity* singleton = NULL; if(!singleton) singleton = new ProxyEventClassIdentity(); - return (void*) singleton; + return singleton; } }; @@ -333,41 +335,41 @@ nsProxyEventClass::DelegatedQueryInterface(nsProxyEventObject* self, nsProxyEventObject* sibling; { - nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance(); - nsAutoMonitor mon(manager->GetMonitor()); + nsProxyObjectManager* manager = nsProxyObjectManager::GetInstance(); + nsAutoMonitor mon(manager->GetMonitor()); - // This includes checking for nsISupports and the iid of self. - // And it also checks for other wrappers that have been constructed - // for this object. - if(nsnull != (sibling = self->LockedFind(aIID))) - { - NS_ADDREF(sibling); - *aInstancePtr = (void*) sibling; - return NS_OK; - } - - // check if asking for an interface that we inherit from - nsCOMPtr current = GetInterfaceInfo(); - nsCOMPtr parent; - - while(NS_SUCCEEDED(current->GetParent(getter_AddRefs(parent))) && parent) - { - current = parent; - - nsIID* iid; - if(NS_SUCCEEDED(current->GetInterfaceIID(&iid)) && iid) + // This includes checking for nsISupports and the iid of self. + // And it also checks for other wrappers that have been constructed + // for this object. + if (nsnull != (sibling = self->LockedFind(aIID))) { - PRBool found = aIID.Equals(*iid); - nsMemory::Free(iid); - if(found) + NS_ADDREF(sibling); + *aInstancePtr = (void*) sibling; + return NS_OK; + } + + // check if asking for an interface that we inherit from + nsCOMPtr current = GetInterfaceInfo(); + nsCOMPtr parent; + + while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(parent))) && parent) + { + current = parent; + + nsIID* iid; + if (NS_SUCCEEDED(current->GetInterfaceIID(&iid)) && iid) { - *aInstancePtr = (void*) self; - NS_ADDREF(self); - return NS_OK; + PRBool found = aIID.Equals(*iid); + nsMemory::Free(iid); + if (found) + { + *aInstancePtr = (void*) self; + NS_ADDREF(self); + return NS_OK; + } } } } - } return CallQueryInterfaceOnProxy(self, aIID, (nsProxyEventObject**)aInstancePtr); } diff --git a/mozilla/xpcom/proxy/src/nsProxyEventObject.cpp b/mozilla/xpcom/proxy/src/nsProxyEventObject.cpp index 5c8cfa3aef7..ff1a785d738 100644 --- a/mozilla/xpcom/proxy/src/nsProxyEventObject.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyEventObject.cpp @@ -44,7 +44,6 @@ #include "nsIProxyObjectManager.h" #include "nsProxyEventPrivate.h" -#include "nsIEventQueueService.h" #include "nsServiceManagerUtils.h" #include "nsHashtable.h" @@ -55,36 +54,35 @@ #include "nsAutoLock.h" static NS_DEFINE_IID(kProxyObject_Identity_Class_IID, NS_PROXYEVENT_IDENTITY_CLASS_IID); -static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); //////////////////////////////////////////////////////////////////////////////// class nsProxyEventKey : public nsHashKey { public: - nsProxyEventKey(void* rootObjectKey, void* destQueueKey, PRInt32 proxyType) - : mRootObjectKey(rootObjectKey), mDestQueueKey(destQueueKey), mProxyType(proxyType) { + nsProxyEventKey(void* rootObjectKey, void* targetKey, PRInt32 proxyType) + : mRootObjectKey(rootObjectKey), mTargetKey(targetKey), mProxyType(proxyType) { } PRUint32 HashCode(void) const { return NS_PTR_TO_INT32(mRootObjectKey) ^ - NS_PTR_TO_INT32(mDestQueueKey) ^ mProxyType; + NS_PTR_TO_INT32(mTargetKey) ^ mProxyType; } PRBool Equals(const nsHashKey *aKey) const { const nsProxyEventKey* other = (const nsProxyEventKey*)aKey; return mRootObjectKey == other->mRootObjectKey - && mDestQueueKey == other->mDestQueueKey + && mTargetKey == other->mTargetKey && mProxyType == other->mProxyType; } nsHashKey *Clone() const { - return new nsProxyEventKey(mRootObjectKey, mDestQueueKey, mProxyType); + return new nsProxyEventKey(mRootObjectKey, mTargetKey, mProxyType); } protected: void* mRootObjectKey; - void* mDestQueueKey; + void* mTargetKey; PRInt32 mProxyType; }; @@ -127,7 +125,7 @@ nsProxyEventObject::DebugDump(const char * message, PRUint32 hashKey) printf("%s wrapper around @ %x\n", isRoot ? "ROOT":"non-root\n", GetRealObject()); nsCOMPtr rootObject = do_QueryInterface(mProxyObject->mRealObject); - nsCOMPtr rootQueue = do_QueryInterface(mProxyObject->mDestQueue); + nsCOMPtr rootQueue = do_QueryInterface(mProxyObject->mTarget); nsProxyEventKey key(rootObject, rootQueue, mProxyObject->mProxyType); printf("Hashkey: %d\n", key.HashCode()); @@ -167,7 +165,7 @@ nsProxyEventObject::DebugDump(const char * message, PRUint32 hashKey) // ////////////////////////////////////////////////////////////////////////////////////////////////// nsProxyEventObject* -nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue, +nsProxyEventObject::GetNewOrUsedProxy(nsIDispatchTarget *target, PRInt32 proxyType, nsISupports *aObj, REFNSIID aIID) @@ -199,7 +197,7 @@ nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue, // if you hit this assertion, you might want to check out how // you are using proxies. You shouldn't need to be creating // a proxy from a proxy. -- dougt@netscape.com - NS_ASSERTION(0, "Someone is building a proxy from a proxy"); + NS_WARNING("Someone is building a proxy from a proxy"); NS_ASSERTION(identificationObject, "where did my identification object go!"); if (!identificationObject) { @@ -229,7 +227,7 @@ nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue, // Get the root nsISupports of the event queue... This is used later, // as part of the hashtable key... - nsCOMPtr destQRoot = do_QueryInterface(destQueue, &rv); + nsCOMPtr destQRoot = do_QueryInterface(target, &rv); if (NS_FAILED(rv) || !destQRoot) { return nsnull; } @@ -247,10 +245,6 @@ nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue, return nsnull; } - nsCOMPtr eventQService(do_GetService(kEventQueueServiceCID, &rv)); - if (NS_FAILED(rv)) - return nsnull; - nsAutoMonitor mon(manager->GetMonitor()); // Get the hash table containing root proxy objects... @@ -299,12 +293,11 @@ nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue, return nsnull; } - peo = new nsProxyEventObject(destQueue, + peo = new nsProxyEventObject(target, proxyType, rootObject, rootClazz, - nsnull, - eventQService); + nsnull); if(!peo) { // Ouch... Out of memory! return nsnull; @@ -352,12 +345,11 @@ nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue, return nsnull; } - peo = new nsProxyEventObject(destQueue, + peo = new nsProxyEventObject(target, proxyType, rawInterface, proxyClazz, - rootProxy, - eventQService); + rootProxy); if (!peo) { // Ouch... Out of memory! return nsnull; @@ -402,19 +394,18 @@ nsProxyEventObject::nsProxyEventObject() NS_WARNING("This constructor should never be called"); } -nsProxyEventObject::nsProxyEventObject(nsIEventQueue *destQueue, +nsProxyEventObject::nsProxyEventObject(nsIDispatchTarget *target, PRInt32 proxyType, nsISupports* aObj, nsProxyEventClass* aClass, - nsProxyEventObject* root, - nsIEventQueueService* eventQService) + nsProxyEventObject* root) : mClass(aClass), mRoot(root), mNext(nsnull) { NS_IF_ADDREF(mRoot); - mProxyObject = new nsProxyObject(destQueue, proxyType, aObj, eventQService); + mProxyObject = new nsProxyObject(target, proxyType, aObj); #ifdef DEBUG_xpcom_proxy DebugDump("Create", 0); @@ -456,7 +447,7 @@ nsProxyEventObject::~nsProxyEventObject() if (realToProxyMap != nsnull) { nsCOMPtr rootObject = do_QueryInterface(mProxyObject->mRealObject); - nsCOMPtr rootQueue = do_QueryInterface(mProxyObject->mDestQueue); + nsCOMPtr rootQueue = do_QueryInterface(mProxyObject->mTarget); nsProxyEventKey key(rootObject, rootQueue, mProxyObject->mProxyType); #ifdef DEBUG_dougt void* value = diff --git a/mozilla/xpcom/proxy/src/nsProxyEventPrivate.h b/mozilla/xpcom/proxy/src/nsProxyEventPrivate.h index 07ad7141dac..c24d309c315 100644 --- a/mozilla/xpcom/proxy/src/nsProxyEventPrivate.h +++ b/mozilla/xpcom/proxy/src/nsProxyEventPrivate.h @@ -36,20 +36,19 @@ * * ***** END LICENSE BLOCK ***** */ -#ifndef __nsProxyEventPrivate_h_ -#define __nsProxyEventPrivate_h_ +#ifndef nsProxyEventPrivate_h__ +#define nsProxyEventPrivate_h__ #include "nscore.h" #include "nsISupports.h" #include "nsIFactory.h" #include "nsHashtable.h" -#include "plevent.h" #include "xptcall.h" // defines nsXPTCVariant -#include "nsIEventQueue.h" #include "nsProxyEvent.h" #include "nsIProxyObjectManager.h" +#include "prmon.h" class nsProxyEventObject; class nsProxyEventClass; @@ -113,7 +112,7 @@ public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_PROXYEVENT_OBJECT_IID) - static nsProxyEventObject* GetNewOrUsedProxy(nsIEventQueue *destQueue, + static nsProxyEventObject* GetNewOrUsedProxy(nsIDispatchTarget *target, PRInt32 proxyType, nsISupports *aObj, REFNSIID aIID); @@ -126,17 +125,16 @@ public: nsProxyEventClass* GetClass() const { return mClass; } - nsIEventQueue* GetQueue() const { return (mProxyObject ? mProxyObject->GetQueue() : nsnull);} + nsIDispatchTarget* GetTarget() const { return (mProxyObject ? mProxyObject->GetTarget() : nsnull);} nsISupports* GetRealObject() const { return (mProxyObject ? mProxyObject->GetRealObject(): nsnull);} PRInt32 GetProxyType() const { return (mProxyObject ? mProxyObject->GetProxyType() : nsnull);} nsProxyEventObject(); - nsProxyEventObject(nsIEventQueue *destQueue, + nsProxyEventObject(nsIDispatchTarget *target, PRInt32 proxyType, nsISupports* aObj, nsProxyEventClass* aClass, - nsProxyEventObject* root, - nsIEventQueueService* eventQService); + nsProxyEventObject* root); nsProxyEventObject* LockedFind(REFNSIID aIID); @@ -199,4 +197,4 @@ private: }; -#endif +#endif // nsProxyEventPrivate_h__ diff --git a/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp b/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp index 566c4e8199d..69ae62618f3 100644 --- a/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyObjectManager.cpp @@ -57,10 +57,10 @@ #include "nsIServiceManager.h" #include "nsCOMPtr.h" -#include "nsIEventQueueService.h" #include "nsIThread.h" +#if 0 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); /***************************************************************************/ @@ -102,6 +102,7 @@ NS_IMETHODIMP nsProxyCreateInstance::CreateInstanceByContractID(const char *aCon iid, result); } +#endif ///////////////////////////////////////////////////////////////////////// // nsProxyObjectManager @@ -118,20 +119,20 @@ nsProxyObjectManager::nsProxyObjectManager() mProxyCreationMonitor = PR_NewMonitor(); } -static PRBool PurgeProxyClasses(nsHashKey *aKey, void *aData, void* closure) +static PRIntn +PurgeProxyClasses(nsHashKey *aKey, void *aData, void* closure) { - nsProxyEventClass* ptr = NS_REINTERPRET_CAST(nsProxyEventClass*, aData); + nsProxyEventClass* ptr = NS_STATIC_CAST(nsProxyEventClass*, aData); NS_RELEASE(ptr); - return PR_TRUE; + return kHashEnumerateNext; } nsProxyObjectManager::~nsProxyObjectManager() { - mProxyClassMap.Reset((nsHashtableEnumFunc)PurgeProxyClasses, nsnull); + mProxyClassMap.Reset(PurgeProxyClasses, nsnull); - if (mProxyCreationMonitor) { + if (mProxyCreationMonitor) PR_DestroyMonitor(mProxyCreationMonitor); - } nsProxyObjectManager::mInstance = nsnull; } @@ -139,92 +140,71 @@ nsProxyObjectManager::~nsProxyObjectManager() PRBool nsProxyObjectManager::IsManagerShutdown() { - if (mInstance) - return PR_FALSE; - return PR_TRUE; + return mInstance == nsnull; } nsProxyObjectManager * nsProxyObjectManager::GetInstance() { - if (! mInstance) - { + if (!mInstance) mInstance = new nsProxyObjectManager(); - } return mInstance; } - void nsProxyObjectManager::Shutdown() { mInstance = nsnull; } - -// Helpers NS_IMETHODIMP -nsProxyObjectManager::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr) +nsProxyObjectManager::Create(nsISupports* outer, const nsIID& aIID, + void* *aInstancePtr) { nsProxyObjectManager *proxyObjectManager = GetInstance(); - - if (proxyObjectManager == nsnull) + if (!proxyObjectManager) return NS_ERROR_OUT_OF_MEMORY; return proxyObjectManager->QueryInterface(aIID, aInstancePtr); } - NS_IMETHODIMP -nsProxyObjectManager::GetProxyForObject(nsIEventQueue *destQueue, +nsProxyObjectManager::GetProxyForObject(nsIDispatchTarget* aTarget, REFNSIID aIID, nsISupports* aObj, PRInt32 proxyType, void** aProxyObject) { - if (!aObj) return NS_ERROR_NULL_POINTER; - if (!aProxyObject) return NS_ERROR_NULL_POINTER; + NS_ENSURE_ARG_POINTER(aTarget); + NS_ENSURE_ARG_POINTER(aObj); nsresult rv; - nsCOMPtr postQ; - *aProxyObject = nsnull; - // check to see if the destination Q is a special case. + // check to see if the target is on our thread. If so, just return the + // real object. - nsCOMPtr eventQService = - do_GetService(kEventQueueServiceCID, &rv); - if (NS_FAILED(rv)) - return rv; - - rv = eventQService->ResolveEventQueue(destQueue, getter_AddRefs(postQ)); - if (NS_FAILED(rv)) - return rv; - - // check to see if the eventQ is on our thread. If so, just return the real object. - - if (postQ && !(proxyType & PROXY_ASYNC) && !(proxyType & PROXY_ALWAYS)) + if (!(proxyType & PROXY_ASYNC) && !(proxyType & PROXY_ALWAYS)) { - PRBool aResult; - postQ->IsOnCurrentThread(&aResult); + PRBool result; + aTarget->IsOnCurrentThread(&result); - if (aResult) - { + if (result) return aObj->QueryInterface(aIID, aProxyObject); - } } // check to see if proxy is there or not. - *aProxyObject = nsProxyEventObject::GetNewOrUsedProxy(postQ, proxyType, aObj, aIID); - - if (*aProxyObject == nsnull) - return NS_ERROR_NO_INTERFACE; //fix error code? + *aProxyObject = nsProxyEventObject::GetNewOrUsedProxy(aTarget, proxyType, + aObj, aIID); + if (!*aProxyObject) + return NS_ERROR_UNEXPECTED; - return NS_OK; -} + return NS_OK; +} +#if 0 NS_IMETHODIMP nsProxyObjectManager::GetProxy( nsIEventQueue *destQueue, const nsCID &aClass, @@ -292,6 +272,7 @@ nsProxyObjectManager::GetProxy( nsIEventQueue *destQueue, return rv; } +#endif /** * Helper function for code that already has a link-time dependency on @@ -301,7 +282,7 @@ nsProxyObjectManager::GetProxy( nsIEventQueue *destQueue, * readable. */ NS_COM nsresult -NS_GetProxyForObject(nsIEventQueue *destQueue, +NS_GetProxyForObject(nsIDispatchTarget *target, REFNSIID aIID, nsISupports* aObj, PRInt32 proxyType, @@ -309,18 +290,17 @@ NS_GetProxyForObject(nsIEventQueue *destQueue, { static NS_DEFINE_CID(proxyObjMgrCID, NS_PROXYEVENT_MANAGER_CID); - nsresult rv; // temp for return value + nsresult rv; // get the proxy object manager // nsCOMPtr proxyObjMgr = do_GetService(proxyObjMgrCID, &rv); - if (NS_FAILED(rv)) - return NS_ERROR_FAILURE; + return rv; // and try to get the proxy object // - return proxyObjMgr->GetProxyForObject(destQueue, aIID, aObj, + return proxyObjMgr->GetProxyForObject(target, aIID, aObj, proxyType, aProxyObject); } diff --git a/mozilla/xpcom/proxy/src/nsProxyRelease.cpp b/mozilla/xpcom/proxy/src/nsProxyRelease.cpp index 3ceeb6674bf..6a809e2ace8 100644 --- a/mozilla/xpcom/proxy/src/nsProxyRelease.cpp +++ b/mozilla/xpcom/proxy/src/nsProxyRelease.cpp @@ -1,21 +1,65 @@ +/* vim:set ts=4 sw=4 sts=4 et cin: */ +/* ***** 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 + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2003 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + #include "nsProxyRelease.h" +#include "nsRunnable.h" +#include "nsAutoPtr.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) +class nsProxyReleaseEvent : public nsRunnable { - delete self; -} +public: + nsProxyReleaseEvent(nsISupports *doomed) + : mDoomed(doomed) { + } + + NS_IMETHOD Run() + { + mDoomed->Release(); + return NS_OK; + } + +private: + nsISupports *mDoomed; +}; NS_COM nsresult -NS_ProxyRelease(nsIEventTarget *target, nsISupports *doomed, PRBool alwaysProxy) +NS_ProxyRelease(nsIDispatchTarget *target, nsISupports *doomed, + PRBool alwaysProxy) { nsresult rv; @@ -33,21 +77,16 @@ NS_ProxyRelease(nsIEventTarget *target, nsISupports *doomed, PRBool alwaysProxy) } } - PLEvent *ev = new PLEvent; + nsRefPtr ev = new nsProxyReleaseEvent(doomed); 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); + rv = target->Dispatch(ev, NS_DISPATCH_NORMAL); 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. } diff --git a/mozilla/xpcom/tests/TestPipes.cpp b/mozilla/xpcom/tests/TestPipes.cpp index 644c8a9bf3d..523965be8b5 100644 --- a/mozilla/xpcom/tests/TestPipes.cpp +++ b/mozilla/xpcom/tests/TestPipes.cpp @@ -41,6 +41,7 @@ #include "nsIInputStream.h" #include "nsIOutputStream.h" #include "nsIServiceManager.h" +#include "nsThreadManager.h" #include "prprf.h" #include "prinrval.h" #include "plstr.h" @@ -134,7 +135,8 @@ TestPipe(nsIInputStream* in, nsIOutputStream* out) if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(receiver); - rv = NS_NewThread(&thread, receiver, 0, PR_JOINABLE_THREAD); + rv = nsThreadManager::NewThread(NS_LITERAL_CSTRING("TestPipe"), + receiver, &thread); if (NS_FAILED(rv)) return rv; PRUint32 total = 0; @@ -160,7 +162,7 @@ TestPipe(nsIInputStream* in, nsIOutputStream* out) PRIntervalTime end = PR_IntervalNow(); - thread->Join(); + thread->Shutdown(); printf("wrote %d bytes, time = %dms\n", total, PR_IntervalToMilliseconds(end - start)); @@ -247,7 +249,8 @@ TestShortWrites(nsIInputStream* in, nsIOutputStream* out) if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(receiver); - rv = NS_NewThread(&thread, receiver, 0, PR_JOINABLE_THREAD); + rv = nsThreadManager::NewThread(NS_LITERAL_CSTRING("TestShortWrites"), + receiver, &thread); if (NS_FAILED(rv)) return rv; PRUint32 total = 0; @@ -274,7 +277,7 @@ TestShortWrites(nsIInputStream* in, nsIOutputStream* out) rv = out->Close(); if (NS_FAILED(rv)) return rv; - thread->Join(); + thread->Shutdown(); printf("wrote %d bytes\n", total); NS_RELEASE(thread); @@ -472,7 +475,8 @@ TestChainedPipes() if (pump == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(pump); - rv = NS_NewThread(&thread, pump, 0, PR_JOINABLE_THREAD); + rv = nsThreadManager::NewThread(NS_LITERAL_CSTRING("TestChainedPipes.pump"), + pump, &thread); if (NS_FAILED(rv)) return rv; nsIThread* receiverThread; @@ -480,7 +484,8 @@ TestChainedPipes() if (receiver == nsnull) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(receiver); - rv = NS_NewThread(&receiverThread, receiver, 0, PR_JOINABLE_THREAD); + rv = nsThreadManager::NewThread(NS_LITERAL_CSTRING("TestChainedPipes.receiver"), + receiver, &receiverThread); if (NS_FAILED(rv)) return rv; PRUint32 total = 0; @@ -504,8 +509,8 @@ TestChainedPipes() rv = out1->Close(); if (NS_FAILED(rv)) return rv; - thread->Join(); - receiverThread->Join(); + thread->Shutdown(); + receiverThread->Shutdown(); NS_RELEASE(thread); NS_RELEASE(pump); diff --git a/mozilla/xpcom/tests/TestThreads.cpp b/mozilla/xpcom/tests/TestThreads.cpp index 4caac3ae73d..d08cf3366c6 100644 --- a/mozilla/xpcom/tests/TestThreads.cpp +++ b/mozilla/xpcom/tests/TestThreads.cpp @@ -38,6 +38,7 @@ #include "nsIThread.h" #include "nsIRunnable.h" +#include "nsThreadManager.h" #include #include #include "nspr.h" @@ -51,7 +52,7 @@ public: NS_IMETHOD Run() { nsCOMPtr thread; - nsresult rv = nsIThread::GetCurrent(getter_AddRefs(thread)); + nsresult rv = nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); if (NS_FAILED(rv)) { printf("failed to get current thread\n"); return rv; @@ -80,30 +81,34 @@ TestThreads() nsresult rv; nsCOMPtr runner; - rv = NS_NewThread(getter_AddRefs(runner), new nsRunner(0), 0, PR_JOINABLE_THREAD); + rv = nsThreadManager::NewThread(NS_LITERAL_CSTRING("TestThreads"), + new nsRunner(0), getter_AddRefs(runner)); if (NS_FAILED(rv)) { printf("failed to create thread\n"); return rv; } nsCOMPtr thread; - rv = nsIThread::GetCurrent(getter_AddRefs(thread)); + rv = nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); if (NS_FAILED(rv)) { printf("failed to get current thread\n"); return rv; } +#if 0 PRThreadScope scope; rv = runner->GetScope(&scope); if (NS_FAILED(rv)) { printf("runner already exited\n"); } +#endif - rv = runner->Join(); // wait for the runner to die before quitting + rv = runner->Shutdown(); // wait for the runner to die before quitting if (NS_FAILED(rv)) { printf("join failed\n"); } +#if 0 rv = runner->GetScope(&scope); // this should fail after Join if (NS_SUCCEEDED(rv)) { printf("get scope failed\n"); @@ -126,6 +131,7 @@ TestThreads() if (NS_SUCCEEDED(rv)) { printf("shouldn't have been able to join an unjoinable thread\n"); } +#endif PR_Sleep(PR_MillisecondsToInterval(100)); // hopefully the runner will quit here @@ -181,15 +187,16 @@ static int Stress(int loops, int threads) for (k = 0; k < threads; k++) { nsCOMPtr t; - nsresult rv = NS_NewThread(getter_AddRefs(t), - new nsStressRunner(k), - 0, PR_JOINABLE_THREAD); + nsCString name("thread:"); + name.AppendInt(k); + nsresult rv = nsThreadManager::NewThread(name, new nsStressRunner(k), + getter_AddRefs(t)); NS_ASSERTION(NS_SUCCEEDED(rv), "can't create thread"); NS_ADDREF(array[k] = t); } for (k = threads-1; k >= 0; k--) { - array[k]->Join(); + array[k]->Shutdown(); NS_RELEASE(array[k]); } delete [] array; diff --git a/mozilla/xpcom/threads/Makefile.in b/mozilla/xpcom/threads/Makefile.in index 2cb9bc61a04..21e73ff2710 100644 --- a/mozilla/xpcom/threads/Makefile.in +++ b/mozilla/xpcom/threads/Makefile.in @@ -55,37 +55,34 @@ MOZILLA_INTERNAL_API = 1 REQUIRES = string \ $(NULL) -CSRCS = \ - plevent.c \ - $(NULL) - CPPSRCS = \ nsAutoLock.cpp \ + nsTaskQueue.cpp \ nsEnvironment.cpp \ - nsEventQueue.cpp \ - nsEventQueueService.cpp \ + nsRunnable.cpp \ nsThread.cpp \ - nsTimerImpl.cpp \ + nsThreadManager.cpp \ nsProcessCommon.cpp \ + nsTimerImpl.cpp \ TimerThread.cpp \ $(NULL) EXPORTS = \ nsAutoLock.h \ - plevent.h \ nsProcess.h \ - nsEventQueueUtils.h \ + nsRunnable.h \ + nsThreadManager.h \ $(NULL) XPIDLSRCS = \ + nsIDispatchTarget.idl \ nsIThread.idl \ + nsIThreadInternal.idl \ + nsIThreadManager.idl \ nsITimer.idl \ nsITimerInternal.idl \ nsITimerManager.idl \ nsIRunnable.idl \ - nsIEventTarget.idl \ - nsIEventQueue.idl \ - nsIEventQueueService.idl \ nsIEnvironment.idl \ nsIProcess.idl \ nsISupportsPriority.idl \ diff --git a/mozilla/xpcom/threads/TimerThread.cpp b/mozilla/xpcom/threads/TimerThread.cpp index a6d87ba2d88..b97222827a3 100644 --- a/mozilla/xpcom/threads/TimerThread.cpp +++ b/mozilla/xpcom/threads/TimerThread.cpp @@ -42,6 +42,7 @@ #include "TimerThread.h" #include "nsAutoLock.h" +#include "nsThreadManager.h" #include "pratom.h" #include "nsIObserverService.h" @@ -105,29 +106,20 @@ nsresult TimerThread::Init() if (PR_AtomicSet(&mInitInProgress, 1) == 0) { nsresult rv; + nsCOMPtr observerService + (do_GetService("@mozilla.org/observer-service;1", &rv)); - mEventQueueService = do_GetService("@mozilla.org/event-queue-service;1", &rv); if (NS_SUCCEEDED(rv)) { - nsCOMPtr observerService - (do_GetService("@mozilla.org/observer-service;1", &rv)); - - if (NS_SUCCEEDED(rv)) { - // We hold on to mThread to keep the thread alive. - rv = NS_NewThread(getter_AddRefs(mThread), - NS_STATIC_CAST(nsIRunnable*, this), - 0, - PR_JOINABLE_THREAD, - PR_PRIORITY_NORMAL, - PR_GLOBAL_THREAD); - - if (NS_FAILED(rv)) { - mThread = nsnull; - } - else { - // We'll be released at xpcom shutdown - observerService->AddObserver(this, "sleep_notification", PR_FALSE); - observerService->AddObserver(this, "wake_notification", PR_FALSE); - } + // We hold on to mThread to keep the thread alive. + rv = nsThreadManager::NewThread(NS_LITERAL_CSTRING("xpcom.timer"), this, + getter_AddRefs(mThread)); + if (NS_FAILED(rv)) { + mThread = nsnull; + } + else { + // We'll be released at xpcom shutdown + observerService->AddObserver(this, "sleep_notification", PR_FALSE); + observerService->AddObserver(this, "wake_notification", PR_FALSE); } } @@ -171,7 +163,7 @@ nsresult TimerThread::Shutdown() } } - mThread->Join(); // wait for the thread to die + mThread->Shutdown(); // wait for the thread to die return NS_OK; } diff --git a/mozilla/xpcom/threads/TimerThread.h b/mozilla/xpcom/threads/TimerThread.h index 039eea68c5c..367f50aff86 100644 --- a/mozilla/xpcom/threads/TimerThread.h +++ b/mozilla/xpcom/threads/TimerThread.h @@ -41,7 +41,6 @@ #ifndef TimerThread_h___ #define TimerThread_h___ -#include "nsIEventQueueService.h" #include "nsIObserver.h" #include "nsIRunnable.h" #include "nsIThread.h" @@ -78,9 +77,6 @@ public: void UpdateFilter(PRUint32 aDelay, PRIntervalTime aTimeout, PRIntervalTime aNow); - // For use by nsTimerImpl::Fire() - nsCOMPtr mEventQueueService; - void DoBeforeSleep(); void DoAfterSleep(); diff --git a/mozilla/xpcom/threads/nsIDispatchTarget.idl b/mozilla/xpcom/threads/nsIDispatchTarget.idl new file mode 100644 index 00000000000..dbe55b85ef3 --- /dev/null +++ b/mozilla/xpcom/threads/nsIDispatchTarget.idl @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIRunnable; + +[scriptable, uuid(4e8febe4-6631-49dc-8ac9-308c1cb9b09c)] +interface nsIDispatchTarget : nsISupports +{ + /** + * Dispatch a task to the target. This function may be called from any + * thread. If flags specifies DISPATCH_SYNC, then the dispatch method + * will not return until the task has been run. NOTE: Calling dispatch + * with DISPATCH_SYNC, may have the side-effect of running other tasks + * on the current thread while waiting for the given task to run to + * completion. This function is re-entrant. + */ + void dispatch(in nsIRunnable task, in unsigned long flags); + const unsigned long DISPATCH_NORMAL = 0; + const unsigned long DISPATCH_SYNC = 1; + + /** + * Returns true if tasks dispatched to this target will run on the + * current thread (i.e., the thread calling this method). + */ + boolean isOnCurrentThread(); +}; + +%{C++ +// convenient aliases: +#define NS_DISPATCH_NORMAL nsIDispatchTarget::DISPATCH_NORMAL +#define NS_DISPATCH_SYNC nsIDispatchTarget::DISPATCH_SYNC +%} diff --git a/mozilla/xpcom/threads/nsIThread.idl b/mozilla/xpcom/threads/nsIThread.idl index f28d6ec942b..a701c297098 100644 --- a/mozilla/xpcom/threads/nsIThread.idl +++ b/mozilla/xpcom/threads/nsIThread.idl @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -12,18 +13,18 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is mozilla.org code. + * The Original Code is Mozilla 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 of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of - * either of 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"), + * 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 @@ -35,110 +36,51 @@ * * ***** END LICENSE BLOCK ***** */ -#include "nsISupports.idl" +#include "nsIDispatchTarget.idl" -%{C++ -#include "prthread.h" - -#define NS_THREAD_CID \ -{ /* 85CE5510-7808-11d3-A181-0050041CAF44 */ \ - 0x85ce5510, \ - 0x7808, \ - 0x11d3, \ - {0xa1, 0x81, 0x00, 0x50, 0x04, 0x1c, 0xaf, 0x44} \ -} - -#define NS_THREAD_CONTRACTID "@mozilla.org/thread;1" -#define NS_THREAD_CLASSNAME "Thread" -#if 0 -%} - -typedef PRUint32 PRThreadPriority; -typedef PRUint32 PRThreadScope; -typedef PRUint32 PRThreadState; - -%{C++ -#endif -%} - -interface nsIRunnable; - -[ptr] native PRThread(PRThread); - -[scriptable, uuid(6be5e380-6886-11d3-9382-00104ba0fd40)] -interface nsIThread : nsISupports +[scriptable, uuid(9c889946-a73a-4af3-ae9a-ea64f7d4e3ca)] +interface nsIThread : nsIDispatchTarget { - // These must all match the values used in prthread.h - const PRUint32 PRIORITY_LOW = 0; - const PRUint32 PRIORITY_NORMAL = 1; - const PRUint32 PRIORITY_HIGH = 2; - const PRUint32 PRIORITY_URGENT = 3; + /** + * Returns the name of the thread. + */ + readonly attribute ACString name; - const PRUint32 SCOPE_LOCAL = 0; - const PRUint32 SCOPE_GLOBAL = 1; - const PRUint32 SCOPE_BOUND = 2; - - const PRUint32 STATE_JOINABLE = 0; - const PRUint32 STATE_UNJOINABLE = 1; - - void join(); - void interrupt(); - - attribute PRThreadPriority priority; - readonly attribute PRThreadScope scope; - readonly attribute PRThreadState state; - - [noscript] PRThread GetPRThread(); - - void init(in nsIRunnable aRunnable, - in PRUint32 aStackSize, - in PRThreadPriority aPriority, - in PRThreadScope aScope, - in PRThreadState aState); - - /* - * Get the currently running thread (really a static method sort of thing). - */ - readonly attribute nsIThread currentThread; - - /* - * Sleep to at least this many milliseconds (only works on currrent thread). - */ - void sleep(in PRUint32 msec); - -%{C++ - // returns the nsIThread for the current thread: - static NS_COM nsresult GetCurrent(nsIThread* *result); - - // returns the nsIThread for an arbitrary PRThread: - static NS_COM nsresult GetIThread(PRThread* prthread, nsIThread* *result); - - // initializes the "main" thread (really, just saves the current thread - // at time of calling. meant to be called once at app startup, in lieu - // of proper static initializers, to save the primordial thread - // for later recall.) - static NS_COM nsresult SetMainThread(); - - // return the "main" thread - static NS_COM nsresult GetMainThread(nsIThread **result); - - static NS_COM PRBool IsMainThread(); -%} + /** + * Shutdown the thread. This method may not be executed from the thread + * itself. Instead, it is meant to be executed from another thread (usually + * the thread that created this thread). When this function returns, the + * thread will be shutdown, and it will no longer be possible to dispatch + * tasks to the thread. + */ + void shutdown(); + + /** + * Run the next task assigned to this thread. This function should block + * execution of the current thread until a task is available and run. + * This function is re-entrant but may only be called if this thread is the + * current thread. + * @throws NS_BASE_STREAM_WOULD_BLOCK if RUN_NO_WAIT is specified and there + * were no tasks to run. + */ + void runNextTask(in unsigned long flags); + const unsigned long RUN_NORMAL = 0; + const unsigned long RUN_NO_WAIT = 1; }; %{C++ -extern NS_COM nsresult -NS_NewThread(nsIThread* *result, - nsIRunnable* runnable, - PRUint32 stackSize = 0, - PRThreadState state = PR_UNJOINABLE_THREAD, - PRThreadPriority priority = PR_PRIORITY_NORMAL, - PRThreadScope scope = PR_GLOBAL_THREAD); +// Run all pending tasks for a given thread before returning. +static inline nsresult +NS_RunPendingTasks(nsIThread *thread) +{ + nsresult rv; + do { + rv = thread->RunNextTask(nsIThread::RUN_NO_WAIT); + } while (NS_SUCCEEDED(rv)); -extern NS_COM nsresult -NS_NewThread(nsIThread* *result, - PRUint32 stackSize = 0, - PRThreadState state = PR_UNJOINABLE_THREAD, - PRThreadPriority priority = PR_PRIORITY_NORMAL, - PRThreadScope scope = PR_GLOBAL_THREAD); + if (rv == NS_BASE_STREAM_WOULD_BLOCK) + rv = NS_OK; + + return rv; +} %} diff --git a/mozilla/xpcom/threads/nsIThreadInternal.idl b/mozilla/xpcom/threads/nsIThreadInternal.idl new file mode 100644 index 00000000000..ce01ae795d3 --- /dev/null +++ b/mozilla/xpcom/threads/nsIThreadInternal.idl @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIThread.idl" + +interface nsIThreadObserver; + +/** + * The standard XPCOM thread object implements this interface, which allows a + * consumer to observe dispatch activity on the thread. + */ +[scriptable, uuid(f89b5063-b06d-42f8-bf23-4dfcf2d80d6a)] +interface nsIThreadInternal : nsIThread +{ + /** + * The consumer's thread observer. + */ + attribute nsIThreadObserver observer; + + /** + * This method returns true if there is a pending task for this thread. + */ + boolean hasPendingTask(); +}; + +/** + * This interface provides the observer with hooks to implement a layered + * event queue. For example, it is possible to overlay processing events + * for a GUI toolkit on top of the events for a thread. + * + * NativeQueue; + * + * Observer = { + * onNewTask(thread, flags) { + * NativeQueue.signal(); + * } + * onBeforeRunNextTask(thread, flags) { + * NativeQueue.processPendingEvents(); + * } + * onAfterRunNextTask(thread, flags, status) { + * NativeQueue.processPendingEvents(); + * } + * onWaitNextTask(thread, flags) { + * while (!thread.hasPendingTask()) { + * NativeQueue.wait(); + * NativeQueue.processPendingEvents(); + * } + * } + * }; + * + * NOTE: The implementation of this interface must be threadsafe. + */ +[scriptable, uuid(719d2fb9-54ce-4847-b15b-fa9960e22f9f)] +interface nsIThreadObserver : nsISupports +{ + /** + * This method is called whenever a new task is added to the thread. This + * method may be called from any thread. + */ + void onNewTask(in nsIThreadInternal thread, + in unsigned long dispatchFlags); + + /** + * This method is called (from nsIThread.runNextTask) before a task is + * processed. This method is only called on the target thread. + */ + void onBeforeRunNextTask(in nsIThreadInternal thread, + in unsigned long runFlags); + + /** + * This method is called (from nsIThread.runNextTask) after a task is + * processed. This method is only called on the target thread. + */ + void onAfterRunNextTask(in nsIThreadInternal thread, + in unsigned long runFlags, + in nsresult status); + + /** + * This method is called (from nsIThread.runNextTask) when it may wait for a + * pending task to become available. It's possible that a pending task may + * already be available when this method is called. This method is only + * called on the target thread. + */ + void onWaitNextTask(in nsIThreadInternal thread, + in unsigned long runFlags); +}; diff --git a/mozilla/xpcom/threads/nsIThreadManager.idl b/mozilla/xpcom/threads/nsIThreadManager.idl new file mode 100644 index 00000000000..082c5600d45 --- /dev/null +++ b/mozilla/xpcom/threads/nsIThreadManager.idl @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIThread; + +[scriptable, uuid(056216f5-8803-46b4-9199-d95bc1f0446f)] +interface nsIThreadManager : nsISupports +{ + /** + * Create a new named thread (a global, user PRThread). The name + * must be unique. + */ + nsIThread newThread(in ACString name); + + /** + * Find a named thread. + */ + nsIThread getThread(in ACString name); + + /** + * Get the main thread. + */ + nsIThread getMainThread(); + + /** + * Get the current thread. + */ + nsIThread getCurrentThread(); + + /** + * Set an external nsIThread instance (or null) as the nsIThread for the + * current thread. If a nsIThread is already associated with the calling + * thread, then this function will replace it with the given nsIThread. If + * the given nsIThread is non-null, then its name attribute must be unique. + * Its name may be equal to the name of the nsIThread being replaced. This + * method returns the nsIThread that was replaced by this method call or null + * if there was no previous nsIThread associated with the current thread. + */ + nsIThread setCurrentThread(in nsIThread thread); + + /** + * This method returns true if the calling thread is the main thread of the + * application process. + */ + boolean isMainThread(); +}; diff --git a/mozilla/xpcom/threads/nsRunnable.cpp b/mozilla/xpcom/threads/nsRunnable.cpp new file mode 100644 index 00000000000..a61b15e667e --- /dev/null +++ b/mozilla/xpcom/threads/nsRunnable.cpp @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsRunnable.h" + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsRunnable, nsIRunnable) + +NS_IMETHODIMP +nsRunnable::Run() +{ + // Do nothing + return NS_OK; +} diff --git a/mozilla/xpcom/threads/nsRunnable.h b/mozilla/xpcom/threads/nsRunnable.h new file mode 100644 index 00000000000..149e0e8c685 --- /dev/null +++ b/mozilla/xpcom/threads/nsRunnable.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsRunnable_h__ +#define nsRunnable_h__ + +#include "nsIRunnable.h" + +// This class is designed to be subclassed. + +class nsRunnable : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + nsRunnable() { + } + +protected: + virtual ~nsRunnable() { + } +}; + +#endif // nsRunnable_h__ diff --git a/mozilla/xpcom/threads/nsTaskQueue.cpp b/mozilla/xpcom/threads/nsTaskQueue.cpp new file mode 100644 index 00000000000..bfc582f705f --- /dev/null +++ b/mozilla/xpcom/threads/nsTaskQueue.cpp @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsTaskQueue.h" +#include "nsAutoLock.h" +#include "nsCOMPtr.h" + +nsTaskQueue::nsTaskQueue() + : mMonitor(nsAutoMonitor::NewMonitor("TaskQueue")) + , mHead(nsnull) + , mTail(nsnull) + , mOffsetHead(0) + , mOffsetTail(0) +{ +} + +nsTaskQueue::~nsTaskQueue() +{ + nsAutoMonitor::DestroyMonitor(mMonitor); +} + +PRBool +nsTaskQueue::GetPendingTask(PRBool wait, nsIRunnable **result) +{ + nsAutoMonitor mon(mMonitor); + + while (IsEmpty()) { + if (!wait) { + if (result) + *result = nsnull; + return PR_FALSE; + } + mon.Wait(); + } + + if (result) + *result = mHead->mTasks[mOffsetHead++]; + + // Check if mHead points to empty Page + if (mOffsetHead == TASKS_PER_PAGE) { + Page *dead = mHead; + mHead = mHead->mNext; + delete dead; + mOffsetHead = 0; + } + + return PR_TRUE; +} + +PRBool +nsTaskQueue::PutTask(nsIRunnable *runnable) +{ + // Avoid calling AddRef+Release while holding our monitor. + nsCOMPtr task(runnable); + PRBool rv = PR_TRUE; + { + nsAutoMonitor mon(mMonitor); + + if (!mHead) { + mHead = new Page(); + if (!mHead) { + rv = PR_FALSE; + } else { + mTail = mHead; + mOffsetHead = 0; + mOffsetTail = 0; + } + } else if (mOffsetTail == TASKS_PER_PAGE) { + Page *page = new Page(); + if (!page) { + rv = PR_FALSE; + } else { + mTail->mNext = page; + mTail = page; + mOffsetTail = 0; + } + } + if (rv) + task.swap(mTail->mTasks[mOffsetTail++]); + } + return rv; +} diff --git a/mozilla/xpcom/threads/nsTaskQueue.h b/mozilla/xpcom/threads/nsTaskQueue.h new file mode 100644 index 00000000000..1740830a843 --- /dev/null +++ b/mozilla/xpcom/threads/nsTaskQueue.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsTaskQueue_h__ +#define nsTaskQueue_h__ + +#include "prmon.h" +#include "nsIRunnable.h" + +// A threadsafe FIFO task queue... +class nsTaskQueue +{ +public: + nsTaskQueue(); + ~nsTaskQueue(); + + // This method adds a new task on the pending task queue. + PRBool PutTask(nsIRunnable *runnable); + + // This method returns true if there is a pending task. + PRBool HasPendingTask() { + return GetPendingTask(PR_FALSE, nsnull); + } + + // This method returns the next pending task or null. + PRBool GetPendingTask(nsIRunnable **runnable) { + return GetPendingTask(PR_FALSE, runnable); + } + + // This method waits for and returns the next pending task. + PRBool WaitPendingTask(nsIRunnable **runnable) { + return GetPendingTask(PR_TRUE, runnable); + } + +private: + + PRBool GetPendingTask(PRBool wait, nsIRunnable **runnable); + + PRBool IsEmpty() { + return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail); + } + + enum { TASKS_PER_PAGE = 15 }; + + struct Page { + struct Page *mNext; + nsIRunnable *mTasks[TASKS_PER_PAGE]; + + Page() : mNext(nsnull) {} + }; + + PRMonitor *mMonitor; + + Page *mHead; + Page *mTail; + + PRUint16 mOffsetHead; // offset into mHead where next item is removed + PRUint16 mOffsetTail; // offset into mTail where next item is added +}; + +#endif // nsTaskQueue_h__ diff --git a/mozilla/xpcom/threads/nsThread.cpp b/mozilla/xpcom/threads/nsThread.cpp index 46c923d9298..4658c661c18 100644 --- a/mozilla/xpcom/threads/nsThread.cpp +++ b/mozilla/xpcom/threads/nsThread.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -12,18 +13,18 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is mozilla.org code. + * The Original Code is Mozilla 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 of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of - * either of 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"), + * 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 @@ -36,450 +37,428 @@ * ***** END LICENSE BLOCK ***** */ #include "nsThread.h" -#include "prmem.h" -#include "prlog.h" +#include "nsThreadManager.h" +#include "nsRunnable.h" #include "nsAutoLock.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" -PRUintn nsThread::kIThreadSelfIndex = 0; -static nsIThread *gMainThread = 0; +NS_DECL_CI_INTERFACE_GETTER(nsThread) -#if defined(PR_LOGGING) -// -// Log module for nsIThread logging... -// -// To enable logging (see prlog.h for full details): -// -// set NSPR_LOG_MODULES=nsIThread:5 -// set NSPR_LOG_FILE=nspr.log -// -// this enables PR_LOG_DEBUG level information and places all output in -// the file nspr.log -// -// gSocketLog is defined in nsSocketTransport.cpp -// -PRLogModuleInfo* nsIThreadLog = nsnull; +//----------------------------------------------------------------------------- +// Because we do not have our own nsIFactory, we have to implement nsIClassInfo +// somewhat manually. -#endif /* PR_LOGGING */ +class nsThreadClassInfo : public nsIClassInfo { +public: + NS_DECL_ISUPPORTS_INHERITED // no mRefCnt + NS_DECL_NSICLASSINFO -//////////////////////////////////////////////////////////////////////////////// + nsThreadClassInfo() {} +}; -nsThread::nsThread() - : mThread(nsnull), mDead(PR_FALSE), mStartLock(nsnull) +static nsThreadClassInfo sThreadClassInfo; + +NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::AddRef() { return 2; } +NS_IMETHODIMP_(nsrefcnt) nsThreadClassInfo::Release() { return 1; } +NS_IMPL_QUERY_INTERFACE1(nsThreadClassInfo, nsIClassInfo) + +NS_IMETHODIMP +nsThreadClassInfo::GetInterfaces(PRUint32 *count, nsIID ***array) { -#if defined(PR_LOGGING) - // - // Initialize the global PRLogModule for nsIThread logging - // if necessary... - // - if (nsIThreadLog == nsnull) { - nsIThreadLog = PR_NewLogModule("nsIThread"); - } -#endif /* PR_LOGGING */ + return NS_CI_INTERFACE_GETTER_NAME(nsThread)(count, array); +} - // enforce matching of constants to enums in prthread.h - NS_ASSERTION(int(nsIThread::PRIORITY_LOW) == int(PR_PRIORITY_LOW) && - int(nsIThread::PRIORITY_NORMAL) == int(PRIORITY_NORMAL) && - int(nsIThread::PRIORITY_HIGH) == int(PRIORITY_HIGH) && - int(nsIThread::PRIORITY_URGENT) == int(PRIORITY_URGENT) && - int(nsIThread::SCOPE_LOCAL) == int(PR_LOCAL_THREAD) && - int(nsIThread::SCOPE_GLOBAL) == int(PR_GLOBAL_THREAD) && - int(nsIThread::STATE_JOINABLE) == int(PR_JOINABLE_THREAD) && - int(nsIThread::STATE_UNJOINABLE) == int(PR_UNJOINABLE_THREAD), - "Bad constant in nsIThread!"); +NS_IMETHODIMP +nsThreadClassInfo::GetHelperForLanguage(PRUint32 lang, nsISupports **result) +{ + *result = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadClassInfo::GetContractID(char **result) +{ + *result = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadClassInfo::GetClassDescription(char **result) +{ + *result = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadClassInfo::GetClassID(nsCID **result) +{ + *result = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadClassInfo::GetImplementationLanguage(PRUint32 *result) +{ + *result = nsIProgrammingLanguage::CPLUSPLUS; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadClassInfo::GetFlags(PRUint32 *result) +{ + *result = THREADSAFE; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadClassInfo::GetClassIDNoAlloc(nsCID *result) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +//----------------------------------------------------------------------------- + +NS_IMPL_THREADSAFE_ADDREF(nsThread) +NS_IMPL_THREADSAFE_RELEASE(nsThread) +NS_INTERFACE_MAP_BEGIN(nsThread) + NS_INTERFACE_MAP_ENTRY(nsIThread) + NS_INTERFACE_MAP_ENTRY(nsIThreadInternal) + NS_INTERFACE_MAP_ENTRY(nsIDispatchTarget) + NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread) + if (aIID.Equals(NS_GET_IID(nsIClassInfo))) { + foundInterface = NS_STATIC_CAST(nsIClassInfo*, &sThreadClassInfo); + } else +NS_INTERFACE_MAP_END +NS_IMPL_CI_INTERFACE_GETTER4(nsThread, nsIThread, nsIThreadInternal, + nsIDispatchTarget, nsISupportsPriority) + +//----------------------------------------------------------------------------- + +class nsThreadShutdownTask : public nsRunnable { +public: + nsThreadShutdownTask(nsThread *thr) + : mThread(thr) { + } + + NS_IMETHODIMP Run() { + mThread->mActive = PR_FALSE; + return NS_OK; + } + +private: + nsRefPtr mThread; +}; + +//----------------------------------------------------------------------------- + +class nsThreadSyncDispatch : public nsRunnable { +public: + nsThreadSyncDispatch(nsIRunnable *task) + : mSyncTask(task) { + } + + NS_IMETHODIMP Run() { + mSyncTask->Run(); + mSyncTask = nsnull; + return NS_OK; + } + + PRBool IsPending() { + return mSyncTask != nsnull; + } + +private: + nsCOMPtr mSyncTask; +}; + +//----------------------------------------------------------------------------- + +// This class is used to convey initialization info to the newly created thread. +class nsThreadStartInfo { +private: + PRInt32 mRefCnt; + + ~nsThreadStartInfo() { + nsAutoMonitor::DestroyMonitor(mMon); + } +public: + PRMonitor *mMon; + nsThread *mThread; + PRBool mInitialized; + + nsThreadStartInfo(nsThread *thr) + : mRefCnt(0) + , mMon(nsAutoMonitor::NewMonitor("nsThreadStartInfo")) + , mThread(thr) + , mInitialized(PR_FALSE) { + } + + void AddRef() { + PR_AtomicIncrement(&mRefCnt); + } + + void Release() { + if (PR_AtomicDecrement(&mRefCnt) == 0) + delete this; + } +}; + +//----------------------------------------------------------------------------- + +/*static*/ void +nsThread::ThreadFunc(void *arg) +{ + nsThreadStartInfo *si = NS_STATIC_CAST(nsThreadStartInfo *, arg); + + nsThread *self = si->mThread; + NS_ADDREF(self); + + // Inform the ThreadManager + nsThreadManager::get()->SetCurrentThread(self, nsnull); + + // Unblock nsThread::Init + { + nsAutoMonitor mon(si->mMon); + si->mInitialized = PR_TRUE; + mon.Notify(); + } + NS_RELEASE(si); + + // Now, process incoming tasks... + while (self->mActive) + self->RunNextTask(nsIThread::RUN_NORMAL); + + NS_RELEASE(self); + + // Inform the threadmanager that this thread is going away + nsThreadManager::get()->SetCurrentThread(nsnull, nsnull); +} + +//----------------------------------------------------------------------------- + +nsThread::nsThread(const nsACString &name) + : mObserverLock(PR_NewLock()) + , mName(name) +{ } nsThread::~nsThread() { - if (mStartLock) - PR_DestroyLock(mStartLock); - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p destroyed\n", this)); - - // This code used to free the nsIThreadLog loginfo stuff - // Don't do that; loginfo structures are owned by nspr - // and would be freed if we ever called PR_Cleanup() - // see bug 142072 + PR_DestroyLock(mObserverLock); } -void -nsThread::Main(void* arg) -{ - nsThread* self = (nsThread*)arg; - - self->WaitUntilReadyToStartMain(); - - nsresult rv = NS_OK; - rv = self->RegisterThreadSelf(); - NS_ASSERTION(rv == NS_OK, "failed to set thread self"); - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p start run %p\n", self, self->mRunnable.get())); - rv = self->mRunnable->Run(); - NS_ASSERTION(NS_SUCCEEDED(rv), "runnable failed"); - -#ifdef DEBUG - // Because a thread can die after gMainThread dies and takes nsIThreadLog with it, - // we need to check for it being null so that we don't crash on shutdown. - if (nsIThreadLog) { - PRThreadState state; - rv = self->GetState(&state); - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p end run %p\n", self, self->mRunnable.get())); - } -#endif - - // explicitly drop the runnable now in case there are circular references - // between it and the thread object - self->mRunnable = nsnull; -} - -void -nsThread::Exit(void* arg) -{ - nsThread* self = (nsThread*)arg; - - if (self->mDead) { - NS_ERROR("attempt to Exit() thread twice"); - return; - } - - self->mDead = PR_TRUE; - -#if defined(PR_LOGGING) - if (nsIThreadLog) { - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p exited\n", self)); - } -#endif - NS_RELEASE(self); -} - -NS_METHOD -nsThread::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) -{ - nsThread* thread = new nsThread(); - if (!thread) return NS_ERROR_OUT_OF_MEMORY; - nsresult rv = thread->QueryInterface(aIID, aResult); - if (NS_FAILED(rv)) delete thread; - return rv; -} - -NS_IMPL_THREADSAFE_ISUPPORTS1(nsThread, nsIThread) - -NS_IMETHODIMP -nsThread::Join() -{ - // don't check for mDead here because nspr calls Exit (cleaning up - // thread-local storage) before they let us join with the thread - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p start join\n", this)); - if (!mThread) - return NS_ERROR_NOT_INITIALIZED; - PRStatus status = PR_JoinThread(mThread); - // XXX can't use NS_RELEASE here because the macro wants to set - // this to null (bad c++) - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p end join\n", this)); - if (status != PR_SUCCESS) - return NS_ERROR_FAILURE; - - NS_RELEASE_THIS(); // most likely the final release of this thread - return NS_OK; -} - -NS_IMETHODIMP -nsThread::GetPriority(PRThreadPriority *result) -{ - if (mDead) - return NS_ERROR_FAILURE; - if (!mThread) - return NS_ERROR_NOT_INITIALIZED; - *result = PR_GetThreadPriority(mThread); - return NS_OK; -} - -NS_IMETHODIMP -nsThread::SetPriority(PRThreadPriority value) -{ - if (mDead) - return NS_ERROR_FAILURE; - if (!mThread) - return NS_ERROR_NOT_INITIALIZED; - PR_SetThreadPriority(mThread, value); - return NS_OK; -} - -NS_IMETHODIMP -nsThread::Interrupt() -{ - if (mDead) - return NS_ERROR_FAILURE; - if (!mThread) - return NS_ERROR_NOT_INITIALIZED; - PRStatus status = PR_Interrupt(mThread); - return status == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -nsThread::GetScope(PRThreadScope *result) -{ - if (mDead) - return NS_ERROR_FAILURE; - if (!mThread) - return NS_ERROR_NOT_INITIALIZED; - *result = PR_GetThreadScope(mThread); - return NS_OK; -} - -NS_IMETHODIMP -nsThread::GetState(PRThreadState *result) -{ - if (mDead) - return NS_ERROR_FAILURE; - if (!mThread) - return NS_ERROR_NOT_INITIALIZED; - *result = PR_GetThreadState(mThread); - return NS_OK; -} - -NS_IMETHODIMP -nsThread::GetPRThread(PRThread* *result) -{ - if (mDead) { - *result = nsnull; - return NS_ERROR_FAILURE; - } - *result = mThread; - return NS_OK; -} - -NS_IMETHODIMP -nsThread::Init(nsIRunnable* runnable, - PRUint32 stackSize, - PRThreadPriority priority, - PRThreadScope scope, - PRThreadState state) -{ - NS_ENSURE_ARG_POINTER(runnable); - if (mRunnable) - return NS_ERROR_ALREADY_INITIALIZED; - - mRunnable = runnable; - - if (mStartLock) - return NS_ERROR_ALREADY_INITIALIZED; - - mStartLock = PR_NewLock(); - if (mStartLock == nsnull) { - mRunnable = nsnull; - return NS_ERROR_OUT_OF_MEMORY; - } - - NS_ADDREF_THIS(); // released in nsThread::Exit - if (state == PR_JOINABLE_THREAD) - NS_ADDREF_THIS(); // released in nsThread::Join - - PR_Lock(mStartLock); - mDead = PR_FALSE; - mThread = PR_CreateThread(PR_USER_THREAD, Main, this, - priority, scope, state, stackSize); - /* As soon as we PR_Unlock(mStartLock), if mThread was successfully - * created, it could run and exit very quickly. In which case, it - * would null mThread and therefore if we check if (mThread) we could - * confuse a successfully created, yet already exited thread with - * OOM - failure to create the thread. So instead we store a local thr - * which we check to see if we really failed to create the thread. - */ - PRThread *thr = mThread; - PR_Unlock(mStartLock); - - if (thr == nsnull) { - mDead = PR_TRUE; // otherwise cleared in nsThread::Exit - mRunnable = nsnull; // otherwise cleared in nsThread::Main(when done) - PR_DestroyLock(mStartLock); - mStartLock = nsnull; - NS_RELEASE_THIS(); // otherwise released in nsThread::Exit - if (state == PR_JOINABLE_THREAD) - NS_RELEASE_THIS(); // otherwise released in nsThread::Join - return NS_ERROR_OUT_OF_MEMORY; - } - - PR_LOG(nsIThreadLog, PR_LOG_DEBUG, - ("nsIThread %p created\n", this)); - - return NS_OK; -} - -/* readonly attribute nsIThread currentThread; */ -NS_IMETHODIMP -nsThread::GetCurrentThread(nsIThread * *aCurrentThread) -{ - return GetIThread(PR_GetCurrentThread(), aCurrentThread); -} - -/* void sleep (in PRUint32 msec); */ -NS_IMETHODIMP -nsThread::Sleep(PRUint32 msec) -{ - if (PR_GetCurrentThread() != mThread) - return NS_ERROR_FAILURE; - - if (PR_Sleep(PR_MillisecondsToInterval(msec)) != PR_SUCCESS) - return NS_ERROR_FAILURE; - - return NS_OK; -} - -NS_COM nsresult -NS_NewThread(nsIThread* *result, - nsIRunnable* runnable, - PRUint32 stackSize, - PRThreadState state, - PRThreadPriority priority, - PRThreadScope scope) -{ - nsresult rv; - nsThread* thread = new nsThread(); - if (thread == nsnull) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(thread); - - rv = thread->Init(runnable, stackSize, priority, scope, state); - if (NS_FAILED(rv)) { - NS_RELEASE(thread); - return rv; - } - - *result = thread; - return NS_OK; -} - -NS_COM nsresult -NS_NewThread(nsIThread* *result, - PRUint32 stackSize, - PRThreadState state, - PRThreadPriority priority, - PRThreadScope scope) -{ - nsThread* thread = new nsThread(); - if (thread == nsnull) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(thread); - *result = thread; - return NS_OK; -} - -//////////////////////////////////////////////////////////////////////////////// - nsresult -nsThread::RegisterThreadSelf() +nsThread::Init() { - PRStatus status; + // spawn thread and wait until it is fully setup + nsThreadStartInfo *si = new nsThreadStartInfo(this); + if (!si) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(si); - if (kIThreadSelfIndex == 0) { - status = PR_NewThreadPrivateIndex(&kIThreadSelfIndex, Exit); - if (status != PR_SUCCESS) return NS_ERROR_FAILURE; - } + mActive = PR_TRUE; + mThread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, si, PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); + if (!mThread) { + NS_RELEASE(si); + return NS_ERROR_OUT_OF_MEMORY; + } - status = PR_SetThreadPrivate(kIThreadSelfIndex, this); - if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + // Wait for thread to call ThreadManager::SetCurrentThread + { + nsAutoMonitor mon(si->mMon); + while (!si->mInitialized) + mon.Wait(); + } - return NS_OK; + NS_RELEASE(si); + return NS_OK; } -void -nsThread::WaitUntilReadyToStartMain() +nsresult +nsThread::InitCurrentThread() { - PR_Lock(mStartLock); - PR_Unlock(mStartLock); - PR_DestroyLock(mStartLock); - mStartLock = nsnull; + mActive = PR_TRUE; + mThread = PR_GetCurrentThread(); + + nsThreadManager::get()->SetCurrentThread(this, nsnull); + return NS_OK; } -NS_COM nsresult -nsIThread::GetCurrent(nsIThread* *result) +PRBool +nsThread::PutTask(nsIRunnable *task, PRUint32 dispatchFlags) { - return GetIThread(PR_GetCurrentThread(), result); + PRBool rv = mTasks.PutTask(task); + if (!rv) + return PR_FALSE; + + nsCOMPtr obs; + { + nsAutoLock lock(mObserverLock); + obs = mObserver; + } + if (obs) + obs->OnNewTask(this, dispatchFlags); + + return PR_TRUE; } -NS_COM nsresult -nsIThread::GetIThread(PRThread* prthread, nsIThread* *result) +NS_IMETHODIMP +nsThread::Dispatch(nsIRunnable *runnable, PRUint32 flags) { - PRStatus status; - nsThread* thread; + NS_ENSURE_STATE(mThread); - if (nsThread::kIThreadSelfIndex == 0) { - status = PR_NewThreadPrivateIndex(&nsThread::kIThreadSelfIndex, nsThread::Exit); - if (status != PR_SUCCESS) return NS_ERROR_FAILURE; - } + if (flags == DISPATCH_NORMAL) { + PutTask(runnable, flags); + } else if (flags & DISPATCH_SYNC) { + nsCOMPtr thread; + nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread)); + NS_ENSURE_STATE(thread); - thread = (nsThread*)PR_GetThreadPrivate(nsThread::kIThreadSelfIndex); - if (thread == nsnull) { - // if the current thread doesn't have an nsIThread associated - // with it, make one - thread = new nsThread(); - if (thread == nsnull) - return NS_ERROR_OUT_OF_MEMORY; - NS_ADDREF(thread); // released by Exit - thread->SetPRThread(prthread); - nsresult rv = thread->RegisterThreadSelf(); - if (NS_FAILED(rv)) return rv; - } - NS_ADDREF(thread); - *result = thread; - return NS_OK; + nsRefPtr task = new nsThreadSyncDispatch(runnable); + PutTask(task, flags); + + while (task->IsPending()) + thread->RunNextTask(nsIThread::RUN_NORMAL); + } + return NS_OK; } -NS_COM nsresult -nsIThread::SetMainThread() +NS_IMETHODIMP +nsThread::IsOnCurrentThread(PRBool *result) { - // strictly speaking, it could be set twice. but practically speaking, - // it's almost certainly an error if it is - if (gMainThread != 0) { - NS_ERROR("Setting main thread twice?"); - return NS_ERROR_FAILURE; - } - return GetCurrent(&gMainThread); + *result = (PR_GetCurrentThread() == mThread); + return NS_OK; } -NS_COM nsresult -nsIThread::GetMainThread(nsIThread **result) +NS_IMETHODIMP +nsThread::GetName(nsACString &result) { - NS_ASSERTION(result, "bad result pointer"); - if (gMainThread == 0) - return NS_ERROR_FAILURE; - *result = gMainThread; - NS_ADDREF(gMainThread); - return NS_OK; + result = mName; // no need to lock since this never changes + return NS_OK; } -NS_COM PRBool -nsIThread::IsMainThread() -{ - if (gMainThread == 0) - return PR_TRUE; - - PRThread *theMainThread; - gMainThread->GetPRThread(&theMainThread); - return theMainThread == PR_GetCurrentThread(); -} - -void +NS_IMETHODIMP nsThread::Shutdown() { - if (gMainThread) { - // XXX nspr doesn't seem to be calling the main thread's destructor - // callback, so let's help it out: - nsThread::Exit(NS_STATIC_CAST(nsThread*, gMainThread)); - nsrefcnt cnt; - NS_RELEASE2(gMainThread, cnt); - NS_WARN_IF_FALSE(cnt == 0, "Main thread being held past XPCOM shutdown."); - gMainThread = nsnull; - - kIThreadSelfIndex = 0; - } + NS_ENSURE_STATE(mThread); + NS_ENSURE_STATE(PR_GetCurrentThread() != mThread); + + // shutdown task queue + nsCOMPtr task = new nsThreadShutdownTask(this); + if (!task) + return NS_ERROR_OUT_OF_MEMORY; + PutTask(task, NS_DISPATCH_NORMAL); + + // XXX we could still end up with other tasks being added after the shutdown task + + PR_JoinThread(mThread); + mThread = nsnull; + return NS_OK; } -//////////////////////////////////////////////////////////////////////////////// +NS_IMETHODIMP +nsThread::RunNextTask(PRUint32 flags) +{ + NS_ENSURE_STATE(PR_GetCurrentThread() == mThread); + NS_ENSURE_STATE(mActive); + NS_ENSURE_ARG(flags == RUN_NORMAL || flags == RUN_NO_WAIT); + + nsCOMPtr obs; + { + nsAutoLock lock(mObserverLock); + obs = mObserver; + } + + if (obs) + obs->OnBeforeRunNextTask(this, flags); + + nsCOMPtr task; + if (flags == RUN_NORMAL) { + if (obs) + obs->OnWaitNextTask(this, flags); + mTasks.WaitPendingTask(getter_AddRefs(task)); + } else { + mTasks.GetPendingTask(getter_AddRefs(task)); + } + + nsresult rv = NS_OK; + + if (task) { + task->Run(); + } else if (flags & RUN_NO_WAIT) { + rv = NS_BASE_STREAM_WOULD_BLOCK; + } + + if (obs) + obs->OnAfterRunNextTask(this, flags, rv); + + return rv; +} + +NS_IMETHODIMP +nsThread::GetPriority(PRInt32 *priority) +{ + *priority = mPriority; + return NS_OK; +} + +NS_IMETHODIMP +nsThread::SetPriority(PRInt32 priority) +{ + NS_ENSURE_STATE(mThread); + + // NSPR defines the following four thread priorities: + // PR_PRIORITY_LOW + // PR_PRIORITY_NORMAL + // PR_PRIORITY_HIGH + // PR_PRIORITY_URGENT + // We map the priority values defined on nsISupportsPriority to these values. + + mPriority = priority; + + PRThreadPriority pri; + if (mPriority <= PRIORITY_HIGHEST) { + pri = PR_PRIORITY_URGENT; + } else if (mPriority < PRIORITY_NORMAL) { + pri = PR_PRIORITY_HIGH; + } else if (mPriority > PRIORITY_NORMAL) { + pri = PR_PRIORITY_LOW; + } else { + pri = PR_PRIORITY_NORMAL; + } + PR_SetThreadPriority(mThread, pri); + + return NS_OK; +} + +NS_IMETHODIMP +nsThread::AdjustPriority(PRInt32 delta) +{ + return SetPriority(mPriority + delta); +} + +NS_IMETHODIMP +nsThread::GetObserver(nsIThreadObserver **obs) +{ + nsAutoLock lock(mObserverLock); + NS_ADDREF(*obs = mObserver); + return NS_OK; +} + +NS_IMETHODIMP +nsThread::SetObserver(nsIThreadObserver *obs) +{ + nsAutoLock lock(mObserverLock); + mObserver = obs; + return NS_OK; +} + +NS_IMETHODIMP +nsThread::HasPendingTask(PRBool *result) +{ + *result = mTasks.HasPendingTask(); + return NS_OK; +} diff --git a/mozilla/xpcom/threads/nsThread.h b/mozilla/xpcom/threads/nsThread.h index e75086a71d8..d005efcd82d 100644 --- a/mozilla/xpcom/threads/nsThread.h +++ b/mozilla/xpcom/threads/nsThread.h @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -12,18 +13,18 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is mozilla.org code. + * The Original Code is Mozilla 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 of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Darin Fisher * * Alternatively, the contents of this file may be used under the terms of - * either of 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"), + * 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 @@ -33,60 +34,53 @@ * 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 ***** - * This Original Code has been modified by IBM Corporation. - * Modifications made by IBM described herein are - * Copyright (c) International Business Machines - * Corporation, 2000 - * - * Modifications to Mozilla code or documentation - * identified per MPL Section 3.3 - * - * Date Modified by Description of modification - * 04/20/2000 IBM Corp. Added PR_CALLBACK for Optlink use in OS2 - */ + * ***** END LICENSE BLOCK ***** */ #ifndef nsThread_h__ #define nsThread_h__ -#include "nsIRunnable.h" -#include "nsIThread.h" +#include "nsIThreadInternal.h" +#include "nsISupportsPriority.h" +#include "nsTaskQueue.h" +#include "nsString.h" #include "nsCOMPtr.h" -class nsThread : public nsIThread +// A native thread +class nsThread : public nsIThreadInternal, public nsISupportsPriority { public: - NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS + NS_DECL_NSIDISPATCHTARGET + NS_DECL_NSITHREAD + NS_DECL_NSITHREADINTERNAL + NS_DECL_NSISUPPORTSPRIORITY - // nsIThread methods: - NS_DECL_NSITHREAD - - // nsThread methods: - nsThread(); + nsThread(const nsACString &name); - nsresult RegisterThreadSelf(); - void SetPRThread(PRThread* thread) { mThread = thread; } - void WaitUntilReadyToStartMain(); + // Initialize this as a wrapper for a new PRThread. + nsresult Init(); - static void PR_CALLBACK Main(void* arg); - static void PR_CALLBACK Exit(void* arg); - static void PR_CALLBACK Shutdown(); - - static PRUintn kIThreadSelfIndex; - - static NS_METHOD - Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); + // Initialize this as a wrapper for the current PRThread. + nsresult InitCurrentThread(); private: - ~nsThread(); + friend class nsThreadShutdownTask; -protected: - PRThread* mThread; - nsCOMPtr mRunnable; - PRBool mDead; - PRLock* mStartLock; + ~nsThread(); + + PR_STATIC_CALLBACK(void) ThreadFunc(void *arg); + + // Wrapper for nsTaskQueue::PutTask + PRBool PutTask(nsIRunnable *task, PRUint32 dispatchFlags); + + PRLock *mObserverLock; + nsCOMPtr mObserver; + + nsTaskQueue mTasks; + nsCString mName; + PRInt32 mPriority; + PRThread *mThread; + PRIntn mActive; }; -//////////////////////////////////////////////////////////////////////////////// - -#endif // nsThread_h__ +#endif // nsThread_h__ diff --git a/mozilla/xpcom/threads/nsThreadManager.cpp b/mozilla/xpcom/threads/nsThreadManager.cpp new file mode 100644 index 00000000000..01a4caf50cb --- /dev/null +++ b/mozilla/xpcom/threads/nsThreadManager.cpp @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsThreadManager.h" +#include "nsThread.h" +#include "nsCOMArray.h" +#include "nsAutoPtr.h" + +//----------------------------------------------------------------------------- + +PR_STATIC_CALLBACK(void) +ReleaseObject(void *data) +{ + NS_STATIC_CAST(nsISupports *, data)->Release(); +} + +PR_STATIC_CALLBACK(PLDHashOperator) +AppendAndRemoveThread(const nsACString &key, nsCOMPtr &thread, + void *arg) +{ + nsCOMArray *threads = + NS_STATIC_CAST(nsCOMArray *, arg); + threads->AppendObject(thread); + return PL_DHASH_REMOVE; +} + +//----------------------------------------------------------------------------- + +nsThreadManager nsThreadManager::sInstance; + +// statically allocated instance +NS_IMETHODIMP_(nsrefcnt) nsThreadManager::AddRef() { return 2; } +NS_IMETHODIMP_(nsrefcnt) nsThreadManager::Release() { return 1; } +NS_IMPL_QUERY_INTERFACE1_CI(nsThreadManager, nsIThreadManager) +NS_IMPL_CI_INTERFACE_GETTER1(nsThreadManager, nsIThreadManager) + +//----------------------------------------------------------------------------- + +nsresult +nsThreadManager::Init() +{ + if (!mThreads.Init()) + return NS_ERROR_OUT_OF_MEMORY; + + if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE) + return NS_ERROR_FAILURE; + + // Setup "main" thread + nsRefPtr mainThread = new nsThread(NS_LITERAL_CSTRING("main")); + if (!mainThread) + return NS_ERROR_OUT_OF_MEMORY; + mainThread->InitCurrentThread(); + + return NS_OK; +} + +void +nsThreadManager::Shutdown() +{ + NS_ASSERTION(IsMainThread(), "shutdown not called from main thread"); + + // We move the threads from the hashtable to a list, so that we avoid + // holding the hashtable lock while calling nsIThread::Shutdown. + + nsCOMArray threads; + mThreads.Enumerate(AppendAndRemoveThread, &threads); + + // Shutdown all background threads. + for (PRInt32 i = 0; i < threads.Count(); ++i) { + nsIThread *thread = threads.ObjectAt(i); + + PRBool isMainThread = PR_FALSE; + thread->IsOnCurrentThread(&isMainThread); + if (!isMainThread) + thread->Shutdown(); + } +} + +NS_IMETHODIMP +nsThreadManager::NewThread(const nsACString &name, nsIThread **result) +{ + // make sure name is unique + + nsThread *thr = new nsThread(name); + if (!thr) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(thr); + + nsresult rv = thr->Init(); + if (NS_FAILED(rv)) { + NS_RELEASE(thr); + return rv; + } + + // At this point, we expect that the thread has been registered in mThread; + // however, it is possible that it could have also been replaced by now, so + // we cannot really assert that it was added. + + *result = thr; + return NS_OK; +} + +NS_IMETHODIMP +nsThreadManager::GetThread(const nsACString &name, nsIThread **result) +{ + mThreads.Get(name, result); + return NS_OK; +} + +NS_IMETHODIMP +nsThreadManager::GetMainThread(nsIThread **result) +{ + mThreads.Get(NS_LITERAL_CSTRING("main"), result); + return NS_OK; +} + +NS_IMETHODIMP +nsThreadManager::GetCurrentThread(nsIThread **result) +{ + // read thread local storage + void *data = PR_GetThreadPrivate(mCurThreadIndex); + if (!data) { + *result = nsnull; + } else { + NS_ADDREF(*result = NS_STATIC_CAST(nsIThread *, data)); + } + return NS_OK; +} + +NS_IMETHODIMP +nsThreadManager::SetCurrentThread(nsIThread *thread, nsIThread **result) +{ + nsCOMPtr curr; + GetCurrentThread(getter_AddRefs(curr)); + + nsCString name; + if (thread) { + thread->GetName(name); + + // make sure thread name is unique + nsCOMPtr temp; + GetThread(name, getter_AddRefs(temp)); + + if (temp && temp != curr) { + NS_NOTREACHED("thread name is not unique"); + return NS_ERROR_INVALID_ARG; + } + mThreads.Put(name, thread); + + NS_ADDREF(thread); // for TLS entry + } else { + curr->GetName(name); + mThreads.Remove(name); + } + + // write thread local storage + PR_SetThreadPrivate(mCurThreadIndex, thread); + + if (result) { + *result = nsnull; + curr.swap(*result); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsThreadManager::IsMainThread(PRBool *result) +{ + nsCOMPtr mainThread; + GetMainThread(getter_AddRefs(mainThread)); + NS_ENSURE_STATE(mainThread); + + return mainThread->IsOnCurrentThread(result); +} diff --git a/mozilla/xpcom/threads/nsThreadManager.h b/mozilla/xpcom/threads/nsThreadManager.h new file mode 100644 index 00000000000..cc483f577d5 --- /dev/null +++ b/mozilla/xpcom/threads/nsThreadManager.h @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** 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 code. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsThreadManager_h__ +#define nsThreadManager_h__ + +#include "nsIThreadManager.h" +#include "nsIThread.h" +#include "nsInterfaceHashtable.h" + +class nsIRunnable; + +class nsThreadManager : public nsIThreadManager +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSITHREADMANAGER + + static nsThreadManager *get() { + return &sInstance; + } + + nsresult Init(); + + // Shutdown all threads. This function should only be called on the main + // thread of the application process. + void Shutdown(); + + static PRBool IsMainThread() { + PRBool result; + get()->IsMainThread(&result); + return result; + } + + static nsresult NewThread(const nsACString &name, nsIRunnable *runnable, + nsIThread **result) { + nsresult rv = get()->NewThread(name, result); + if (NS_SUCCEEDED(rv)) + rv = (*result)->Dispatch(runnable, NS_DISPATCH_NORMAL); + return rv; + } + +private: + nsThreadManager() : mCurThreadIndex(0) {} + ~nsThreadManager() {} + + static nsThreadManager sInstance; + + nsInterfaceHashtableMT mThreads; + PRUintn mCurThreadIndex; // thread-local-storage index +}; + +#define NS_THREADMANAGER_CLASSNAME "nsThreadManager" +#define NS_THREADMANAGER_CONTRACTID "@mozilla.org/thread-manager;1" +#define NS_THREADMANAGER_CID \ +{ /* 7a4204c6-e45a-4c37-8ebb-6709a22c917c */ \ + 0x7a4204c6, \ + 0xe45a, \ + 0x4c37, \ + {0x8e, 0xbb, 0x67, 0x09, 0xa2, 0x2c, 0x91, 0x7c} \ +} + +#endif // nsThreadManager_h__ diff --git a/mozilla/xpcom/threads/nsTimerImpl.cpp b/mozilla/xpcom/threads/nsTimerImpl.cpp index 4517dbb5561..09ba994cae7 100644 --- a/mozilla/xpcom/threads/nsTimerImpl.cpp +++ b/mozilla/xpcom/threads/nsTimerImpl.cpp @@ -41,11 +41,10 @@ #include "nsTimerImpl.h" #include "TimerThread.h" #include "nsAutoLock.h" - +#include "nsAutoPtr.h" #include "nsVoidArray.h" - -#include "nsIEventQueue.h" - +#include "nsThreadManager.h" +#include "nsRunnable.h" #include "prmem.h" static PRInt32 gGenerator = 0; @@ -150,7 +149,7 @@ nsTimerImpl::nsTimerImpl() : mTimeout(0) { // XXXbsmedberg: shouldn't this be in Init()? - nsIThread::GetCurrent(getter_AddRefs(mCallingThread)); + nsThreadManager::get()->GetCurrentThread(getter_AddRefs(mCallingThread)); mCallback.c = nsnull; @@ -428,28 +427,45 @@ void nsTimerImpl::Fire() } -struct TimerEventType : public PLEvent { - PRInt32 mGeneration; +class nsTimerEvent : public nsRunnable { +public: + NS_IMETHOD Run(); + + nsTimerEvent(nsTimerImpl *timer, PRInt32 generation) + : mTimer(timer), mGeneration(generation) { + // timer is already addref'd for us + } + #ifdef DEBUG_TIMERS PRIntervalTime mInitTime; #endif + +private: + ~nsTimerEvent() { +#ifdef DEBUG + if (mTimer) + NS_WARNING("leaking reference to nsTimerImpl"); +#endif + } + + nsTimerImpl *mTimer; + PRInt32 mGeneration; }; - -PR_STATIC_CALLBACK(void*) handleTimerEvent(PLEvent* aEvent) +NS_IMETHODIMP nsTimerEvent::Run() { - TimerEventType *event = NS_STATIC_CAST(TimerEventType*, aEvent); + nsRefPtr timer; + timer.swap(mTimer); - nsTimerImpl* timer = NS_STATIC_CAST(nsTimerImpl*, event->owner); - if (event->mGeneration != timer->GetGeneration()) - return nsnull; + if (mGeneration != timer->GetGeneration()) + return NS_OK; #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { PRIntervalTime now = PR_IntervalNow(); PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] time between PostTimerEvent() and Fire(): %dms\n", - event->owner, PR_IntervalToMilliseconds(now - event->mInitTime))); + this, PR_IntervalToMilliseconds(now - mInitTime))); } #endif @@ -460,43 +476,27 @@ PR_STATIC_CALLBACK(void*) handleTimerEvent(PLEvent* aEvent) NS_ASSERTION(gManager, "Global Thread Manager is null!"); if (gManager) gManager->AddIdleTimer(timer); - return nsnull; + return NS_OK; } } timer->Fire(); - return nsnull; + return NS_OK; } -PR_STATIC_CALLBACK(void) destroyTimerEvent(PLEvent* aEvent) -{ - TimerEventType *event = NS_STATIC_CAST(TimerEventType*, aEvent); - - nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl*, event->owner); - NS_RELEASE(timer); - PR_DELETE(event); -} - - void nsTimerImpl::PostTimerEvent() { - // XXX we may want to reuse the PLEvent in the case of repeating timers. - TimerEventType* event; - - // construct - event = PR_NEW(TimerEventType); - if (!event) - return; - - // initialize - PL_InitEvent((PLEvent*)event, this, handleTimerEvent, destroyTimerEvent); + // XXX we may want to reuse this nsTimerEvent in the case of repeating timers. // Since TimerThread addref'd 'this' for us, we don't need to addref here. - // We will release in destroyMyEvent. We do need to copy the generation - // number from this timer into the event, so we can avoid firing a timer - // that was re-initialized after being canceled. - event->mGeneration = mGeneration; + // We will release in destroyMyEvent. We need to copy the generation number + // from this timer into the event, so we can avoid firing a timer that was + // re-initialized after being canceled. + + nsTimerEvent* event = new nsTimerEvent(this, mGeneration); + if (!event) + return; #ifdef DEBUG_TIMERS if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { @@ -512,18 +512,7 @@ void nsTimerImpl::PostTimerEvent() gThread->AddTimer(this); } - PRThread *thread; - nsresult rv = mCallingThread->GetPRThread(&thread); - if (NS_FAILED(rv)) { - NS_WARNING("Dropping timer event because thread is dead"); - return; - } - - nsCOMPtr queue; - if (gThread) - gThread->mEventQueueService->GetThreadEventQueue(thread, getter_AddRefs(queue)); - if (queue) - queue->PostEvent(event); + mCallingThread->Dispatch(event, NS_DISPATCH_NORMAL); } void nsTimerImpl::SetDelayInternal(PRUint32 aDelay) @@ -614,7 +603,7 @@ nsresult nsTimerManager::AddIdleTimer(nsITimer* timer) NS_IMETHODIMP nsTimerManager::FireNextIdleTimer() { - if (!gFireOnIdle || !nsIThread::IsMainThread()) { + if (!gFireOnIdle || !nsThreadManager::IsMainThread()) { return NS_OK; } diff --git a/mozilla/xpcom/threads/nsTimerImpl.h b/mozilla/xpcom/threads/nsTimerImpl.h index 98596f4e918..731de2c4d80 100644 --- a/mozilla/xpcom/threads/nsTimerImpl.h +++ b/mozilla/xpcom/threads/nsTimerImpl.h @@ -108,7 +108,6 @@ public: private: ~nsTimerImpl(); - nsresult InitCommon(PRUint32 aType, PRUint32 aDelay); void ReleaseCallback()