diff options
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.cpp')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.cpp | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.cpp b/src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.cpp new file mode 100644 index 00000000..4fbb65aa --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.cpp @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** 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 Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Rick Potts <rpotts@netscape.com> + * Ramiro Estrugo <ramiro@netscape.com> + * Warren Harris <warren@netscape.com> + * Leaf Nunes <leaf@mozilla.org> + * David Matiskella <davidm@netscape.com> + * David Hyatt <hyatt@netscape.com> + * Seth Spitzer <sspitzer@netscape.com> + * Suresh Duddi <dp@netscape.com> + * Bruce Mitchener <bruce@cybersight.com> + * Scott Collins <scc@netscape.com> + * Daniel Matejka <danm@netscape.com> + * Doug Turner <dougt@netscape.com> + * Stuart Parmenter <pavlov@netscape.com> + * Mike Kaply <mkaply@us.ibm.com> + * Dan Mosedale <dmose@mozilla.org> + * + * 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"), + * 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 "nsEventQueueService.h" +#include "prmon.h" +#include "nsIComponentManager.h" +#include "nsIThread.h" +#include "nsPIEventQueueChain.h" + +#include "prlog.h" + +#if defined(PR_LOGGING) || defined(DEBUG_danm) +extern PRLogModuleInfo* gEventQueueLog; +extern PRUint32 gEventQueueLogCount; +#endif + +static NS_DEFINE_CID(kEventQueueCID, NS_EVENTQUEUE_CID); + +nsEventQueueServiceImpl::nsEventQueueServiceImpl() +{ + mEventQMonitor = PR_NewMonitor(); +#if defined(PR_LOGGING) && defined(DEBUG_danm) + if (!gEventQueueLog) + gEventQueueLog = PR_NewLogModule("nseventqueue"); +#endif +} + +PR_STATIC_CALLBACK(PLDHashOperator) +hash_enum_remove_queues(const void *aThread_ptr, + nsCOMPtr<nsIEventQueue>& aEldestQueue, + void* closure) +{ + // 'aQueue' should be the eldest queue. + nsCOMPtr<nsPIEventQueueChain> pie(do_QueryInterface(aEldestQueue)); + nsCOMPtr<nsIEventQueue> q; + + // stop accepting events for youngest to oldest + pie->GetYoungest(getter_AddRefs(q)); + while (q) { + q->StopAcceptingEvents(); + + nsCOMPtr<nsPIEventQueueChain> pq(do_QueryInterface(q)); + pq->GetElder(getter_AddRefs(q)); + } + + return PL_DHASH_REMOVE; +} + +nsEventQueueServiceImpl::~nsEventQueueServiceImpl() +{ + // XXX make it so we only enum over this once + mEventQTable.Enumerate(hash_enum_remove_queues, nsnull); // call StopAcceptingEvents on everything and clear out the hashtable + + PR_DestroyMonitor(mEventQMonitor); +} + +nsresult +nsEventQueueServiceImpl::Init() +{ + NS_ENSURE_TRUE(mEventQMonitor, NS_ERROR_OUT_OF_MEMORY); + + // This will only be called once on the main thread, so it's safe to + // not enter the monitor here. + if (!mEventQTable.Init()) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // ensure that a main thread event queue exists! + nsresult rv; + nsCOMPtr<nsIThread> mainThread; + rv = nsIThread::GetMainThread(getter_AddRefs(mainThread)); + if (NS_SUCCEEDED(rv)) { + PRThread *thr; + rv = mainThread->GetPRThread(&thr); + if (NS_SUCCEEDED(rv)) + rv = CreateEventQueue(thr, PR_TRUE); + } + return rv; +} + +/* nsISupports interface implementation... */ +NS_IMPL_THREADSAFE_ISUPPORTS1(nsEventQueueServiceImpl, nsIEventQueueService) + +/* nsIEventQueueService interface implementation... */ + +NS_IMETHODIMP +nsEventQueueServiceImpl::CreateThreadEventQueue() +{ + return CreateEventQueue(PR_GetCurrentThread(), PR_TRUE); +} + +NS_IMETHODIMP +nsEventQueueServiceImpl::CreateMonitoredThreadEventQueue() +{ + return CreateEventQueue(PR_GetCurrentThread(), PR_FALSE); +} + +NS_IMETHODIMP +nsEventQueueServiceImpl::CreateFromIThread(nsIThread *aThread, PRBool aNative, + nsIEventQueue **aResult) +{ + nsresult rv; + PRThread *prThread; + + rv = aThread->GetPRThread(&prThread); + if (NS_SUCCEEDED(rv)) { + rv = CreateEventQueue(prThread, aNative); // addrefs + if (NS_SUCCEEDED(rv)) + rv = GetThreadEventQueue(prThread, aResult); // addrefs + } + return rv; +} + +// private method +NS_IMETHODIMP +nsEventQueueServiceImpl::MakeNewQueue(PRThread* thread, + PRBool aNative, + nsIEventQueue **aQueue) +{ + nsresult rv; + nsCOMPtr<nsIEventQueue> queue = do_CreateInstance(kEventQueueCID, &rv); + + if (NS_SUCCEEDED(rv)) { + rv = queue->InitFromPRThread(thread, aNative); + } + *aQueue = queue; + NS_IF_ADDREF(*aQueue); + return rv; +} + +// private method +NS_IMETHODIMP +nsEventQueueServiceImpl::CreateEventQueue(PRThread *aThread, PRBool aNative) +{ + nsresult rv = NS_OK; + /* Enter the lock that protects the EventQ hashtable... */ + PR_EnterMonitor(mEventQMonitor); + + /* create only one event queue chain per thread... */ + if (!mEventQTable.GetWeak(aThread)) { + nsCOMPtr<nsIEventQueue> queue; + + // we don't have one in the table + rv = MakeNewQueue(aThread, aNative, getter_AddRefs(queue)); // create new queue + mEventQTable.Put(aThread, queue); // add to the table (initial addref) + } + + // Release the EventQ lock... + PR_ExitMonitor(mEventQMonitor); + return rv; +} + + +NS_IMETHODIMP +nsEventQueueServiceImpl::DestroyThreadEventQueue(void) +{ + nsresult rv = NS_OK; + + /* Enter the lock that protects the EventQ hashtable... */ + PR_EnterMonitor(mEventQMonitor); + + PRThread* currentThread = PR_GetCurrentThread(); + nsIEventQueue* queue = mEventQTable.GetWeak(currentThread); + if (queue) { + queue->StopAcceptingEvents(); // tell the queue to stop accepting events + queue = nsnull; // Queue may die on the next line + mEventQTable.Remove(currentThread); // remove nsIEventQueue from hash table (releases) + } + + // Release the EventQ lock... + PR_ExitMonitor(mEventQMonitor); + return rv; +} + +NS_IMETHODIMP +nsEventQueueServiceImpl::CreateFromPLEventQueue(PLEventQueue* aPLEventQueue, nsIEventQueue** aResult) +{ + // Create our thread queue using the component manager + nsresult rv; + nsCOMPtr<nsIEventQueue> queue = do_CreateInstance(kEventQueueCID, &rv); + if (NS_FAILED(rv)) return rv; + + rv = queue->InitFromPLQueue(aPLEventQueue); + if (NS_FAILED(rv)) return rv; + + *aResult = queue; + NS_IF_ADDREF(*aResult); + return NS_OK; +} + + +// Return the active event queue on our chain +/* inline */ +nsresult nsEventQueueServiceImpl::GetYoungestEventQueue(nsIEventQueue *queue, nsIEventQueue **aResult) +{ + nsCOMPtr<nsIEventQueue> answer; + + if (queue) { + nsCOMPtr<nsPIEventQueueChain> ourChain(do_QueryInterface(queue)); + if (ourChain) + ourChain->GetYoungestActive(getter_AddRefs(answer)); + else + answer = queue; + } + + *aResult = answer; + NS_IF_ADDREF(*aResult); + return NS_OK; +} + + +// create new event queue, append it to the current thread's chain of event queues. +// return it, addrefed. +NS_IMETHODIMP +nsEventQueueServiceImpl::PushThreadEventQueue(nsIEventQueue **aNewQueue) +{ + nsresult rv = NS_OK; + PRThread* currentThread = PR_GetCurrentThread(); + PRBool native = PR_TRUE; // native by default as per old comment + + + NS_ASSERTION(aNewQueue, "PushThreadEventQueue called with null param"); + + /* Enter the lock that protects the EventQ hashtable... */ + PR_EnterMonitor(mEventQMonitor); + + nsIEventQueue* queue = mEventQTable.GetWeak(currentThread); + + NS_ASSERTION(queue, "pushed event queue on top of nothing"); + + if (queue) { // find out what kind of queue our relatives are + nsCOMPtr<nsIEventQueue> youngQueue; + GetYoungestEventQueue(queue, getter_AddRefs(youngQueue)); + if (youngQueue) { + youngQueue->IsQueueNative(&native); + } + } + + nsIEventQueue* newQueue = nsnull; + MakeNewQueue(currentThread, native, &newQueue); // create new queue; addrefs + + if (!queue) { + // shouldn't happen. as a fallback, we guess you wanted a native queue + mEventQTable.Put(currentThread, newQueue); + } + + // append to the event queue chain + nsCOMPtr<nsPIEventQueueChain> ourChain(do_QueryInterface(queue)); // QI the queue in the hash table + if (ourChain) + ourChain->AppendQueue(newQueue); // append new queue to it + + *aNewQueue = newQueue; + +#if defined(PR_LOGGING) && defined(DEBUG_danm) + PLEventQueue *equeue; + (*aNewQueue)->GetPLEventQueue(&equeue); + PR_LOG(gEventQueueLog, PR_LOG_DEBUG, + ("EventQueue: Service push queue [queue=%lx]",(long)equeue)); + ++gEventQueueLogCount; +#endif + + // Release the EventQ lock... + PR_ExitMonitor(mEventQMonitor); + return rv; +} + +// disable and release the given queue (though the last one won't be released) +NS_IMETHODIMP +nsEventQueueServiceImpl::PopThreadEventQueue(nsIEventQueue *aQueue) +{ + PRThread* currentThread = PR_GetCurrentThread(); + + /* Enter the lock that protects the EventQ hashtable... */ + PR_EnterMonitor(mEventQMonitor); + + nsCOMPtr<nsIEventQueue> eldestQueue; + mEventQTable.Get(currentThread, getter_AddRefs(eldestQueue)); + + // If we are popping the eldest queue, remove its mEventQTable entry. + if (aQueue == eldestQueue) + mEventQTable.Remove(currentThread); + + // Exit the monitor before processing pending events to avoid deadlock. + // Our reference from the eldestQueue nsCOMPtr will keep that object alive. + // Since it is thread-private, no one else can race with us here. + PR_ExitMonitor(mEventQMonitor); + if (!eldestQueue) + return NS_ERROR_FAILURE; + +#if defined(PR_LOGGING) && defined(DEBUG_danm) + PLEventQueue *equeue; + aQueue->GetPLEventQueue(&equeue); + PR_LOG(gEventQueueLog, PR_LOG_DEBUG, + ("EventQueue: Service pop queue [queue=%lx]",(long)equeue)); + ++gEventQueueLogCount; +#endif + aQueue->StopAcceptingEvents(); + aQueue->ProcessPendingEvents(); // make sure we don't orphan any events + + return NS_OK; +} + +NS_IMETHODIMP +nsEventQueueServiceImpl::GetThreadEventQueue(PRThread* aThread, nsIEventQueue** aResult) +{ + /* Parameter validation... */ + if (NULL == aResult) return NS_ERROR_NULL_POINTER; + + PRThread* keyThread = aThread; + + if (keyThread == NS_CURRENT_THREAD) + { + keyThread = PR_GetCurrentThread(); + } + else if (keyThread == NS_UI_THREAD) + { + nsCOMPtr<nsIThread> mainIThread; + + // Get the primordial thread + nsresult rv = nsIThread::GetMainThread(getter_AddRefs(mainIThread)); + if (NS_FAILED(rv)) return rv; + + rv = mainIThread->GetPRThread(&keyThread); + if (NS_FAILED(rv)) return rv; + } + + /* Enter the lock that protects the EventQ hashtable... */ + PR_EnterMonitor(mEventQMonitor); + + nsCOMPtr<nsIEventQueue> queue; + mEventQTable.Get(keyThread, getter_AddRefs(queue)); + + PR_ExitMonitor(mEventQMonitor); + + if (queue) { + GetYoungestEventQueue(queue, aResult); // get the youngest active queue + } else { + *aResult = nsnull; + } + // XXX: Need error code for requesting an event queue when none exists... + if (!*aResult) { + return NS_ERROR_NOT_AVAILABLE; + } + return NS_OK; +} + + +NS_IMETHODIMP +nsEventQueueServiceImpl::ResolveEventQueue(nsIEventQueue* queueOrConstant, nsIEventQueue* *resultQueue) +{ + if (queueOrConstant == NS_CURRENT_EVENTQ) { + return GetThreadEventQueue(NS_CURRENT_THREAD, resultQueue); + } + else if (queueOrConstant == NS_UI_THREAD_EVENTQ) { + return GetThreadEventQueue(NS_UI_THREAD, resultQueue); + } + + *resultQueue = queueOrConstant; + NS_ADDREF(*resultQueue); + return NS_OK; +} + +NS_IMETHODIMP +nsEventQueueServiceImpl::GetSpecialEventQueue(PRInt32 aQueue, + nsIEventQueue* *_retval) +{ + nsresult rv; + + // barf if someone gave us a zero pointer + // + if (!_retval) { + return NS_ERROR_NULL_POINTER; + } + + // try and get the requested event queue, returning NS_ERROR_FAILURE if there + // is a problem. GetThreadEventQueue() does the AddRef() for us. + // + switch (aQueue) { + case CURRENT_THREAD_EVENT_QUEUE: + rv = GetThreadEventQueue(NS_CURRENT_THREAD, _retval); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + break; + + case UI_THREAD_EVENT_QUEUE: + rv = GetThreadEventQueue(NS_UI_THREAD, _retval); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + break; + + // somebody handed us a bogus constant + // + default: + return NS_ERROR_ILLEGAL_VALUE; + } + + return NS_OK; +} + |