Back to index

lightning-sunbird  0.9+nobinonly
nsEventQueueService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Rick Potts <rpotts@netscape.com>
00024  *   Ramiro Estrugo <ramiro@netscape.com>
00025  *   Warren Harris <warren@netscape.com>
00026  *   Leaf Nunes <leaf@mozilla.org>
00027  *   David Matiskella <davidm@netscape.com>
00028  *   David Hyatt <hyatt@netscape.com>
00029  *   Seth Spitzer <sspitzer@netscape.com>
00030  *   Suresh Duddi <dp@netscape.com>
00031  *   Bruce Mitchener <bruce@cybersight.com>
00032  *   Scott Collins <scc@netscape.com>
00033  *   Daniel Matejka <danm@netscape.com>
00034  *   Doug Turner <dougt@netscape.com>
00035  *   Stuart Parmenter <pavlov@netscape.com>
00036  *   Mike Kaply <mkaply@us.ibm.com>
00037  *   Dan Mosedale <dmose@mozilla.org>
00038  *
00039  * Alternatively, the contents of this file may be used under the terms of
00040  * either of the GNU General Public License Version 2 or later (the "GPL"),
00041  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00042  * in which case the provisions of the GPL or the LGPL are applicable instead
00043  * of those above. If you wish to allow use of your version of this file only
00044  * under the terms of either the GPL or the LGPL, and not to allow others to
00045  * use your version of this file under the terms of the MPL, indicate your
00046  * decision by deleting the provisions above and replace them with the notice
00047  * and other provisions required by the GPL or the LGPL. If you do not delete
00048  * the provisions above, a recipient may use your version of this file under
00049  * the terms of any one of the MPL, the GPL or the LGPL.
00050  *
00051  * ***** END LICENSE BLOCK ***** */
00052 
00053 #include "nsEventQueueService.h"
00054 #include "prmon.h"
00055 #include "nsIComponentManager.h"
00056 #include "nsIThread.h"
00057 #include "nsPIEventQueueChain.h"
00058 
00059 #include "prlog.h"
00060 
00061 #if defined(PR_LOGGING) || defined(DEBUG_danm)
00062 extern PRLogModuleInfo* gEventQueueLog;
00063 extern PRUint32 gEventQueueLogCount;
00064 #endif
00065 
00066 static NS_DEFINE_CID(kEventQueueCID, NS_EVENTQUEUE_CID);
00067 
00068 nsEventQueueServiceImpl::nsEventQueueServiceImpl()
00069 {
00070   mEventQMonitor = PR_NewMonitor();
00071 #if defined(PR_LOGGING) && defined(DEBUG_danm)
00072   if (!gEventQueueLog)
00073     gEventQueueLog = PR_NewLogModule("nseventqueue");
00074 #endif
00075 }
00076 
00077 PR_STATIC_CALLBACK(PLDHashOperator)
00078 hash_enum_remove_queues(const void *aThread_ptr,
00079                         nsCOMPtr<nsIEventQueue>& aEldestQueue,
00080                         void* closure)
00081 {
00082   // 'aQueue' should be the eldest queue.
00083   nsCOMPtr<nsPIEventQueueChain> pie(do_QueryInterface(aEldestQueue));
00084   nsCOMPtr<nsIEventQueue> q;
00085 
00086   // stop accepting events for youngest to oldest
00087   pie->GetYoungest(getter_AddRefs(q));
00088   while (q) {
00089     q->StopAcceptingEvents();
00090 
00091     nsCOMPtr<nsPIEventQueueChain> pq(do_QueryInterface(q));
00092     pq->GetElder(getter_AddRefs(q));
00093   }
00094 
00095   return PL_DHASH_REMOVE;
00096 }
00097 
00098 nsEventQueueServiceImpl::~nsEventQueueServiceImpl()
00099 {
00100   // XXX make it so we only enum over this once
00101   mEventQTable.Enumerate(hash_enum_remove_queues, nsnull); // call StopAcceptingEvents on everything and clear out the hashtable
00102 
00103   PR_DestroyMonitor(mEventQMonitor);
00104 }
00105 
00106 nsresult
00107 nsEventQueueServiceImpl::Init()
00108 {
00109   NS_ENSURE_TRUE(mEventQMonitor, NS_ERROR_OUT_OF_MEMORY);
00110 
00111   // This will only be called once on the main thread, so it's safe to
00112   // not enter the monitor here.
00113   if (!mEventQTable.Init()) {
00114     return NS_ERROR_OUT_OF_MEMORY;
00115   }
00116 
00117   // ensure that a main thread event queue exists!
00118   nsresult rv;
00119   nsCOMPtr<nsIThread> mainThread;
00120   rv = nsIThread::GetMainThread(getter_AddRefs(mainThread));
00121   if (NS_SUCCEEDED(rv)) {
00122     PRThread *thr;
00123     rv = mainThread->GetPRThread(&thr);
00124     if (NS_SUCCEEDED(rv))
00125       rv = CreateEventQueue(thr, PR_TRUE);
00126   }
00127   return rv;
00128 }
00129 
00130 /* nsISupports interface implementation... */
00131 NS_IMPL_THREADSAFE_ISUPPORTS1(nsEventQueueServiceImpl, nsIEventQueueService)
00132 
00133 /* nsIEventQueueService interface implementation... */
00134 
00135 NS_IMETHODIMP
00136 nsEventQueueServiceImpl::CreateThreadEventQueue()
00137 {
00138   return CreateEventQueue(PR_GetCurrentThread(), PR_TRUE);
00139 }
00140 
00141 NS_IMETHODIMP
00142 nsEventQueueServiceImpl::CreateMonitoredThreadEventQueue()
00143 {
00144   return CreateEventQueue(PR_GetCurrentThread(), PR_FALSE);
00145 }
00146 
00147 NS_IMETHODIMP
00148 nsEventQueueServiceImpl::CreateFromIThread(nsIThread *aThread, PRBool aNative,
00149                                            nsIEventQueue **aResult)
00150 {
00151   nsresult rv;
00152   PRThread *prThread;
00153 
00154   rv = aThread->GetPRThread(&prThread);
00155   if (NS_SUCCEEDED(rv)) {
00156     rv = CreateEventQueue(prThread, aNative); // addrefs
00157     if (NS_SUCCEEDED(rv))
00158       rv = GetThreadEventQueue(prThread, aResult); // addrefs
00159   }
00160   return rv;
00161 }
00162 
00163 // private method
00164 NS_IMETHODIMP
00165 nsEventQueueServiceImpl::MakeNewQueue(PRThread* thread,
00166                                       PRBool aNative,
00167                                       nsIEventQueue **aQueue)
00168 {
00169   nsresult rv;
00170   nsCOMPtr<nsIEventQueue> queue = do_CreateInstance(kEventQueueCID, &rv);
00171 
00172   if (NS_SUCCEEDED(rv)) {
00173     rv = queue->InitFromPRThread(thread, aNative);
00174     if (NS_FAILED(rv))
00175       queue = nsnull;
00176   }
00177   *aQueue = queue;
00178   NS_IF_ADDREF(*aQueue);
00179   return rv;
00180 }
00181 
00182 // private method
00183 NS_IMETHODIMP
00184 nsEventQueueServiceImpl::CreateEventQueue(PRThread *aThread, PRBool aNative)
00185 {
00186   nsresult rv = NS_OK;
00187   /* Enter the lock that protects the EventQ hashtable... */
00188   PR_EnterMonitor(mEventQMonitor);
00189 
00190   /* create only one event queue chain per thread... */
00191   if (!mEventQTable.GetWeak(aThread)) {
00192     nsCOMPtr<nsIEventQueue> queue;
00193 
00194     // we don't have one in the table, create new queue
00195     rv = MakeNewQueue(aThread, aNative, getter_AddRefs(queue));
00196     if (NS_SUCCEEDED(rv))
00197       mEventQTable.Put(aThread, queue); // add to the table (initial addref)
00198   }
00199 
00200   // Release the EventQ lock...
00201   PR_ExitMonitor(mEventQMonitor);
00202   return rv;
00203 }
00204 
00205 
00206 NS_IMETHODIMP
00207 nsEventQueueServiceImpl::DestroyThreadEventQueue(void)
00208 {
00209   nsresult rv = NS_OK;
00210 
00211   /* Enter the lock that protects the EventQ hashtable... */
00212   PR_EnterMonitor(mEventQMonitor);
00213 
00214   PRThread* currentThread = PR_GetCurrentThread();
00215   nsIEventQueue* queue = mEventQTable.GetWeak(currentThread);
00216   if (queue) {
00217     queue->StopAcceptingEvents(); // tell the queue to stop accepting events
00218     queue = nsnull; // Queue may die on the next line
00219     mEventQTable.Remove(currentThread); // remove nsIEventQueue from hash table (releases)
00220   }
00221 
00222   // Release the EventQ lock...
00223   PR_ExitMonitor(mEventQMonitor);
00224   return rv;
00225 }
00226 
00227 NS_IMETHODIMP
00228 nsEventQueueServiceImpl::CreateFromPLEventQueue(PLEventQueue* aPLEventQueue, nsIEventQueue** aResult)
00229 {
00230   // Create our thread queue using the component manager
00231   nsresult rv;
00232   nsCOMPtr<nsIEventQueue> queue = do_CreateInstance(kEventQueueCID, &rv);
00233   if (NS_FAILED(rv)) return rv;
00234 
00235   rv = queue->InitFromPLQueue(aPLEventQueue);
00236   if (NS_FAILED(rv)) return rv;
00237 
00238   *aResult = queue;
00239   NS_IF_ADDREF(*aResult);
00240   return NS_OK;
00241 }
00242 
00243 
00244 // Return the active event queue on our chain
00245 /* inline */
00246 nsresult nsEventQueueServiceImpl::GetYoungestEventQueue(nsIEventQueue *queue, nsIEventQueue **aResult)
00247 {
00248   nsCOMPtr<nsIEventQueue> answer;
00249 
00250   if (queue) {
00251     nsCOMPtr<nsPIEventQueueChain> ourChain(do_QueryInterface(queue));
00252     if (ourChain)
00253       ourChain->GetYoungestActive(getter_AddRefs(answer));
00254     else
00255       answer = queue;
00256   }
00257 
00258   *aResult = answer;
00259   NS_IF_ADDREF(*aResult);
00260   return NS_OK;
00261 }
00262 
00263 
00264 // create new event queue, append it to the current thread's chain of event queues.
00265 // return it, addrefed.
00266 NS_IMETHODIMP
00267 nsEventQueueServiceImpl::PushThreadEventQueue(nsIEventQueue **aNewQueue)
00268 {
00269   nsresult rv = NS_OK;
00270   PRThread* currentThread = PR_GetCurrentThread();
00271   PRBool native = PR_TRUE; // native by default as per old comment
00272 
00273 
00274   NS_ASSERTION(aNewQueue, "PushThreadEventQueue called with null param");
00275 
00276   /* Enter the lock that protects the EventQ hashtable... */
00277   PR_EnterMonitor(mEventQMonitor);
00278 
00279   nsIEventQueue* queue = mEventQTable.GetWeak(currentThread);
00280 
00281   NS_ASSERTION(queue, "pushed event queue on top of nothing");
00282 
00283   if (queue) { // find out what kind of queue our relatives are
00284     nsCOMPtr<nsIEventQueue> youngQueue;
00285     GetYoungestEventQueue(queue, getter_AddRefs(youngQueue));
00286     if (youngQueue) {
00287       youngQueue->IsQueueNative(&native);
00288     }
00289   }
00290 
00291   nsIEventQueue* newQueue = nsnull;
00292   rv = MakeNewQueue(currentThread, native, &newQueue);  // AddRefs on success
00293   if (NS_SUCCEEDED(rv)) {
00294     if (!queue) {
00295       // shouldn't happen. as a fallback, we guess you wanted a native queue
00296       mEventQTable.Put(currentThread, newQueue);
00297     }
00298 
00299       // append to the event queue chain -- QI the queue in the hash table
00300       nsCOMPtr<nsPIEventQueueChain> ourChain(do_QueryInterface(queue));
00301     if (ourChain)
00302       ourChain->AppendQueue(newQueue); // append new queue to it
00303 
00304     *aNewQueue = newQueue;
00305 
00306 #if defined(PR_LOGGING) && defined(DEBUG_danm)
00307     PLEventQueue *equeue;
00308     (*aNewQueue)->GetPLEventQueue(&equeue);
00309     PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
00310            ("EventQueue: Service push queue [queue=%lx]",(long)equeue));
00311     ++gEventQueueLogCount;
00312 #endif
00313   }
00314 
00315   // Release the EventQ lock...
00316   PR_ExitMonitor(mEventQMonitor);
00317   return rv;
00318 }
00319 
00320 // disable and release the given queue (though the last one won't be released)
00321 NS_IMETHODIMP
00322 nsEventQueueServiceImpl::PopThreadEventQueue(nsIEventQueue *aQueue)
00323 {
00324   PRThread* currentThread = PR_GetCurrentThread();
00325 
00326   /* Enter the lock that protects the EventQ hashtable... */
00327   PR_EnterMonitor(mEventQMonitor);
00328 
00329   nsCOMPtr<nsIEventQueue> eldestQueue;
00330   mEventQTable.Get(currentThread, getter_AddRefs(eldestQueue));
00331 
00332   // If we are popping the eldest queue, remove its mEventQTable entry.
00333   if (aQueue == eldestQueue)
00334     mEventQTable.Remove(currentThread);
00335 
00336   // Exit the monitor before processing pending events to avoid deadlock.
00337   // Our reference from the eldestQueue nsCOMPtr will keep that object alive.
00338   // Since it is thread-private, no one else can race with us here.
00339   PR_ExitMonitor(mEventQMonitor);
00340   if (!eldestQueue)
00341     return NS_ERROR_FAILURE;
00342 
00343 #if defined(PR_LOGGING) && defined(DEBUG_danm)
00344   PLEventQueue *equeue;
00345   aQueue->GetPLEventQueue(&equeue);
00346   PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
00347          ("EventQueue: Service pop queue [queue=%lx]",(long)equeue));
00348   ++gEventQueueLogCount;
00349 #endif
00350   aQueue->StopAcceptingEvents();
00351   aQueue->ProcessPendingEvents(); // make sure we don't orphan any events
00352 
00353   return NS_OK;
00354 }
00355 
00356 NS_IMETHODIMP
00357 nsEventQueueServiceImpl::GetThreadEventQueue(PRThread* aThread, nsIEventQueue** aResult)
00358 {
00359   /* Parameter validation... */
00360   if (NULL == aResult) return NS_ERROR_NULL_POINTER;
00361 
00362   PRThread* keyThread = aThread;
00363 
00364   if (keyThread == NS_CURRENT_THREAD)
00365   {
00366      keyThread = PR_GetCurrentThread();
00367   }
00368   else if (keyThread == NS_UI_THREAD)
00369   {
00370     nsCOMPtr<nsIThread>  mainIThread;
00371 
00372     // Get the primordial thread
00373     nsresult rv = nsIThread::GetMainThread(getter_AddRefs(mainIThread));
00374     if (NS_FAILED(rv)) return rv;
00375 
00376     rv = mainIThread->GetPRThread(&keyThread);
00377     if (NS_FAILED(rv)) return rv;
00378   }
00379 
00380   /* Enter the lock that protects the EventQ hashtable... */
00381   PR_EnterMonitor(mEventQMonitor);
00382 
00383   nsCOMPtr<nsIEventQueue> queue;
00384   mEventQTable.Get(keyThread, getter_AddRefs(queue));
00385 
00386   PR_ExitMonitor(mEventQMonitor);
00387 
00388   if (queue) {
00389     GetYoungestEventQueue(queue, aResult); // get the youngest active queue
00390   } else {
00391     *aResult = nsnull;
00392   }
00393   // XXX: Need error code for requesting an event queue when none exists...
00394   if (!*aResult) {
00395     return NS_ERROR_NOT_AVAILABLE;
00396   }
00397   return NS_OK;
00398 }
00399 
00400 
00401 NS_IMETHODIMP
00402 nsEventQueueServiceImpl::ResolveEventQueue(nsIEventQueue* queueOrConstant, nsIEventQueue* *resultQueue)
00403 {
00404   if (queueOrConstant == NS_CURRENT_EVENTQ) {
00405     return GetThreadEventQueue(NS_CURRENT_THREAD, resultQueue);
00406   }
00407   else if (queueOrConstant == NS_UI_THREAD_EVENTQ) {
00408     return GetThreadEventQueue(NS_UI_THREAD, resultQueue);
00409   }
00410 
00411   *resultQueue = queueOrConstant;
00412   NS_ADDREF(*resultQueue);
00413   return NS_OK;
00414 }
00415 
00416 NS_IMETHODIMP
00417 nsEventQueueServiceImpl::GetSpecialEventQueue(PRInt32 aQueue,
00418                                               nsIEventQueue* *_retval)
00419 {
00420   nsresult rv;
00421 
00422   // barf if someone gave us a zero pointer
00423   //
00424   if (!_retval) {
00425     return NS_ERROR_NULL_POINTER;
00426   }
00427 
00428   // try and get the requested event queue, returning NS_ERROR_FAILURE if there
00429   // is a problem.  GetThreadEventQueue() does the AddRef() for us.
00430   //
00431   switch (aQueue) {
00432   case CURRENT_THREAD_EVENT_QUEUE:
00433     rv = GetThreadEventQueue(NS_CURRENT_THREAD, _retval);
00434     if (NS_FAILED(rv)) {
00435       return NS_ERROR_FAILURE;
00436     }
00437     break;
00438 
00439   case UI_THREAD_EVENT_QUEUE:
00440     rv = GetThreadEventQueue(NS_UI_THREAD, _retval);
00441     if (NS_FAILED(rv)) {
00442       return NS_ERROR_FAILURE;
00443     }
00444     break;
00445 
00446     // somebody handed us a bogus constant
00447     //
00448   default:
00449     return NS_ERROR_ILLEGAL_VALUE;
00450   }
00451 
00452   return NS_OK;
00453 }
00454