initial checkin of xpcom changes

git-svn-id: svn://10.0.0.236/branches/THREADS_20060213_BRANCH@189905 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
darin%meer.net
2006-02-14 00:11:11 +00:00
parent 28457b2f8a
commit 0028e4b5e2
41 changed files with 2089 additions and 1355 deletions

View File

@@ -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<nsIThread> thread;
nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread));
rv = proxyManager->GetProxyForObject(thread,
NS_GET_IID(nsIConsoleListener),
aListener,
PROXY_ASYNC | PROXY_ALWAYS,

View File

@@ -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<nsIThread> main;
rv = nsIThread::GetMainThread(getter_AddRefs(main));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIThread> 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<nsIEventQueueService> eqs = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
if (eqs) {
nsCOMPtr<nsIEventQueue> 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<nsIThread> 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);

View File

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

View File

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

View File

@@ -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<nsEventQueueServiceImpl> eqs;
CallGetService(NS_EVENTQUEUESERVICE_CONTRACTID,
(nsEventQueueServiceImpl**) getter_AddRefs(eqs));
nsCOMPtr <nsIEventQueue> currentQ;
NS_GetCurrentEventQ(getter_AddRefs(currentQ), eqs);
nsCOMPtr<nsIThread> thread;
nsThreadManager::get()->GetCurrentThread(getter_AddRefs(thread));
nsCOMPtr<nsIObserverService> 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);

View File

@@ -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<nsIThread> mainThread;
nsThreadManager::get()->GetMainThread(getter_AddRefs(mainThread));
nsCOMPtr<nsIObserverService> 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<nsISupportsCString> entry

View File

@@ -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,

View File

@@ -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,

View File

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

View File

@@ -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<nsIInputStreamCallback> 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<nsIInputStreamCallback> 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<nsIAsyncInputStream> mStream;
nsCOMPtr<nsIInputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> 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<nsIDispatchTarget> 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<nsIOutputStreamCallback> 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<nsIOutputStreamCallback> 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<nsIAsyncOutputStream> mStream;
nsCOMPtr<nsIOutputStreamCallback> mCallback;
nsCOMPtr<nsIEventTarget> 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<nsIDispatchTarget> 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<nsIOutputStream> mSink;
nsCOMPtr<nsIAsyncInputStream> mAsyncSource;
nsCOMPtr<nsIAsyncOutputStream> mAsyncSink;
nsCOMPtr<nsIEventTarget> mTarget;
nsCOMPtr<nsIDispatchTarget> 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,

View File

@@ -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,

View File

@@ -55,9 +55,9 @@ EXPORTS = \
nsProxiedService.h \
$(NULL)
XPIDLSRCS = nsIProxyCreateInstance.idl \
nsIProxyObjectManager.idl \
$(NULL)
XPIDLSRCS = \
nsIProxyObjectManager.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@@ -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,

View File

@@ -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<nsISupports> 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<nsISupports> 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<nsISupports> mProxiedService;
};
#endif // nsProxiedService_h_
#endif // nsProxiedService_h__

View File

@@ -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<nsIEventQueue> mDestQueue; /* destination queue */
nsCOMPtr<nsIDispatchTarget> mTarget; /* dispatch target */
nsCOMPtr<nsISupports> mRealObject; /* the non-proxy object that this event is referring to.
This is a strong ref. */
nsCOMPtr<nsIEventQueueService> 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<nsIEventQueue> mCallersEventQ; /* this is the eventQ that we must post a message back to
when we are done invoking the method (only PROXY_SYNC).
*/
nsCOMPtr<nsIDispatchTarget> 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<nsProxyObject> mOwner; /* this is the strong referenced nsProxyObject */
void RefCountInInterfacePointers(PRBool addRef);
void CopyStrings(PRBool copy);
};
#endif
#endif // nsProxyEvent_h__

View File

@@ -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<nsIEventQueueService> eventQService \
= do_GetService(kEventQueueServiceCID); \
NS_ASSERTION(eventQService, "event queue service is unavailable"); \
\
nsCOMPtr<nsIEventQueue> 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

View File

@@ -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<nsIRunnable> 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<nsIComponentManager> 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<nsIRunnable> 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<nsIEventQueue> eventQ;
rv = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(eventQ));
if (NS_FAILED(rv))
nsCOMPtr<nsIThread> 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<nsProxyCallEvent> 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);
}

View File

@@ -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<nsIInterfaceInfo> current = GetInterfaceInfo();
nsCOMPtr<nsIInterfaceInfo> 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<nsIInterfaceInfo> current = GetInterfaceInfo();
nsCOMPtr<nsIInterfaceInfo> 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);
}

View File

