/* -*- 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 * Ramiro Estrugo * Warren Harris * Leaf Nunes * David Matiskella * David Hyatt * Seth Spitzer * Suresh Duddi * Bruce Mitchener * Scott Collins * Daniel Matejka * Doug Turner * Stuart Parmenter * Mike Kaply * Dan Mosedale * * 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& aEldestQueue, void* closure) { // 'aQueue' should be the eldest queue. nsCOMPtr pie(do_QueryInterface(aEldestQueue)); nsCOMPtr q; // stop accepting events for youngest to oldest pie->GetYoungest(getter_AddRefs(q)); while (q) { q->StopAcceptingEvents(); nsCOMPtr 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 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 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 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 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 answer; if (queue) { nsCOMPtr 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 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 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 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 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 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; }