summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libs/xpcom18a4/xpcom/threads/nsEventQueue.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.cpp b/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.cpp
new file mode 100644
index 00000000..7dc8f089
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.cpp
@@ -0,0 +1,632 @@
+/* -*- 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):
+ *
+ * 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 "nsCOMPtr.h"
+#include "nsEventQueue.h"
+#include "nsIEventQueueService.h"
+#include "nsIThread.h"
+
+#include "nsIServiceManager.h"
+#include "nsIObserverService.h"
+
+#include "nsString.h"
+
+#include "prlog.h"
+
+#ifdef NS_DEBUG
+#include "prprf.h"
+#endif
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+/* found these logs useful in conjunction with netlibStreamEvent logging
+ from netwerk. */
+PRLogModuleInfo* gEventQueueLog = 0;
+PRUint32 gEventQueueLogCount = 0;
+PRUint32 gEventQueueLogPPCount = 0;
+static int gEventQueueLogPPLevel = 0;
+static PLEventQueue *gEventQueueLogQueue = 0;
+static PRThread *gEventQueueLogThread = 0;
+#endif
+
+// in a real system, these would be members in a header class...
+static const char gActivatedNotification[] = "nsIEventQueueActivated";
+static const char gDestroyedNotification[] = "nsIEventQueueDestroyed";
+
+nsEventQueueImpl::nsEventQueueImpl()
+{
+ NS_ADDREF_THIS();
+ /* The slightly weird ownership model for eventqueues goes like this:
+
+ General:
+ There's an addref from the factory generally held by whoever asked for
+ the queue. The queue addrefs itself (right here) and releases itself
+ after someone calls StopAcceptingEvents() on the queue and when it is
+ dark and empty (in CheckForDeactivation()).
+
+ Chained queues:
+
+ Eldest queue:
+ The eldest queue in a chain is held on to by the EventQueueService
+ in a hash table, so it is possible that the eldest queue may not be
+ released until the EventQueueService is shutdown.
+ You may not call StopAcceptingEvents() on this queue until you have
+ done so on all younger queues.
+
+ General:
+ Each queue holds a reference to their immediate elder link and a weak
+ reference to their immediate younger link. Because you must shut down
+ queues from youngest to eldest, all the references will be removed.
+
+ It happens something like:
+ queue->StopAcceptingEvents()
+ {
+ CheckForDeactivation()
+ {
+ -- hopefully we are able to shutdown now --
+ Unlink()
+ {
+ -- remove the reference we hold to our elder queue --
+ -- NULL out our elder queues weak reference to us --
+ }
+ RELEASE ourself (to balance the ADDREF here in the constructor)
+ -- and we should go away. --
+ }
+ }
+
+
+ Notes:
+ A dark queue no longer accepts events. An empty queue simply has no events.
+ */
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Created [queue=%lx]",(long)mEventQueue));
+ ++gEventQueueLogCount;
+#endif
+
+ mYoungerQueue = nsnull;
+ mEventQueue = nsnull;
+ mAcceptingEvents = PR_TRUE;
+ mCouldHaveEvents = PR_TRUE;
+}
+
+nsEventQueueImpl::~nsEventQueueImpl()
+{
+ Unlink();
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Destroyed [queue=%lx]",(long)mEventQueue));
+ ++gEventQueueLogCount;
+#endif
+
+ if (mEventQueue) {
+ NotifyObservers(gDestroyedNotification);
+ PL_DestroyEventQueue(mEventQueue);
+ }
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::Init(PRBool aNative)
+{
+ PRThread *thread = PR_GetCurrentThread();
+ if (aNative)
+ mEventQueue = PL_CreateNativeEventQueue("Thread event queue...", thread);
+ else
+ mEventQueue = PL_CreateMonitoredEventQueue("Thread event queue...", thread);
+ NotifyObservers(gActivatedNotification);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::InitFromPRThread(PRThread* thread, PRBool aNative)
+{
+ if (thread == NS_CURRENT_THREAD)
+ {
+ thread = PR_GetCurrentThread();
+ }
+ else if (thread == NS_UI_THREAD)
+ {
+ nsCOMPtr<nsIThread> mainIThread;
+ nsresult rv;
+
+ // Get the primordial thread
+ rv = nsIThread::GetMainThread(getter_AddRefs(mainIThread));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = mainIThread->GetPRThread(&thread);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ if (aNative)
+ mEventQueue = PL_CreateNativeEventQueue("Thread event queue...", thread);
+ else
+ mEventQueue = PL_CreateMonitoredEventQueue("Thread event queue...", thread);
+ NotifyObservers(gActivatedNotification);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::InitFromPLQueue(PLEventQueue* aQueue)
+{
+ mEventQueue = aQueue;
+ NotifyObservers(gActivatedNotification);
+ return NS_OK;
+}
+
+/* nsISupports interface implementation... */
+NS_IMPL_THREADSAFE_ISUPPORTS3(nsEventQueueImpl,
+ nsIEventQueue,
+ nsIEventTarget,
+ nsPIEventQueueChain)
+
+/* nsIEventQueue interface implementation... */
+
+NS_IMETHODIMP
+nsEventQueueImpl::StopAcceptingEvents()
+{
+ // this assertion is bogus. I should be able to shut down the eldest queue,
+ // as long as there are no younger children
+
+
+ NS_ASSERTION(mElderQueue || !mYoungerQueue, "attempted to disable eldest queue in chain");
+ mAcceptingEvents = PR_FALSE;
+ CheckForDeactivation();
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: StopAccepting [queue=%lx, accept=%d, could=%d]",
+ (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
+ ++gEventQueueLogCount;
+#endif
+ return NS_OK;
+}
+
+// utility funtion to send observers a notification
+void
+nsEventQueueImpl::NotifyObservers(const char *aTopic)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIEventQueue> kungFuDeathGrip(this);
+ nsCOMPtr<nsISupports> us(do_QueryInterface(kungFuDeathGrip));
+ os->NotifyObservers(us, aTopic, NULL);
+ }
+}
+
+
+NS_IMETHODIMP
+nsEventQueueImpl::InitEvent(PLEvent* aEvent,
+ void* owner,
+ PLHandleEventProc handler,
+ PLDestroyEventProc destructor)
+{
+ PL_InitEvent(aEvent, owner, handler, destructor);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsEventQueueImpl::PostEvent(PLEvent* aEvent)
+{
+ if (!mAcceptingEvents) {
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Punt posted event [queue=%lx, accept=%d, could=%d]",
+ (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
+ ++gEventQueueLogCount;
+#endif
+ nsresult rv = NS_ERROR_FAILURE;
+ NS_ASSERTION(mElderQueue, "event dropped because event chain is dead");
+ if (mElderQueue) {
+ nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
+ if (elder)
+ rv = elder->PostEvent(aEvent);
+ }
+ return rv;
+ }
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Posting event [queue=%lx]", (long)mEventQueue));
+ ++gEventQueueLogCount;
+#endif
+ return PL_PostEvent(mEventQueue, aEvent) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::PostSynchronousEvent(PLEvent* aEvent, void** aResult)
+{
+ if (!mAcceptingEvents) {
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Punt posted synchronous event [queue=%lx, accept=%d, could=%d]",
+ (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
+ ++gEventQueueLogCount;
+#endif
+ nsresult rv = NS_ERROR_NO_INTERFACE;
+ NS_ASSERTION(mElderQueue, "event dropped because event chain is dead");
+ if (mElderQueue) {
+ nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
+ if (elder)
+ rv = elder->PostSynchronousEvent(aEvent, aResult);
+ return rv;
+ }
+ return NS_ERROR_ABORT;
+ }
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Posting synchronous event [queue=%lx]", (long)mEventQueue));
+ ++gEventQueueLogCount;
+#endif
+ void* result = PL_PostSynchronousEvent(mEventQueue, aEvent);
+ if (aResult)
+ *aResult = result;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::EnterMonitor()
+{
+ PL_ENTER_EVENT_QUEUE_MONITOR(mEventQueue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::ExitMonitor()
+{
+ PL_EXIT_EVENT_QUEUE_MONITOR(mEventQueue);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsEventQueueImpl::RevokeEvents(void* owner)
+{
+ PL_RevokeEvents(mEventQueue, owner);
+ if (mElderQueue) {
+ nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
+ if (elder)
+ elder->RevokeEvents(owner);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsEventQueueImpl::GetPLEventQueue(PLEventQueue** aEventQueue)
+{
+ if (!mEventQueue)
+ return NS_ERROR_NULL_POINTER;
+
+ *aEventQueue = mEventQueue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::IsOnCurrentThread(PRBool *aResult)
+{
+ *aResult = PL_IsQueueOnCurrentThread( mEventQueue );
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsEventQueueImpl::IsQueueNative(PRBool *aResult)
+{
+ *aResult = PL_IsQueueNative(mEventQueue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::PendingEvents(PRBool *aResult)
+{
+ *aResult = PL_EventAvailable(mEventQueue);
+ if (!*aResult && mElderQueue) {
+ nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
+ if (elder)
+ return elder->EventAvailable(*aResult);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsEventQueueImpl::ProcessPendingEvents()
+{
+ PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
+
+ NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
+
+ if (!correctThread)
+ return NS_ERROR_FAILURE;
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ ++gEventQueueLogPPLevel;
+ if ((gEventQueueLogQueue != mEventQueue || gEventQueueLogThread != PR_GetCurrentThread() ||
+ gEventQueueLogCount != gEventQueueLogPPCount) && gEventQueueLogPPLevel == 1) {
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: Process pending [queue=%lx, accept=%d, could=%d]",
+ (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
+ gEventQueueLogPPCount = ++gEventQueueLogCount;
+ gEventQueueLogQueue = mEventQueue;
+ gEventQueueLogThread = PR_GetCurrentThread();
+ }
+#endif
+ PL_ProcessPendingEvents(mEventQueue);
+
+ // if we're no longer accepting events and there are still events in the
+ // queue, then process remaining events.
+ if (!mAcceptingEvents && PL_EventAvailable(mEventQueue))
+ PL_ProcessPendingEvents(mEventQueue);
+
+ CheckForDeactivation();
+
+ if (mElderQueue) {
+ nsCOMPtr<nsIEventQueue> elder(do_QueryInterface(mElderQueue));
+ if (elder)
+ elder->ProcessPendingEvents();
+ }
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ --gEventQueueLogPPLevel;
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::EventLoop()
+{
+ PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
+
+ NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
+
+ if (!correctThread)
+ return NS_ERROR_FAILURE;
+
+ PL_EventLoop(mEventQueue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::EventAvailable(PRBool& aResult)
+{
+ aResult = PL_EventAvailable(mEventQueue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::GetEvent(PLEvent** aResult)
+{
+ *aResult = PL_GetEvent(mEventQueue);
+ CheckForDeactivation();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::HandleEvent(PLEvent* aEvent)
+{
+ PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
+ NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
+ if (!correctThread)
+ return NS_ERROR_FAILURE;
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: handle event [queue=%lx, accept=%d, could=%d]",
+ (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
+ ++gEventQueueLogCount;
+#endif
+ PL_HandleEvent(aEvent);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::WaitForEvent(PLEvent** aResult)
+{
+ PRBool correctThread = PL_IsQueueOnCurrentThread(mEventQueue);
+ NS_ASSERTION(correctThread, "attemping to process events on the wrong thread");
+ if (!correctThread)
+ return NS_ERROR_FAILURE;
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: wait for event [queue=%lx, accept=%d, could=%d]",
+ (long)mEventQueue,(int)mAcceptingEvents,(int)mCouldHaveEvents));
+ ++gEventQueueLogCount;
+#endif
+ *aResult = PL_WaitForEvent(mEventQueue);
+ CheckForDeactivation();
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(PRInt32)
+nsEventQueueImpl::GetEventQueueSelectFD()
+{
+ return PL_GetEventQueueSelectFD(mEventQueue);
+}
+
+NS_METHOD
+nsEventQueueImpl::Create(nsISupports *aOuter,
+ REFNSIID aIID,
+ void **aResult)
+{
+ nsEventQueueImpl* evt = new nsEventQueueImpl();
+ if (evt == NULL)
+ return NS_ERROR_OUT_OF_MEMORY;
+ nsresult rv = evt->QueryInterface(aIID, aResult);
+ if (NS_FAILED(rv)) {
+ delete evt;
+ }
+ return rv;
+}
+
+// ---------------- nsPIEventQueueChain -----------------
+
+NS_IMETHODIMP
+nsEventQueueImpl::AppendQueue(nsIEventQueue *aQueue)
+{
+ nsresult rv;
+ nsCOMPtr<nsIEventQueue> end;
+ nsCOMPtr<nsPIEventQueueChain> queueChain(do_QueryInterface(aQueue));
+
+ if (!aQueue)
+ return NS_ERROR_NO_INTERFACE;
+
+/* this would be nice
+ NS_ASSERTION(aQueue->mYoungerQueue == NULL && aQueue->mElderQueue == NULL,
+ "event queue repeatedly appended to queue chain");
+*/
+ rv = NS_ERROR_NO_INTERFACE;
+
+#ifdef NS_DEBUG
+ int depth = 0;
+ nsEventQueueImpl *next = this;
+ while (next && depth < 100) {
+ next = NS_STATIC_CAST(nsEventQueueImpl *, next->mYoungerQueue);
+ ++depth;
+ }
+ if (depth > 5) {
+ char warning[80];
+ PR_snprintf(warning, sizeof(warning),
+ "event queue chain length is %d. this is almost certainly a leak.", depth);
+ NS_WARNING(warning);
+ }
+#endif
+
+ // (be careful doing this outside nsEventQueueService's mEventQMonitor)
+
+ GetYoungest(getter_AddRefs(end));
+ nsCOMPtr<nsPIEventQueueChain> endChain(do_QueryInterface(end));
+ if (endChain) {
+ endChain->SetYounger(queueChain);
+ queueChain->SetElder(endChain);
+ rv = NS_OK;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::Unlink()
+{
+ nsCOMPtr<nsPIEventQueueChain> young = mYoungerQueue,
+ old = mElderQueue;
+
+#if defined(PR_LOGGING) && defined(DEBUG_danm)
+ PR_LOG(gEventQueueLog, PR_LOG_DEBUG,
+ ("EventQueue: unlink [queue=%lx, younger=%lx, elder=%lx]",
+ (long)mEventQueue,(long)mYoungerQueue, (long)mElderQueue.get()));
+ ++gEventQueueLogCount;
+#endif
+
+ // this is probably OK, but shouldn't happen by design, so tell me if it does
+ NS_ASSERTION(!mYoungerQueue, "event queue chain broken in middle");
+
+ // break links early in case the Release cascades back onto us
+ mYoungerQueue = nsnull;
+ mElderQueue = nsnull;
+
+ if (young)
+ young->SetElder(old);
+ if (old) {
+ old->SetYounger(young);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::GetYoungest(nsIEventQueue **aQueue)
+{
+ if (mYoungerQueue)
+ return mYoungerQueue->GetYoungest(aQueue);
+
+ nsIEventQueue *answer = NS_STATIC_CAST(nsIEventQueue *, this);
+ NS_ADDREF(answer);
+ *aQueue = answer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::GetYoungestActive(nsIEventQueue **aQueue)
+{
+ nsCOMPtr<nsIEventQueue> answer;
+
+ if (mYoungerQueue)
+ mYoungerQueue->GetYoungestActive(getter_AddRefs(answer));
+ if (!answer) {
+ if (mAcceptingEvents && mCouldHaveEvents)
+ answer = NS_STATIC_CAST(nsIEventQueue *, this);
+ }
+ *aQueue = answer;
+ NS_IF_ADDREF(*aQueue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::SetYounger(nsPIEventQueueChain *aQueue)
+{
+ mYoungerQueue = aQueue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::SetElder(nsPIEventQueueChain *aQueue)
+{
+ mElderQueue = aQueue;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::GetYounger(nsIEventQueue **aQueue)
+{
+ if (!mYoungerQueue) {
+ *aQueue = nsnull;
+ return NS_OK;
+ }
+ return mYoungerQueue->QueryInterface(NS_GET_IID(nsIEventQueue), (void**)&aQueue);
+}
+
+NS_IMETHODIMP
+nsEventQueueImpl::GetElder(nsIEventQueue **aQueue)
+{
+ if (!mElderQueue) {
+ *aQueue = nsnull;
+ return NS_OK;
+ }
+ return mElderQueue->QueryInterface(NS_GET_IID(nsIEventQueue), (void**)&aQueue);
+}
+