@@ -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<nsISupports> rootObject = do_QueryInterface(mProxyObject->mRealObject);
nsCOMPtr<nsISupports> rootQueue = do_QueryInterface(mProxyObject->mDestQueue);
nsCOMPtr<nsISupports> 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<nsISupports> destQRoot = do_QueryInterface(destQueue, &rv);
nsCOMPtr<nsISupports> destQRoot = do_QueryInterface(target, &rv);
if (NS_FAILED(rv) || !destQRoot) {
return nsnull;
}
@@ -247,10 +245,6 @@ nsProxyEventObject::GetNewOrUsedProxy(nsIEventQueue *destQueue,
return nsnull;
}
nsCOMPtr<nsIEventQueueService> 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<nsISupports> rootObject = do_QueryInterface(mProxyObject->mRealObject);
nsCOMPtr<nsISupports> rootQueue = do_QueryInterface(mProxyObject->mDestQueue);
nsCOMPtr<nsISupports> rootQueue = do_QueryInterface(mProxyObject->mTarget);
nsProxyEventKey key(rootObject, rootQueue, mProxyObject->mProxyType);
#ifdef DEBUG_dougt
void* value =

View File

@@ -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__

View File

@@ -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<nsIEventQueue> 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<nsIEventQueueService> 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<nsIProxyObjectManager> 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);
}

View File

@@ -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 <darin@meer.net>
*
* 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<nsIRunnable> 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.
}

View File

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

View File

@@ -38,6 +38,7 @@
#include "nsIThread.h"
#include "nsIRunnable.h"
#include "nsThreadManager.h"
#include <stdio.h>
#include <stdlib.h>
#include "nspr.h"
@@ -51,7 +52,7 @@ public:
NS_IMETHOD Run() {
nsCOMPtr<nsIThread> 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<nsIThread> 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<nsIThread> 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<nsIThread> 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;

View File

@@ -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 \

View File

@@ -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<nsIObserverService> observerService
(do_GetService("@mozilla.org/observer-service;1", &rv));
mEventQueueService = do_GetService("@mozilla.org/event-queue-service;1", &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIObserverService> 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;
}

View File

@@ -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<nsIEventQueueService> mEventQueueService;
void DoBeforeSleep();
void DoAfterSleep();

View File

@@ -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 <darin@meer.net>
*
* 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
%}

View File

@@ -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 <darin@meer.net>
*
* 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;
}
%}

View File

@@ -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 <darin@meer.net>
*
* 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);
};

View File

@@ -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 <darin@meer.net>
*
* 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();
};

View File

@@ -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 <darin@meer.net>
*
* 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;
}

View File

@@ -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 <darin@meer.net>
*
* 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__

View File

@@ -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 <darin@meer.net>
*
* 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<nsIRunnable> 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;
}

View File

@@ -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 <darin@meer.net>
*
* 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__

View File

@@ -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 <darin@meer.net>
*
* 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<nsThread> 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<nsIRunnable> 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<nsIThreadObserver> 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<nsIThread> 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<nsThreadSyncDispatch> 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<nsIRunnable> 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<nsIThreadObserver> obs;
{
nsAutoLock lock(mObserverLock);
obs = mObserver;
}
if (obs)
obs->OnBeforeRunNextTask(this, flags);
nsCOMPtr<nsIRunnable> 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;
}

View File

@@ -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 <darin@meer.net>
*
* 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<nsIRunnable> 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<nsIThreadObserver> mObserver;
nsTaskQueue mTasks;
nsCString mName;
PRInt32 mPriority;
PRThread *mThread;
PRIntn mActive;
};
////////////////////////////////////////////////////////////////////////////////
#endif // nsThread_h__
#endif // nsThread_h__

View File

@@ -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 <darin@meer.net>
*
* 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<nsIThread> &thread,
void *arg)
{
nsCOMArray<nsIThread> *threads =
NS_STATIC_CAST(nsCOMArray<nsIThread> *, 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<nsThread> 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<nsIThread> 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<nsIThread> curr;
GetCurrentThread(getter_AddRefs(curr));
nsCString name;
if (thread) {
thread->GetName(name);
// make sure thread name is unique
nsCOMPtr<nsIThread> 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<nsIThread> mainThread;
GetMainThread(getter_AddRefs(mainThread));
NS_ENSURE_STATE(mainThread);
return mainThread->IsOnCurrentThread(result);
}

View File

@@ -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 <darin@meer.net>
*
* 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<nsCStringHashKey, nsIThread> 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__

View File

@@ -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<nsTimerImpl> 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<nsIEventQueue> 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;
}

View File

@@ -108,7 +108,6 @@ public:
private:
~nsTimerImpl();
nsresult InitCommon(PRUint32 aType, PRUint32 aDelay);
void ReleaseCallback()