diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/libs/xpcom18a4/xpcom/threads | |
parent | Initial commit. (diff) | |
download | virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs/xpcom18a4/xpcom/threads')
33 files changed, 8602 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/threads/.cvsignore b/src/libs/xpcom18a4/xpcom/threads/.cvsignore new file mode 100644 index 00000000..f3c7a7c5 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/src/libs/xpcom18a4/xpcom/threads/Makefile.in b/src/libs/xpcom18a4/xpcom/threads/Makefile.in new file mode 100644 index 00000000..83433706 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/Makefile.in @@ -0,0 +1,105 @@ +# +# ***** 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.org 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 ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = xpcom +ifeq ($(OS_ARCH),WINNT) +XPIDL_MODULE = xpcom_thread +else +XPIDL_MODULE = xpcom_threads +endif +LIBRARY_NAME = xpcomthreads_s +GRE_MODULE = 1 +REQUIRES = string \ + $(NULL) + + +CSRCS = \ + plevent.c \ + $(NULL) + +CPPSRCS = \ + nsAutoLock.cpp \ + nsEnvironment.cpp \ + nsEventQueue.cpp \ + nsEventQueueService.cpp \ + nsThread.cpp \ + nsTimerImpl.cpp \ + nsProcessCommon.cpp \ + TimerThread.cpp \ + $(NULL) + +EXPORTS = \ + nsAutoLock.h \ + plevent.h \ + nsProcess.h \ + nsEventQueueUtils.h \ + $(NULL) + +XPIDLSRCS = \ + nsIThread.idl \ + nsITimer.idl \ + nsITimerInternal.idl \ + nsITimerManager.idl \ + nsIRunnable.idl \ + nsIEventTarget.idl \ + nsIEventQueue.idl \ + nsIEventQueueService.idl \ + nsIEnvironment.idl \ + nsIProcess.idl \ + $(NULL) + +EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS)) + +LOCAL_INCLUDES = -I$(srcdir)/../components + +# we don't want the shared lib, but we want to force the creation of a static lib. +FORCE_STATIC_LIB = 1 + +# Force use of PIC +FORCE_USE_PIC = 1 + +include $(topsrcdir)/config/rules.mk + +DEFINES += -D_IMPL_NS_COM + diff --git a/src/libs/xpcom18a4/xpcom/threads/TimerThread.cpp b/src/libs/xpcom18a4/xpcom/threads/TimerThread.cpp new file mode 100644 index 00000000..94076892 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/TimerThread.cpp @@ -0,0 +1,462 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter <pavlov@netscape.com> + * + * 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 "nsTimerImpl.h" +#include "TimerThread.h" + +#include "nsAutoLock.h" +#include "pratom.h" + +#include "nsIObserverService.h" +#include "nsIServiceManager.h" + +NS_IMPL_THREADSAFE_ISUPPORTS3(TimerThread, nsIRunnable, nsISupportsWeakReference, nsIObserver) + +TimerThread::TimerThread() : + mInitInProgress(0), + mInitialized(PR_FALSE), + mLock(nsnull), + mCondVar(nsnull), + mShutdown(PR_FALSE), + mWaiting(PR_FALSE), + mSleeping(PR_FALSE), + mDelayLineCounter(0), + mMinTimerPeriod(0), + mTimeoutAdjustment(0) +{ +} + +TimerThread::~TimerThread() +{ + if (mCondVar) + PR_DestroyCondVar(mCondVar); + if (mLock) + PR_DestroyLock(mLock); + + mThread = nsnull; + + PRInt32 n = mTimers.Count(); + while (--n >= 0) { + nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[n]); + NS_RELEASE(timer); + } + + nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1"); + if (observerService) { + observerService->RemoveObserver(this, "sleep_notification"); + observerService->RemoveObserver(this, "wake_notification"); + } + +} + +nsresult +TimerThread::InitLocks() +{ + NS_ASSERTION(!mLock, "InitLocks called twice?"); + mLock = PR_NewLock(); + if (!mLock) + return NS_ERROR_OUT_OF_MEMORY; + + mCondVar = PR_NewCondVar(mLock); + if (!mCondVar) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +nsresult TimerThread::Init() +{ + if (mInitialized) { + if (!mThread) + return NS_ERROR_FAILURE; + + return NS_OK; + } + + if (PR_AtomicSet(&mInitInProgress, 1) == 0) { + nsresult 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 { + observerService->AddObserver(this, "sleep_notification", PR_TRUE); + observerService->AddObserver(this, "wake_notification", PR_TRUE); + } + } + } + + PR_Lock(mLock); + mInitialized = PR_TRUE; + PR_NotifyAllCondVar(mCondVar); + PR_Unlock(mLock); + } + else { + PR_Lock(mLock); + while (!mInitialized) { + PR_WaitCondVar(mCondVar, PR_INTERVAL_NO_TIMEOUT); + } + PR_Unlock(mLock); + } + + if (!mThread) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +nsresult TimerThread::Shutdown() +{ + if (!mThread) + return NS_ERROR_NOT_INITIALIZED; + + { // lock scope + nsAutoLock lock(mLock); + + mShutdown = PR_TRUE; + + // notify the cond var so that Run() can return + if (mCondVar && mWaiting) + PR_NotifyCondVar(mCondVar); + + nsTimerImpl *timer; + for (PRInt32 i = mTimers.Count() - 1; i >= 0; i--) { + timer = NS_STATIC_CAST(nsTimerImpl*, mTimers[i]); + RemoveTimerInternal(timer); + } + } + + mThread->Join(); // wait for the thread to die + return NS_OK; +} + +// Keep track of how early (positive slack) or late (negative slack) timers +// are running, and use the filtered slack number to adaptively estimate how +// early timers should fire to be "on time". +void TimerThread::UpdateFilter(PRUint32 aDelay, PRIntervalTime aTimeout, + PRIntervalTime aNow) +{ + PRInt32 slack = (PRInt32) (aTimeout - aNow); + double smoothSlack = 0; + PRUint32 i, filterLength; + static PRIntervalTime kFilterFeedbackMaxTicks = + PR_MillisecondsToInterval(FILTER_FEEDBACK_MAX); + + if (slack > 0) { + if (slack > (PRInt32)kFilterFeedbackMaxTicks) + slack = kFilterFeedbackMaxTicks; + } else { + if (slack < -(PRInt32)kFilterFeedbackMaxTicks) + slack = -(PRInt32)kFilterFeedbackMaxTicks; + } + mDelayLine[mDelayLineCounter & DELAY_LINE_LENGTH_MASK] = slack; + if (++mDelayLineCounter < DELAY_LINE_LENGTH) { + // Startup mode: accumulate a full delay line before filtering. + PR_ASSERT(mTimeoutAdjustment == 0); + filterLength = 0; + } else { + // Past startup: compute number of filter taps based on mMinTimerPeriod. + if (mMinTimerPeriod == 0) { + mMinTimerPeriod = (aDelay != 0) ? aDelay : 1; + } else if (aDelay != 0 && aDelay < mMinTimerPeriod) { + mMinTimerPeriod = aDelay; + } + + filterLength = (PRUint32) (FILTER_DURATION / mMinTimerPeriod); + if (filterLength > DELAY_LINE_LENGTH) + filterLength = DELAY_LINE_LENGTH; + else if (filterLength < 4) + filterLength = 4; + + for (i = 1; i <= filterLength; i++) + smoothSlack += mDelayLine[(mDelayLineCounter-i) & DELAY_LINE_LENGTH_MASK]; + smoothSlack /= filterLength; + + // XXXbe do we need amplification? hacking a fudge factor, need testing... + mTimeoutAdjustment = (PRInt32) (smoothSlack * 1.5); + } + +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + PR_LOG(gTimerLog, PR_LOG_DEBUG, + ("UpdateFilter: smoothSlack = %g, filterLength = %u\n", + smoothSlack, filterLength)); + } +#endif +} + +/* void Run(); */ +NS_IMETHODIMP TimerThread::Run() +{ + nsAutoLock lock(mLock); + + while (!mShutdown) { + PRIntervalTime waitFor; + + if (mSleeping) { + // Sleep for 0.1 seconds while not firing timers. + waitFor = PR_MillisecondsToInterval(100); + } else { + waitFor = PR_INTERVAL_NO_TIMEOUT; + PRIntervalTime now = PR_IntervalNow(); + nsTimerImpl *timer = nsnull; + + if (mTimers.Count() > 0) { + timer = NS_STATIC_CAST(nsTimerImpl*, mTimers[0]); + + if (!TIMER_LESS_THAN(now, timer->mTimeout + mTimeoutAdjustment)) { + next: + // NB: AddRef before the Release under RemoveTimerInternal to avoid + // mRefCnt passing through zero, in case all other refs than the one + // from mTimers have gone away (the last non-mTimers[i]-ref's Release + // must be racing with us, blocked in gThread->RemoveTimer waiting + // for TimerThread::mLock, under nsTimerImpl::Release. + + NS_ADDREF(timer); + RemoveTimerInternal(timer); + + // We release mLock around the Fire call to avoid deadlock. + lock.unlock(); + +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + PR_LOG(gTimerLog, PR_LOG_DEBUG, + ("Timer thread woke up %dms from when it was supposed to\n", + (now >= timer->mTimeout) + ? PR_IntervalToMilliseconds(now - timer->mTimeout) + : -(PRInt32)PR_IntervalToMilliseconds(timer->mTimeout-now)) + ); + } +#endif + + // We are going to let the call to PostTimerEvent here handle the + // release of the timer so that we don't end up releasing the timer + // on the TimerThread instead of on the thread it targets. + timer->PostTimerEvent(); + timer = nsnull; + + lock.lock(); + if (mShutdown) + break; + + // Update now, as PostTimerEvent plus the locking may have taken a + // tick or two, and we may goto next below. + now = PR_IntervalNow(); + } + } + + if (mTimers.Count() > 0) { + timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[0]); + + PRIntervalTime timeout = timer->mTimeout + mTimeoutAdjustment; + + // Don't wait at all (even for PR_INTERVAL_NO_WAIT) if the next timer + // is due now or overdue. + if (!TIMER_LESS_THAN(now, timeout)) + goto next; + waitFor = timeout - now; + } + +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + if (waitFor == PR_INTERVAL_NO_TIMEOUT) + PR_LOG(gTimerLog, PR_LOG_DEBUG, + ("waiting for PR_INTERVAL_NO_TIMEOUT\n")); + else + PR_LOG(gTimerLog, PR_LOG_DEBUG, + ("waiting for %u\n", PR_IntervalToMilliseconds(waitFor))); + } +#endif + } + + mWaiting = PR_TRUE; + PR_WaitCondVar(mCondVar, waitFor); + mWaiting = PR_FALSE; + } + + return NS_OK; +} + +nsresult TimerThread::AddTimer(nsTimerImpl *aTimer) +{ + nsAutoLock lock(mLock); + + // Add the timer to our list. + PRInt32 i = AddTimerInternal(aTimer); + if (i < 0) + return NS_ERROR_OUT_OF_MEMORY; + + // Awaken the timer thread. + if (mCondVar && mWaiting && i == 0) + PR_NotifyCondVar(mCondVar); + + return NS_OK; +} + +nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer) +{ + nsAutoLock lock(mLock); + + // Our caller has a strong ref to aTimer, so it can't go away here under + // ReleaseTimerInternal. + RemoveTimerInternal(aTimer); + + PRInt32 i = AddTimerInternal(aTimer); + if (i < 0) + return NS_ERROR_OUT_OF_MEMORY; + + // Awaken the timer thread. + if (mCondVar && mWaiting && i == 0) + PR_NotifyCondVar(mCondVar); + + return NS_OK; +} + +nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer) +{ + nsAutoLock lock(mLock); + + // Remove the timer from our array. Tell callers that aTimer was not found + // by returning NS_ERROR_NOT_AVAILABLE. Unlike the TimerDelayChanged case + // immediately above, our caller may be passing a (now-)weak ref in via the + // aTimer param, specifically when nsTimerImpl::Release loses a race with + // TimerThread::Run, must wait for the mLock auto-lock here, and during the + // wait Run drops the only remaining ref to aTimer via RemoveTimerInternal. + + if (!RemoveTimerInternal(aTimer)) + return NS_ERROR_NOT_AVAILABLE; + + // Awaken the timer thread. + if (mCondVar && mWaiting) + PR_NotifyCondVar(mCondVar); + + return NS_OK; +} + +// This function must be called from within a lock +PRInt32 TimerThread::AddTimerInternal(nsTimerImpl *aTimer) +{ + PRIntervalTime now = PR_IntervalNow(); + PRInt32 count = mTimers.Count(); + PRInt32 i = 0; + for (; i < count; i++) { + nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl *, mTimers[i]); + + // Don't break till we have skipped any overdue timers. Do not include + // mTimeoutAdjustment here, because we are really trying to avoid calling + // TIMER_LESS_THAN(t, u), where the t is now + DELAY_INTERVAL_MAX, u is + // now - overdue, and DELAY_INTERVAL_MAX + overdue > DELAY_INTERVAL_LIMIT. + // In other words, we want to use now-based time, now adjusted time, even + // though "overdue" ultimately depends on adjusted time. + + // XXX does this hold for TYPE_REPEATING_PRECISE? /be + + if (TIMER_LESS_THAN(now, timer->mTimeout) && + TIMER_LESS_THAN(aTimer->mTimeout, timer->mTimeout)) { + break; + } + } + + if (!mTimers.InsertElementAt(aTimer, i)) + return -1; + + aTimer->mArmed = PR_TRUE; + NS_ADDREF(aTimer); + return i; +} + +PRBool TimerThread::RemoveTimerInternal(nsTimerImpl *aTimer) +{ + if (!mTimers.RemoveElement(aTimer)) + return PR_FALSE; + + // Order is crucial here -- see nsTimerImpl::Release. + aTimer->mArmed = PR_FALSE; + NS_RELEASE(aTimer); + return PR_TRUE; +} + +void TimerThread::DoBeforeSleep() +{ + mSleeping = PR_TRUE; +} + +void TimerThread::DoAfterSleep() +{ + for (PRInt32 i = 0; i < mTimers.Count(); i ++) { + nsTimerImpl *timer = NS_STATIC_CAST(nsTimerImpl*, mTimers[i]); + // get and set the delay to cause its timeout to be recomputed + PRUint32 delay; + timer->GetDelay(&delay); + timer->SetDelay(delay); + } + + // nuke the stored adjustments, so they get recalibrated + mTimeoutAdjustment = 0; + mDelayLineCounter = 0; + mSleeping = PR_FALSE; +} + + +/* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */ +NS_IMETHODIMP +TimerThread::Observe(nsISupports* /* aSubject */, const char *aTopic, const PRUnichar* /* aData */) +{ + if (strcmp(aTopic, "sleep_notification") == 0) + DoBeforeSleep(); + else if (strcmp(aTopic, "wake_notification") == 0) + DoAfterSleep(); + + return NS_OK; +} diff --git a/src/libs/xpcom18a4/xpcom/threads/TimerThread.h b/src/libs/xpcom18a4/xpcom/threads/TimerThread.h new file mode 100644 index 00000000..fa23cfc9 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/TimerThread.h @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter <pavlov@netscape.com> + * + * 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 ***** */ + +#ifndef TimerThread_h___ +#define TimerThread_h___ + +#include "nsWeakReference.h" + +#include "nsIEventQueueService.h" +#include "nsIObserver.h" +#include "nsIRunnable.h" +#include "nsIThread.h" + +#include "nsTimerImpl.h" + +#include "nsVoidArray.h" + +#include "prcvar.h" +#include "prinrval.h" +#include "prlock.h" + +class TimerThread : public nsSupportsWeakReference, + public nsIRunnable, + public nsIObserver +{ +public: + TimerThread(); + NS_HIDDEN_(nsresult) InitLocks(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + NS_DECL_NSIOBSERVER + + NS_HIDDEN_(nsresult) Init(); + NS_HIDDEN_(nsresult) Shutdown(); + + nsresult AddTimer(nsTimerImpl *aTimer); + nsresult TimerDelayChanged(nsTimerImpl *aTimer); + nsresult RemoveTimer(nsTimerImpl *aTimer); + +#define FILTER_DURATION 1e3 /* one second */ +#define FILTER_FEEDBACK_MAX 100 /* 1/10th of a second */ + + void UpdateFilter(PRUint32 aDelay, PRIntervalTime aTimeout, + PRIntervalTime aNow); + + // For use by nsTimerImpl::Fire() + nsCOMPtr<nsIEventQueueService> mEventQueueService; + + void DoBeforeSleep(); + void DoAfterSleep(); + +private: + ~TimerThread(); + + PRInt32 mInitInProgress; + PRBool mInitialized; + + // These two internal helper methods must be called while mLock is held. + // AddTimerInternal returns the position where the timer was added in the + // list, or -1 if it failed. + PRInt32 AddTimerInternal(nsTimerImpl *aTimer); + PRBool RemoveTimerInternal(nsTimerImpl *aTimer); + + nsCOMPtr<nsIThread> mThread; + PRLock *mLock; + PRCondVar *mCondVar; + + PRPackedBool mShutdown; + PRPackedBool mWaiting; + PRPackedBool mSleeping; + + nsVoidArray mTimers; + +#define DELAY_LINE_LENGTH_LOG2 5 +#define DELAY_LINE_LENGTH_MASK PR_BITMASK(DELAY_LINE_LENGTH_LOG2) +#define DELAY_LINE_LENGTH PR_BIT(DELAY_LINE_LENGTH_LOG2) + + PRInt32 mDelayLine[DELAY_LINE_LENGTH]; + PRUint32 mDelayLineCounter; + PRUint32 mMinTimerPeriod; // milliseconds + PRInt32 mTimeoutAdjustment; +}; + +#endif /* TimerThread_h___ */ diff --git a/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.cpp b/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.cpp new file mode 100644 index 00000000..df89408c --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.cpp @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org 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 "nsAutoLock.h" + +#ifdef DEBUG + +#include "plhash.h" +#include "prprf.h" +#include "prlock.h" +#include "prthread.h" +#include "nsDebug.h" +#include "nsVoidArray.h" + +#ifdef NS_TRACE_MALLOC_XXX +# include <stdio.h> +# include "nsTraceMalloc.h" +#endif + +static PRUintn LockStackTPI = (PRUintn)-1; +static PLHashTable* OrderTable = 0; +static PRLock* OrderTableLock = 0; + +static const char* const LockTypeNames[] = {"Lock", "Monitor", "CMonitor"}; + +struct nsNamedVector : public nsVoidArray { + const char* mName; + +#ifdef NS_TRACE_MALLOC_XXX + // Callsites for the inner locks/monitors stored in our base nsVoidArray. + // This array parallels our base nsVoidArray. + nsVoidArray mInnerSites; +#endif + + nsNamedVector(const char* name = 0, PRUint32 initialSize = 0) + : nsVoidArray(initialSize), + mName(name) + { + } +}; + +static void * PR_CALLBACK +_hash_alloc_table(void *pool, PRSize size) +{ + return operator new(size); +} + +static void PR_CALLBACK +_hash_free_table(void *pool, void *item) +{ + operator delete(item); +} + +static PLHashEntry * PR_CALLBACK +_hash_alloc_entry(void *pool, const void *key) +{ + return new PLHashEntry; +} + +/* + * Because monitors and locks may be associated with an nsAutoLockBase, + * without having had their associated nsNamedVector created explicitly in + * nsAutoMonitor::NewMonitor/DeleteMonitor, we need to provide a freeEntry + * PLHashTable hook, to avoid leaking nsNamedVectors which are replaced by + * nsAutoMonitor::NewMonitor. + * + * There is still a problem with the OrderTable containing orphaned + * nsNamedVector entries, for manually created locks wrapped by nsAutoLocks. + * (there should be no manually created monitors wrapped by nsAutoMonitors: + * you should use nsAutoMonitor::NewMonitor and nsAutoMonitor::DestroyMonitor + * instead of PR_NewMonitor and PR_DestroyMonitor). These lock vectors don't + * strictly leak, as they are killed on shutdown, but there are unnecessary + * named vectors in the hash table that outlive their associated locks. + * + * XXX so we should have nsLock, nsMonitor, etc. and strongly type their + * XXX nsAutoXXX counterparts to take only the non-auto types as inputs + */ +static void PR_CALLBACK +_hash_free_entry(void *pool, PLHashEntry *entry, PRUintn flag) +{ + nsNamedVector* vec = (nsNamedVector*) entry->value; + if (vec) { + entry->value = 0; + delete vec; + } + if (flag == HT_FREE_ENTRY) + delete entry; +} + +static const PLHashAllocOps _hash_alloc_ops = { + _hash_alloc_table, _hash_free_table, + _hash_alloc_entry, _hash_free_entry +}; + +PR_STATIC_CALLBACK(PRIntn) +_purge_one(PLHashEntry* he, PRIntn cnt, void* arg) +{ + nsNamedVector* vec = (nsNamedVector*) he->value; + + if (he->key == arg) + return HT_ENUMERATE_REMOVE; + vec->RemoveElement(arg); + return HT_ENUMERATE_NEXT; +} + +PR_STATIC_CALLBACK(void) +OnMonitorRecycle(void* addr) +{ + PR_Lock(OrderTableLock); + PL_HashTableEnumerateEntries(OrderTable, _purge_one, addr); + PR_Unlock(OrderTableLock); +} + +PR_STATIC_CALLBACK(PLHashNumber) +_hash_pointer(const void* key) +{ + return PLHashNumber(NS_PTR_TO_INT32(key)) >> 2; +} + +// Must be single-threaded here, early in primordial thread. +static void InitAutoLockStatics() +{ + (void) PR_NewThreadPrivateIndex(&LockStackTPI, 0); + OrderTable = PL_NewHashTable(64, _hash_pointer, + PL_CompareValues, PL_CompareValues, + &_hash_alloc_ops, 0); + if (OrderTable && !(OrderTableLock = PR_NewLock())) { + PL_HashTableDestroy(OrderTable); + OrderTable = 0; + } + PR_CSetOnMonitorRecycle(OnMonitorRecycle); +} + +void _FreeAutoLockStatics() +{ + PLHashTable* table = OrderTable; + if (!table) return; + + // Called at shutdown, so we don't need to lock. + PR_CSetOnMonitorRecycle(0); + PR_DestroyLock(OrderTableLock); + OrderTableLock = 0; + PL_HashTableDestroy(table); + OrderTable = 0; +} + +static nsNamedVector* GetVector(PLHashTable* table, const void* key) +{ + PLHashNumber hash = _hash_pointer(key); + PLHashEntry** hep = PL_HashTableRawLookup(table, hash, key); + PLHashEntry* he = *hep; + if (he) + return (nsNamedVector*) he->value; + nsNamedVector* vec = new nsNamedVector(); + if (vec) + PL_HashTableRawAdd(table, hep, hash, key, vec); + return vec; +} + +// We maintain an acyclic graph in OrderTable, so recursion can't diverge. +static PRBool Reachable(PLHashTable* table, const void* goal, const void* start) +{ + PR_ASSERT(goal); + PR_ASSERT(start); + nsNamedVector* vec = GetVector(table, start); + for (PRUint32 i = 0, n = vec->Count(); i < n; i++) { + void* addr = vec->ElementAt(i); + if (addr == goal || Reachable(table, goal, addr)) + return PR_TRUE; + } + return PR_FALSE; +} + +static PRBool WellOrdered(const void* addr1, const void* addr2, + const void *callsite2, PRUint32* index2p, + nsNamedVector** vec1p, nsNamedVector** vec2p) +{ + PRBool rv = PR_TRUE; + PLHashTable* table = OrderTable; + if (!table) return rv; + PR_Lock(OrderTableLock); + + // Check whether we've already asserted (addr1 < addr2). + nsNamedVector* vec1 = GetVector(table, addr1); + if (vec1) { + PRUint32 i, n; + + for (i = 0, n = vec1->Count(); i < n; i++) + if (vec1->ElementAt(i) == addr2) + break; + + if (i == n) { + // Now check for (addr2 < addr1) and return false if so. + nsNamedVector* vec2 = GetVector(table, addr2); + if (vec2) { + for (i = 0, n = vec2->Count(); i < n; i++) { + void* addri = vec2->ElementAt(i); + PR_ASSERT(addri); + if (addri == addr1 || Reachable(table, addr1, addri)) { + *index2p = i; + *vec1p = vec1; + *vec2p = vec2; + rv = PR_FALSE; + break; + } + } + + if (rv) { + // Assert (addr1 < addr2) into the order table. + // XXX fix plvector/nsVector to use const void* + vec1->AppendElement((void*) addr2); +#ifdef NS_TRACE_MALLOC_XXX + vec1->mInnerSites.AppendElement((void*) callsite2); +#endif + } + } + } + } + + PR_Unlock(OrderTableLock); + return rv; +} + +nsAutoLockBase::nsAutoLockBase(void* addr, nsAutoLockType type) +{ + if (LockStackTPI == PRUintn(-1)) + InitAutoLockStatics(); + + nsAutoLockBase* stackTop = + (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI); + if (stackTop) { + if (stackTop->mAddr == addr) { + // Ignore reentry: it's legal for monitors, and NSPR will assert + // if you reenter a PRLock. + } else if (!addr) { + // Ignore null addresses: the caller promises not to use the + // lock at all, and NSPR will assert if you enter it. + } else { + const void* node = +#ifdef NS_TRACE_MALLOC_XXX + NS_GetStackTrace(1) +#else + nsnull +#endif + ; + nsNamedVector* vec1; + nsNamedVector* vec2; + PRUint32 i2; + + if (!WellOrdered(stackTop->mAddr, addr, node, &i2, &vec1, &vec2)) { + char buf[128]; + PR_snprintf(buf, sizeof buf, + "Potential deadlock between %s%s@%p and %s%s@%p", + vec1->mName ? vec1->mName : "", + LockTypeNames[stackTop->mType], + stackTop->mAddr, + vec2->mName ? vec2->mName : "", + LockTypeNames[type], + addr); +#ifdef NS_TRACE_MALLOC_XXX + fprintf(stderr, "\n*** %s\n\nCurrent stack:\n", buf); + NS_DumpStackTrace(node, stderr); + + fputs("\nPrevious stack:\n", stderr); + NS_DumpStackTrace(vec2->mInnerSites.ElementAt(i2), stderr); + putc('\n', stderr); +#endif + NS_ERROR(buf); + } + } + } + + mAddr = addr; + mDown = stackTop; + mType = type; + if (mAddr) + (void) PR_SetThreadPrivate(LockStackTPI, this); +} + +nsAutoLockBase::~nsAutoLockBase() +{ + if (mAddr) + (void) PR_SetThreadPrivate(LockStackTPI, mDown); +} + +void nsAutoLockBase::Show() +{ + if (!mAddr) + return; + nsAutoLockBase* curr = (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI); + nsAutoLockBase* prev = nsnull; + while (curr != mDown) { + prev = curr; + curr = prev->mDown; + } + if (!prev) + PR_SetThreadPrivate(LockStackTPI, this); + else + prev->mDown = this; +} + +void nsAutoLockBase::Hide() +{ + if (!mAddr) + return; + nsAutoLockBase* curr = (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI); + nsAutoLockBase* prev = nsnull; + while (curr != this) { + prev = curr; + curr = prev->mDown; + } + if (!prev) + PR_SetThreadPrivate(LockStackTPI, mDown); + else + prev->mDown = mDown; +} + +#endif /* DEBUG */ + +PRMonitor* nsAutoMonitor::NewMonitor(const char* name) +{ + PRMonitor* mon = PR_NewMonitor(); +#ifdef DEBUG + if (mon && OrderTable) { + nsNamedVector* value = new nsNamedVector(name); + if (value) { + PR_Lock(OrderTableLock); + PL_HashTableAdd(OrderTable, mon, value); + PR_Unlock(OrderTableLock); + } + } +#endif + return mon; +} + +void nsAutoMonitor::DestroyMonitor(PRMonitor* mon) +{ +#ifdef DEBUG + if (OrderTable) + OnMonitorRecycle(mon); +#endif + PR_DestroyMonitor(mon); +} + +void nsAutoMonitor::Enter() +{ +#ifdef DEBUG + if (!mAddr) { + NS_ERROR("It is not legal to enter a null monitor"); + return; + } + nsAutoLockBase* stackTop = + (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI); + NS_ASSERTION(stackTop == mDown, "non-LIFO nsAutoMonitor::Enter"); + mDown = stackTop; + (void) PR_SetThreadPrivate(LockStackTPI, this); +#endif + PR_EnterMonitor(mMonitor); + mLockCount += 1; +} + +void nsAutoMonitor::Exit() +{ +#ifdef DEBUG + if (!mAddr) { + NS_ERROR("It is not legal to exit a null monitor"); + return; + } + (void) PR_SetThreadPrivate(LockStackTPI, mDown); +#endif + PRStatus status = PR_ExitMonitor(mMonitor); + NS_ASSERTION(status == PR_SUCCESS, "PR_ExitMonitor failed"); + mLockCount -= 1; +} + +// XXX we don't worry about cached monitors being destroyed behind our back. +// XXX current NSPR (mozilla/nsprpub/pr/src/threads/prcmon.c) never destroys +// XXX a cached monitor! potential resource pig in conjunction with necko... + +void nsAutoCMonitor::Enter() +{ +#ifdef DEBUG + nsAutoLockBase* stackTop = + (nsAutoLockBase*) PR_GetThreadPrivate(LockStackTPI); + NS_ASSERTION(stackTop == mDown, "non-LIFO nsAutoCMonitor::Enter"); + mDown = stackTop; + (void) PR_SetThreadPrivate(LockStackTPI, this); +#endif + PR_CEnterMonitor(mLockObject); + mLockCount += 1; +} + +void nsAutoCMonitor::Exit() +{ +#ifdef DEBUG + (void) PR_SetThreadPrivate(LockStackTPI, mDown); +#endif + PRStatus status = PR_CExitMonitor(mLockObject); + NS_ASSERTION(status == PR_SUCCESS, "PR_CExitMonitor failed"); + mLockCount -= 1; +} diff --git a/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.h b/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.h new file mode 100644 index 00000000..a82d6ffc --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsAutoLock.h @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org 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 ***** */ + + +/* + A stack-based lock object that makes using PRLock a bit more + convenient. It acquires the monitor when constructed, and releases + it when it goes out of scope. + + For example, + + class Foo { + private: + PRLock* mLock; + + public: + Foo(void) { + mLock = PR_NewLock(); + } + + ~Foo(void) { + PR_DestroyLock(mLock); + } + + void ThreadSafeMethod(void) { + // we're don't hold the lock yet... + + nsAutoLock lock(mLock); + // ...but now we do. + + // we even can do wacky stuff like return from arbitrary places w/o + // worrying about forgetting to release the lock + if (some_weird_condition) + return; + + // otherwise do some other stuff + } + + void ThreadSafeBlockScope(void) { + // we're not in the lock here... + + { + nsAutoLock lock(mLock); + // but we are now, at least until the block scope closes + } + + // ...now we're not in the lock anymore + } + }; + + A similar stack-based locking object is available for PRMonitor. The + major difference is that the PRMonitor must be created and destroyed + via the static methods on nsAutoMonitor. + + For example: + Foo::Foo() { + mMon = nsAutoMonitor::NewMonitor("FooMonitor"); + } + nsresult Foo::MyMethod(...) { + nsAutoMonitor mon(mMon); + ... + // go ahead and do deeply nested returns... + return NS_ERROR_FAILURE; + ... + // or call Wait or Notify... + mon.Wait(); + ... + // cleanup is automatic + } + */ + +#ifndef nsAutoLock_h__ +#define nsAutoLock_h__ + +#include "nscore.h" +#include "prlock.h" +#include "prlog.h" + +/** + * nsAutoLockBase + * This is the base class for the stack-based locking objects. + * Clients of derived classes need not play with this superclass. + **/ +class NS_COM nsAutoLockBase { +protected: + nsAutoLockBase() {} + enum nsAutoLockType {eAutoLock, eAutoMonitor, eAutoCMonitor}; + +#ifdef DEBUG + nsAutoLockBase(void* addr, nsAutoLockType type); + ~nsAutoLockBase(); + + void Show(); + void Hide(); + + void* mAddr; + nsAutoLockBase* mDown; + nsAutoLockType mType; +#else + nsAutoLockBase(void* addr, nsAutoLockType type) {} + ~nsAutoLockBase() {} + + void Show() {} + void Hide() {} +#endif +}; + +/** + * nsAutoLock + * Stack-based locking object for PRLock. + **/ +class NS_COM nsAutoLock : public nsAutoLockBase { +private: + PRLock* mLock; + PRBool mLocked; + + // Not meant to be implemented. This makes it a compiler error to + // construct or assign an nsAutoLock object incorrectly. + nsAutoLock(void); + nsAutoLock(const nsAutoLock& /*aLock*/); + nsAutoLock& operator =(const nsAutoLock& /*aLock*/); + + // Not meant to be implemented. This makes it a compiler error to + // attempt to create an nsAutoLock object on the heap. + static void* operator new(size_t /*size*/) CPP_THROW_NEW; + static void operator delete(void* /*memory*/); + +public: + /** + * Constructor + * The constructor aquires the given lock. The destructor + * releases the lock. + * + * @param aLock A valid PRLock* returned from the NSPR's + * PR_NewLock() function. + **/ + nsAutoLock(PRLock* aLock) + : nsAutoLockBase(aLock, eAutoLock), + mLock(aLock), + mLocked(PR_TRUE) { + PR_ASSERT(mLock); + + // This will assert deep in the bowels of NSPR if you attempt + // to re-enter the lock. + PR_Lock(mLock); + } + + ~nsAutoLock(void) { + if (mLocked) + PR_Unlock(mLock); + } + + /** + * lock + * Client may call this to reaquire the given lock. Take special + * note that attempting to aquire a locked lock will hang or crash. + **/ + void lock() { + Show(); + PR_ASSERT(!mLocked); + PR_Lock(mLock); + mLocked = PR_TRUE; + } + + + /** + * unlock + * Client may call this to release the given lock. Take special + * note unlocking an unlocked lock has undefined results. + **/ + void unlock() { + PR_ASSERT(mLocked); + PR_Unlock(mLock); + mLocked = PR_FALSE; + Hide(); + } +}; + +#include "prcmon.h" +#include "nsError.h" +#include "nsDebug.h" + +class NS_COM nsAutoMonitor : public nsAutoLockBase { +public: + + /** + * NewMonitor + * Allocates a new PRMonitor for use with nsAutoMonitor. + * @param name A (unique /be?) name which can reference this monitor + * @returns nsnull if failure + * A valid PRMonitor* is successful while must be destroyed + * by nsAutoMonitor::DestroyMonitor() + **/ + static PRMonitor* NewMonitor(const char* name); + static void DestroyMonitor(PRMonitor* mon); + + + /** + * Constructor + * The constructor locks the given monitor. During destruction + * the monitor will be unlocked. + * + * @param mon A valid PRMonitor* returned from + * nsAutoMonitor::NewMonitor(). + **/ + nsAutoMonitor(PRMonitor* mon) + : nsAutoLockBase((void*)mon, eAutoMonitor), + mMonitor(mon), mLockCount(0) + { + NS_ASSERTION(mMonitor, "null monitor"); + if (mMonitor) { + PR_EnterMonitor(mMonitor); + mLockCount = 1; + } + } + + ~nsAutoMonitor() { + NS_ASSERTION(mMonitor, "null monitor"); + if (mMonitor && mLockCount) { +#ifdef DEBUG + PRStatus status = +#endif + PR_ExitMonitor(mMonitor); + NS_ASSERTION(status == PR_SUCCESS, "PR_ExitMonitor failed"); + } + } + + /** + * Enter + * Client may call this to reenter the given monitor. + * @see prmon.h + **/ + void Enter(); + + /** + * Exit + * Client may call this to exit the given monitor. + * @see prmon.h + **/ + void Exit(); + + /** + * Wait + * @see prmon.h + **/ + nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) { + return PR_Wait(mMonitor, interval) == PR_SUCCESS + ? NS_OK : NS_ERROR_FAILURE; + } + + /** + * Notify + * @see prmon.h + **/ + nsresult Notify() { + return PR_Notify(mMonitor) == PR_SUCCESS + ? NS_OK : NS_ERROR_FAILURE; + } + + /** + * NotifyAll + * @see prmon.h + **/ + nsresult NotifyAll() { + return PR_NotifyAll(mMonitor) == PR_SUCCESS + ? NS_OK : NS_ERROR_FAILURE; + } + +private: + PRMonitor* mMonitor; + PRInt32 mLockCount; + + // Not meant to be implemented. This makes it a compiler error to + // construct or assign an nsAutoLock object incorrectly. + nsAutoMonitor(void); + nsAutoMonitor(const nsAutoMonitor& /*aMon*/); + nsAutoMonitor& operator =(const nsAutoMonitor& /*aMon*/); + + // Not meant to be implemented. This makes it a compiler error to + // attempt to create an nsAutoLock object on the heap. + static void* operator new(size_t /*size*/) CPP_THROW_NEW; + static void operator delete(void* /*memory*/); +}; + +//////////////////////////////////////////////////////////////////////////////// +// Once again, this time with a cache... +// (Using this avoids the need to allocate a PRMonitor, which may be useful when +// a large number of objects of the same class need associated monitors.) + +#include "prcmon.h" +#include "nsError.h" + +class NS_COM nsAutoCMonitor : public nsAutoLockBase { +public: + nsAutoCMonitor(void* lockObject) + : nsAutoLockBase(lockObject, eAutoCMonitor), + mLockObject(lockObject), mLockCount(0) + { + NS_ASSERTION(lockObject, "null lock object"); + PR_CEnterMonitor(mLockObject); + mLockCount = 1; + } + + ~nsAutoCMonitor() { + if (mLockCount) { +#ifdef DEBUG + PRStatus status = +#endif + PR_CExitMonitor(mLockObject); + NS_ASSERTION(status == PR_SUCCESS, "PR_CExitMonitor failed"); + } + } + + void Enter(); + void Exit(); + + nsresult Wait(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT) { + return PR_CWait(mLockObject, interval) == PR_SUCCESS + ? NS_OK : NS_ERROR_FAILURE; + } + + nsresult Notify() { + return PR_CNotify(mLockObject) == PR_SUCCESS + ? NS_OK : NS_ERROR_FAILURE; + } + + nsresult NotifyAll() { + return PR_CNotifyAll(mLockObject) == PR_SUCCESS + ? NS_OK : NS_ERROR_FAILURE; + } + +private: + void* mLockObject; + PRInt32 mLockCount; + + // Not meant to be implemented. This makes it a compiler error to + // construct or assign an nsAutoLock object incorrectly. + nsAutoCMonitor(void); + nsAutoCMonitor(const nsAutoCMonitor& /*aMon*/); + nsAutoCMonitor& operator =(const nsAutoCMonitor& /*aMon*/); + + // Not meant to be implemented. This makes it a compiler error to + // attempt to create an nsAutoLock object on the heap. + static void* operator new(size_t /*size*/) CPP_THROW_NEW; + static void operator delete(void* /*memory*/); +}; + +#endif // nsAutoLock_h__ + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEnvironment.cpp b/src/libs/xpcom18a4/xpcom/threads/nsEnvironment.cpp new file mode 100644 index 00000000..139b24a6 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsEnvironment.cpp @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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 embedding code. + * + * The Initial Developers of the Original Code are + * Benjamin Smedberg <bsmedberg@covad.net> and + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>. + * + * Portions created by the Initial Developer are Copyright (C) 2003/2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 "nsEnvironment.h" +#include "prenv.h" +#include "prprf.h" +#include "nsAutoLock.h" +#include "nsBaseHashtable.h" +#include "nsHashKeys.h" +#include "nsPromiseFlatString.h" +#include "nsDependentString.h" +#include "nsNativeCharsetUtils.h" + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsEnvironment, nsIEnvironment) + +NS_METHOD +nsEnvironment::Create(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + nsresult rv; + *aResult = nsnull; + + if (aOuter != nsnull) { + return NS_ERROR_NO_AGGREGATION; + } + + nsEnvironment* obj = new nsEnvironment(); + if (!obj) { + return NS_ERROR_OUT_OF_MEMORY; + } + + obj->mLock = PR_NewLock(); + if (!obj->mLock) { + delete obj; + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = obj->QueryInterface(aIID, aResult); + if (NS_FAILED(rv)) { + delete obj; + } + return rv; +} + +nsEnvironment::~nsEnvironment() +{ + if (mLock) + PR_DestroyLock(mLock); +} + +NS_IMETHODIMP +nsEnvironment::Exists(const nsAString& aName, PRBool *aOutValue) +{ + nsCAutoString nativeName; + nsresult rv = NS_CopyUnicodeToNative(aName, nativeName); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString nativeVal; +#if defined(XP_UNIX) + /* For Unix/Linux platforms we follow the Unix definition: + * An environment variable exists when |getenv()| returns a non-NULL value. + * An environment variable does not exist when |getenv()| returns NULL. + */ + const char *value = PR_GetEnv(nativeName.get()); + + *aOutValue = (value)?(PR_TRUE):(PR_FALSE); +#else + /* For non-Unix/Linux platforms we have to fall back to a + * "portable" definition (which is incorrect for Unix/Linux!!!!) + * which simply checks whether the string returned by |Get()| is empty + * or not. + */ + nsAutoString value; + Get(aName, value); + *aOutValue = (value.IsEmpty())?(PR_FALSE):(PR_TRUE); +#endif /* XP_UNIX */ + + return NS_OK; +} + +NS_IMETHODIMP +nsEnvironment::Get(const nsAString& aName, nsAString& aOutValue) +{ + nsCAutoString nativeName; + nsresult rv = NS_CopyUnicodeToNative(aName, nativeName); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString nativeVal; + const char *value = PR_GetEnv(nativeName.get()); + if (value) { + rv = NS_CopyNativeToUnicode(nsDependentCString(value), aOutValue); + } else { + aOutValue.Truncate(); + rv = NS_OK; + } + + return rv; +} + +/* Environment strings must have static duration; We're gonna leak all of this + * at shutdown: this is by design, caused how Unix/Linux implement environment + * vars. + */ + +typedef nsBaseHashtableET<nsCStringHashKey,char*> EnvEntryType; +typedef nsTHashtable<EnvEntryType> EnvHashType; + +static EnvHashType *gEnvHash = nsnull; + +static PRBool +EnsureEnvHash() +{ + if (gEnvHash) + return PR_TRUE; + + gEnvHash = new EnvHashType; + if (!gEnvHash) + return PR_FALSE; + + if(gEnvHash->Init()) + return PR_TRUE; + + delete gEnvHash; + gEnvHash = nsnull; + return PR_FALSE; +} + +NS_IMETHODIMP +nsEnvironment::Set(const nsAString& aName, const nsAString& aValue) +{ + nsCAutoString nativeName; + nsCAutoString nativeVal; + + nsresult rv = NS_CopyUnicodeToNative(aName, nativeName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_CopyUnicodeToNative(aValue, nativeVal); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoLock lock(mLock); // autolock unlocks automagically + + if (!EnsureEnvHash()){ + return NS_ERROR_UNEXPECTED; + } + + EnvEntryType* entry = gEnvHash->PutEntry(nativeName); + if (!entry) { + return NS_ERROR_OUT_OF_MEMORY; + } + + char* newData = PR_smprintf("%s=%s", + nativeName.get(), + nativeVal.get()); + if (!newData) { + return NS_ERROR_OUT_OF_MEMORY; + } + + PR_SetEnv(newData); + if (entry->mData) { + PR_smprintf_free(entry->mData); + } + entry->mData = newData; + return NS_OK; +} + + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEnvironment.h b/src/libs/xpcom18a4/xpcom/threads/nsEnvironment.h new file mode 100644 index 00000000..46e8d66a --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsEnvironment.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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 embedding code. + * + * The Initial Developers of the Original Code are + * Benjamin Smedberg <bsmedberg@covad.net> and + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>. + * + * Portions created by the Initial Developer are Copyright (C) 2003/2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 nsEnvironment_h__ +#define nsEnvironment_h__ + +#include "nsIEnvironment.h" +#include "prlock.h" + +#define NS_ENVIRONMENT_CID \ + { 0X3D68F92UL, 0X9513, 0X4E25, \ + { 0X9B, 0XE9, 0X7C, 0XB2, 0X39, 0X87, 0X41, 0X72 } } +#define NS_ENVIRONMENT_CONTRACTID "@mozilla.org/process/environment;1" + +class nsEnvironment : public nsIEnvironment +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIENVIRONMENT + + static NS_METHOD Create(nsISupports *aOuter, REFNSIID aIID, + void **aResult); + +private: + nsEnvironment() { } + ~nsEnvironment(); + + PRLock *mLock; +}; + +#endif /* !nsEnvironment_h__ */ 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); +} + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.h b/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.h new file mode 100644 index 00000000..51f33e47 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsEventQueue.h @@ -0,0 +1,89 @@ +/* -*- 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): + * Pierre Phaneuf <pp@ludusdesign.com> + * + * 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 "prmon.h" +#include "nsIEventQueue.h" +#include "nsPIEventQueueChain.h" + +class nsEventQueueImpl : public nsIEventQueue, + public nsPIEventQueueChain +{ +public: + nsEventQueueImpl(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIEVENTTARGET + NS_DECL_NSIEVENTQUEUE + + // Helpers + static NS_METHOD Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr); + + static const nsCID& CID() { static nsCID cid = NS_EVENTQUEUE_CID; return cid; } + + // nsPIEventQueueChain interface + NS_IMETHOD AppendQueue(nsIEventQueue *aQueue); + NS_IMETHOD Unlink(); + NS_IMETHOD GetYoungest(nsIEventQueue **aQueue); + NS_IMETHOD GetYoungestActive(nsIEventQueue **aQueue); + NS_IMETHOD SetYounger(nsPIEventQueueChain *aQueue); + NS_IMETHOD GetYounger(nsIEventQueue **aQueue); + NS_IMETHOD SetElder(nsPIEventQueueChain *aQueue); + NS_IMETHOD GetElder(nsIEventQueue **aQueue); + +private: + ~nsEventQueueImpl(); + + PLEventQueue *mEventQueue; + PRBool mAcceptingEvents, // accept new events or pass them on? + mCouldHaveEvents; // accepting new ones, or still have old ones? + nsCOMPtr<nsPIEventQueueChain> mElderQueue; // younger can hold on to elder + nsPIEventQueueChain *mYoungerQueue; // but elder can't hold on to younger + + void NotifyObservers(const char *aTopic); + + void CheckForDeactivation() { + if (mCouldHaveEvents && !mAcceptingEvents && !PL_EventAvailable(mEventQueue)) { + if (PL_IsQueueOnCurrentThread(mEventQueue)) { + mCouldHaveEvents = PR_FALSE; + NS_RELEASE_THIS(); // balance ADDREF from the constructor + } else + NS_ERROR("CheckForDeactivation called from wrong thread!"); + } + } +}; + 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; +} + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.h b/src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.h new file mode 100644 index 00000000..6fddb941 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsEventQueueService.h @@ -0,0 +1,77 @@ +/* -*- 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 ***** */ + +#ifndef nsEventQueueService_h__ +#define nsEventQueueService_h__ + +#include "nsIEventQueueService.h" +#include "nsInterfaceHashtable.h" +#include "nsHashKeys.h" +#include "nsIEventQueue.h" + +//////////////////////////////////////////////////////////////////////////////// + +class nsEventQueueServiceImpl : public nsIEventQueueService +{ +public: + nsEventQueueServiceImpl(); + + nsresult Init(); + + // nsISupports interface... + NS_DECL_ISUPPORTS + + // nsIEventQueueService interface... + NS_DECL_NSIEVENTQUEUESERVICE + +private: + ~nsEventQueueServiceImpl(); + + /* Create a queue for the given thread if one does not exist. + Addref the descriptor in any case. parameter aNative is + ignored if the queue already exists. */ + NS_IMETHOD CreateEventQueue(PRThread *aThread, PRBool aNative); + NS_IMETHOD MakeNewQueue(PRThread* thread, PRBool aNative, nsIEventQueue **aQueue); + inline nsresult GetYoungestEventQueue(nsIEventQueue *queue, nsIEventQueue **aResult); + + nsInterfaceHashtable<nsVoidPtrHashKey, nsIEventQueue> mEventQTable; + PRMonitor *mEventQMonitor; +}; + +//////////////////////////////////////////////////////////////////////////////// + +#endif // nsEventQueueService_h__ diff --git a/src/libs/xpcom18a4/xpcom/threads/nsEventQueueUtils.h b/src/libs/xpcom18a4/xpcom/threads/nsEventQueueUtils.h new file mode 100644 index 00000000..5df2e406 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsEventQueueUtils.h @@ -0,0 +1,78 @@ +/* ***** 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) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Darin Fisher <darin@netscape.com> + * + * 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 nsEventQueueUtils_h__ +#define nsEventQueueUtils_h__ + +#include "nsIEventQueueService.h" +#include "nsIServiceManager.h" +#include "nsCOMPtr.h" + +inline nsresult +NS_GetEventQueueService(nsIEventQueueService **result) +{ + static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); + return CallGetService(kEventQueueServiceCID, result); +} + +inline nsresult +NS_GetCurrentEventQ(nsIEventQueue **result, + nsIEventQueueService *serv = nsnull) +{ + nsCOMPtr<nsIEventQueueService> eqs; + if (!serv) { + nsresult rv = NS_GetEventQueueService(getter_AddRefs(eqs)); + if (NS_FAILED(rv)) return rv; + serv = eqs; + } + return serv->GetThreadEventQueue(NS_CURRENT_THREAD, result); +} + +inline nsresult +NS_GetMainEventQ(nsIEventQueue **result, + nsIEventQueueService *serv = nsnull) +{ + nsCOMPtr<nsIEventQueueService> eqs; + if (!serv) { + nsresult rv = NS_GetEventQueueService(getter_AddRefs(eqs)); + if (NS_FAILED(rv)) return rv; + serv = eqs; + } + return serv->GetThreadEventQueue(NS_UI_THREAD, result); +} + +#endif // !nsEventQueueUtils_h__ diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIEnvironment.idl b/src/libs/xpcom18a4/xpcom/threads/nsIEnvironment.idl new file mode 100644 index 00000000..95351346 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIEnvironment.idl @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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 embedding code. + * + * The Initial Developers of the Original Code are + * Benjamin Smedberg <bsmedberg@covad.net> and + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>. + * + * Portions created by the Initial Developer are Copyright (C) 2003/2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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" + +/** + * Scriptable access to the current process environment. + * + */ +[scriptable, uuid(101d5941-d820-4e85-a266-9a3469940807)] +interface nsIEnvironment : nsISupports +{ + /** + * Set the value of an environment variable. + * + * @param aName the variable name to set. + * @param aValue the value to set. + */ + void set(in AString aName, in AString aValue); + + /** + * Get the value of an environment variable. + * + * @param aName the variable name to retrieve. + * @return returns the value of the env variable. An empty string + * will be returned when the env variable does not exist or + * when the value itself is an empty string - please use + * |exists()| to probe whether the env variable exists + * or not. + */ + AString get(in AString aName); + + /** + * Check the existence of an environment variable. + * This method checks whether an environment variable is present in + * the environment or not. + * + * - For Unix/Linux platforms we follow the Unix definition: + * An environment variable exists when |getenv()| returns a non-NULL value. + * An environment variable does not exist when |getenv()| returns NULL. + * - For non-Unix/Linux platforms we have to fall back to a + * "portable" definition (which is incorrect for Unix/Linux!!!!) + * which simply checks whether the string returned by |Get()| is empty + * or not. + * + * @param aName the variable name to probe. + * @return if the variable has been set, the value returned is + * PR_TRUE. If the variable was not defined in the + * environment PR_FALSE will be returned. + */ + boolean exists(in AString aName); +}; + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIEventQueue.idl b/src/libs/xpcom18a4/xpcom/threads/nsIEventQueue.idl new file mode 100644 index 00000000..416c80a1 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIEventQueue.idl @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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): + * David Hyatt <hyatt@netscape.com> + * Suresh Duddi <dp@netscape.com> + * Doug Turner <dougt@netscape.com> + * Judson Valeski <valeski@netscape.com> + * Dan Matejka <danm@netscape.com> + * Ray Whitmer <rayw@netscape.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 "nsIEventTarget.idl" + +%{C++ +#include "prthread.h" + +// {13D86C61-00A9-11d3-9F2A-00400553EEF0} +#define NS_EVENTQUEUE_CID \ +{ 0x13d86c61, 0xa9, 0x11d3, { 0x9f, 0x2a, 0x0, 0x40, 0x5, 0x53, 0xee, 0xf0 } } + +#define NS_EVENTQUEUE_CONTRACTID "@mozilla.org/event-queue;1" +#define NS_EVENTQUEUE_CLASSNAME "Event Queue" + +%} + +// some forward decls +// +[ptr] native PLEventQueuePtr(PLEventQueue); +[ptr] native PRThreadPtr(PRThread); +native PRStatus(PRStatus); +[ref] native PRBoolRef(PRBool); +native PLHandleEventProc(PLHandleEventProc); +native PLDestroyEventProc(PLDestroyEventProc); + +[scriptable, uuid(176AFB41-00A4-11d3-9F2A-00400553EEF0)] +interface nsIEventQueue : nsIEventTarget +{ + [noscript] void initEvent(in PLEventPtr aEvent, + in voidPtr owner, + in PLHandleEventProc handler, + in PLDestroyEventProc destructor); + + [noscript] void postSynchronousEvent(in PLEventPtr aEvent, + out voidPtr aResult); + + boolean pendingEvents(); + void processPendingEvents(); + void eventLoop(); + + [noscript] void eventAvailable(in PRBoolRef aResult); + [noscript] PLEventPtr getEvent(); + [noscript] void handleEvent(in PLEventPtr aEvent); + [noscript] PLEventPtr waitForEvent(); + + [notxpcom] PRInt32 getEventQueueSelectFD(); + + void init(in boolean aNative); + [noscript] void initFromPRThread(in PRThreadPtr thread, + in boolean aNative); + [noscript] void initFromPLQueue(in PLEventQueuePtr aQueue); + + void enterMonitor(); + void exitMonitor(); + + [noscript] void revokeEvents(in voidPtr owner); + [noscript] PLEventQueuePtr getPLEventQueue(); + + boolean isQueueNative(); + + // effectively kill the queue. warning: the queue is allowed to delete + // itself any time after this. + void stopAcceptingEvents(); +}; + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIEventQueueService.idl b/src/libs/xpcom18a4/xpcom/threads/nsIEventQueueService.idl new file mode 100644 index 00000000..e9c93e86 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIEventQueueService.idl @@ -0,0 +1,151 @@ +/* -*- 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> + * David Matiskella <davidm@netscape.com> + * David Hyatt <hyatt@netscape.com> + * Suresh Duddi <dp@netscape.com> + * Scott Collins <scc@netscape.com> + * Dan Matejka <danm@netscape.com> + * Doug Turner <dougt@netscape.com> + * Ray Whitmer <rayw@netscape.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 "nsISupports.idl" +#include "nsIEventQueue.idl" + +%{C++ +#include "prthread.h" +#include "plevent.h" + +/* be761f00-a3b0-11d2-996c-0080c7cb1080 */ +#define NS_EVENTQUEUESERVICE_CID \ +{ 0xbe761f00, 0xa3b0, 0x11d2, \ + {0x99, 0x6c, 0x00, 0x80, 0xc7, 0xcb, 0x10, 0x80} } + +#define NS_EVENTQUEUESERVICE_CONTRACTID "@mozilla.org/event-queue-service;1" +#define NS_EVENTQUEUESERVICE_CLASSNAME "Event Queue Service" + +#define NS_CURRENT_THREAD ((PRThread*)0) +#define NS_CURRENT_EVENTQ ((nsIEventQueue*)0) + +#define NS_UI_THREAD ((PRThread*)1) +#define NS_UI_THREAD_EVENTQ ((nsIEventQueue*)1) + +%} + +/* a forward decl */ +interface nsIThread; + +[scriptable, uuid(a6cf90dc-15b3-11d2-932e-00805f8add32)] +interface nsIEventQueueService : nsISupports +{ + /** + * Creates and holds a native event queue for the current thread. + * "Native" queues have an associated callback mechanism which is + * automatically triggered when an event is posted. See plevent.c for + * details. + * @return NS_OK on success, or a host of failure indications + */ + void createThreadEventQueue(); + + /** + * Creates and hold a monitored event queue for the current thread. + * "Monitored" queues have no callback processing mechanism. + * @return NS_OK on success, or a host of failure indications + */ + void createMonitoredThreadEventQueue(); + + /** + * Somewhat misnamed, this method releases the service's hold on the event + * queue(s) for this thread. Subsequent attempts to access this thread's + * queue (GetThreadEventQueue, for example) may fail, though the queue itself + * will be destroyed only after all references to it are released and the + * queue itself is no longer actively processing events. + * @return nonsense. + */ + void destroyThreadEventQueue(); + + nsIEventQueue createFromIThread(in nsIThread aThread, + in boolean aNative); + + [noscript] nsIEventQueue createFromPLEventQueue(in PLEventQueuePtr + aPLEventQueue); + + // Add a new event queue for the current thread, making it the "current" + // queue. Return that queue, addrefed. + nsIEventQueue pushThreadEventQueue(); + + // release and disable the queue + void popThreadEventQueue(in nsIEventQueue aQueue); + + [noscript] nsIEventQueue getThreadEventQueue(in PRThreadPtr aThread); + + /** + * @deprecated in favor of getSpecialEventQueue, since that's + * scriptable and this isn't. + * + * Check for any "magic" event queue constants (NS_CURRENT_EVENTQ, + * NS_UI_THREAD_EVENTQ) and return the real event queue that they + * represent, AddRef()ed. Otherwise, return the event queue passed + * in, AddRef()ed. This is not scriptable because the arguments in + * question may be magic constants rather than real nsIEventQueues. + * + * @arg queueOrConstant either a real event queue or a magic + * constant to be resolved + * + * @return a real event queue, AddRef()ed + */ + [noscript] nsIEventQueue resolveEventQueue(in nsIEventQueue queueOrConstant); + + /** + * Returns the appropriate special event queue, AddRef()ed. Really + * just a scriptable version of ResolveEventQueue. + * + * @arg aQueue Either CURRENT_THREAD_EVENT_QUEUE or + * UI_THREAD_EVENT_QUEUE + * @return The requested nsIEventQueue, AddRef()ed + * @exception NS_ERROR_NULL_POINTER Zero pointer passed in for return value + * @exception NS_ERROR_ILLEGAL_VALUE Bogus constant passed in aQueue + * @exception NS_ERROR_FAILURE Error while calling + * GetThreadEventQueue() + */ + nsIEventQueue getSpecialEventQueue(in long aQueue); + + const long CURRENT_THREAD_EVENT_QUEUE = 0; + const long UI_THREAD_EVENT_QUEUE = 1; + +}; diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIEventTarget.idl b/src/libs/xpcom18a4/xpcom/threads/nsIEventTarget.idl new file mode 100644 index 00000000..3b19d9ca --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIEventTarget.idl @@ -0,0 +1,67 @@ +/* ***** 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 IBM Corporation. + * Portions created by IBM Corporation are Copyright (C) 2003 + * IBM Corporation. All Rights Reserved. + * + * Contributor(s): + * IBM Corp. + * + * 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" + +%{C++ +#include "plevent.h" +%} + +[ptr] native PLEventPtr(PLEvent); + +/** + * nsIEventTarget + * + * This interface is used to dispatch events to a particular thread. In many + * cases the event target also supports nsIEventQueue. + */ +[scriptable, uuid(ea99ad5b-cc67-4efb-97c9-2ef620a59f2a)] +interface nsIEventTarget : nsISupports +{ + /** + * Method for posting an asynchronous event to the event target. If this + * method succeeds, then the event will be dispatched on the target thread. + * + * @param aEvent + * The event to dispatched. + */ + [noscript] void postEvent(in PLEventPtr aEvent); + + /** + * This method returns true if the event target is the current thread. + */ + boolean isOnCurrentThread(); +}; diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIProcess.idl b/src/libs/xpcom18a4/xpcom/threads/nsIProcess.idl new file mode 100644 index 00000000..07769cb7 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIProcess.idl @@ -0,0 +1,32 @@ +#include "nsIFile.idl" +#include "nsISupports.idl" + +[scriptable, uuid(9da0b650-d07e-4617-a18a-250035572ac8)] + +interface nsIProcess : nsISupports +{ + void init(in nsIFile executable); + void initWithPid(in unsigned long pid); + + void kill(); + + /** XXX what charset? **/ + /** Executes the file this object was initialized with + * @param blocking Whether to wait until the process terminates before returning or not + * @param args An array of arguments to pass to the process + * @param count The length of the args array + * @return the PID of the newly spawned process */ + unsigned long run(in boolean blocking, [array, size_is(count)] in string args, in unsigned long count); + + readonly attribute nsIFile location; + readonly attribute unsigned long pid; + readonly attribute string processName; + readonly attribute unsigned long processSignature; + readonly attribute long exitValue; +}; + +%{C++ + +#define NS_PROCESS_CONTRACTID "@mozilla.org/process/util;1" +#define NS_PROCESS_CLASSNAME "Process Specification" +%} diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIRunnable.idl b/src/libs/xpcom18a4/xpcom/threads/nsIRunnable.idl new file mode 100644 index 00000000..1cedda38 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIRunnable.idl @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org 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 "nsISupports.idl" + +[scriptable, uuid(4a2abaf0-6886-11d3-9382-00104ba0fd40)] +interface nsIRunnable : nsISupports +{ + void run(); +}; diff --git a/src/libs/xpcom18a4/xpcom/threads/nsIThread.idl b/src/libs/xpcom18a4/xpcom/threads/nsIThread.idl new file mode 100644 index 00000000..f28d6ec9 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsIThread.idl @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org 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 "nsISupports.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 +{ + // 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; + + 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(); +%} +}; + +%{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); + +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); +%} diff --git a/src/libs/xpcom18a4/xpcom/threads/nsITimer.idl b/src/libs/xpcom18a4/xpcom/threads/nsITimer.idl new file mode 100644 index 00000000..855d538d --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsITimer.idl @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * 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 "nsISupports.idl" + +interface nsIObserver; + +%{C++ +/** + * The signature of the timer callback function passed to initWithCallback. This + * is the function that will get called when the timer expires if the timer is + * initialized via initWithCallback. + * + * @param aTimer the timer which has expired + * @param aClosure opaque parameter passed to initWithCallback + * + * Implementers should return the following: + * + * @return NS_OK + * + */ +class nsITimer; +typedef void (*nsTimerCallbackFunc) (nsITimer *aTimer, void *aClosure); +%} + +native nsTimerCallbackFunc(nsTimerCallbackFunc); + +/** + * The callback interface for timers. + */ +interface nsITimer; + +[scriptable, uuid(a796816d-7d47-4348-9ab8-c7aeb3216a7d)] +interface nsITimerCallback : nsISupports +{ + /** + * @param aTimer the timer which has expired + */ + void notify(in nsITimer timer); +}; + + +/** + * nsITimer instances must be initialized by calling one of the "init" methods + * documented below. You may also re-initialize an existing instance with new + * delay to avoid the overhead of destroying and creating a timer. It is not + * necessary to cancel the timer in that case. + */ +[scriptable, uuid(29ee628e-a3ea-471f-965d-dc9f11d1c183)] +interface nsITimer : nsISupports +{ + /* Timer types */ + + /** + * Type of a timer that fires once only. + */ + const short TYPE_ONE_SHOT = 0; + + /** + * After firing, a TYPE_REPEATING_SLACK timer is stopped and not restarted + * until its callback completes. Specified timer period will be at least + * the time between when processing for last firing the callback completes + * and when the next firing occurs. + * + * This is the preferable repeating type for most situations. + */ + const short TYPE_REPEATING_SLACK = 1; + + /** + * An TYPE_REPEATING_PRECISE repeating timer aims to have constant period + * between firings. The processing time for each timer callback should not + * influence the timer period. However, if the processing for the last + * timer firing could not be completed until just before the next firing + * occurs, then you could have two timer notification routines being + * executed in quick succession. + */ + const short TYPE_REPEATING_PRECISE = 2; + + /** + * Initialize a timer that will fire after the said delay. + * A user must keep a reference to this timer till it is + * is no longer needed or has been cancelled. + * + * @param aObserver the callback object that observes the + * ``timer-callback'' topic with the subject being + * the timer itself when the timer fires: + * + * observe(nsISupports aSubject, => nsITimer + * string aTopic, => ``timer-callback'' + * wstring data => null + * + * @param aDelay delay in milliseconds for timer to fire + * @param aType timer type per TYPE* consts defined above + */ + void init(in nsIObserver aObserver, in unsigned long aDelay, + in unsigned long aType); + + + /** + * Initialize a timer to fire after the given millisecond interval. + * This version takes a function to call and a closure to pass to + * that function. + * + * @param aFunc The function to invoke + * @param aClosure An opaque pointer to pass to that function + * @param aDelay The millisecond interval + * @param aType Timer type per TYPE* consts defined above + */ + [noscript] void initWithFuncCallback(in nsTimerCallbackFunc aCallback, + in voidPtr aClosure, + in unsigned long aDelay, + in unsigned long aType); + + /** + * Initialize a timer to fire after the given millisecond interval. + * This version takes a function to call and a closure to pass to + * that function. + * + * @param aFunc nsITimerCallback interface to call when timer expires + * @param aDelay The millisecond interval + * @param aType Timer type per TYPE* consts defined above + */ + void initWithCallback(in nsITimerCallback aCallback, + in unsigned long aDelay, + in unsigned long aType); + + /** + * Cancel the timer. This method works on all types, not just on repeating + * timers -- you might want to cancel a TYPE_ONE_SHOT timer, and even reuse + * it by re-initializing it (to avoid object destruction and creation costs + * by conserving one timer instance). + */ + void cancel(); + + /** + * The millisecond delay of the timeout + */ + attribute unsigned long delay; + + /** + * The timer type : one shot or repeating + */ + attribute unsigned long type; + + /** + * The opaque pointer pass to initWithCallback. + */ + [noscript] readonly attribute voidPtr closure; +}; + +%{C++ +#define NS_TIMER_CONTRACTID "@mozilla.org/timer;1" +#define NS_TIMER_CALLBACK_TOPIC "timer-callback" +%} + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsITimerInternal.idl b/src/libs/xpcom18a4/xpcom/threads/nsITimerInternal.idl new file mode 100644 index 00000000..c334ae19 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsITimerInternal.idl @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * 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 "nsISupports.idl" + + +[scriptable, uuid(6dd8f185-ceb8-4878-8e38-2d13edc2d079)] +interface nsITimerInternal : nsISupports +{ + attribute boolean idle; +}; + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsITimerManager.idl b/src/libs/xpcom18a4/xpcom/threads/nsITimerManager.idl new file mode 100644 index 00000000..f391d313 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsITimerManager.idl @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter <pavlov@netscape.com> + * + * 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 "nsISupports.idl" + +[scriptable, uuid(8fce8c6a-1dd2-11b2-8352-8cdd2b965efc)] +interface nsITimerManager : nsISupports +{ + /** + * A flag that turns on the use of idle timers on the main thread. + * this should only be called once. + * + * By default, idle timers are off. + * + * One this is set to TRUE, you are expected to call hasIdleTimers/fireNextIdleTimer + * when you have time in your main loop. + */ + attribute boolean useIdleTimers; + + boolean hasIdleTimers(); + + // Fire next idle timers waiting on the current thread + void fireNextIdleTimer(); +}; diff --git a/src/libs/xpcom18a4/xpcom/threads/nsPIEventQueueChain.h b/src/libs/xpcom18a4/xpcom/threads/nsPIEventQueueChain.h new file mode 100644 index 00000000..8d3c0933 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsPIEventQueueChain.h @@ -0,0 +1,93 @@ +/* -*- 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 ***** */ + +#ifndef nsPIEventQueueChain_h__ +#define nsPIEventQueueChain_h__ + +#include "nsISupports.h" + +// {8f310040-82a7-11d3-95bc-0060083a0bcf} +#define NS_IEVENTQUEUECHAIN_IID \ +{ 0x8f310040, 0x82a7, 0x11d3, { 0x95, 0xbc, 0x0, 0x60, 0x8, 0x3a, 0xb, 0xcf } } + +class nsIEventQueue; + +class nsPIEventQueueChain : public nsISupports +{ +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NS_IEVENTQUEUECHAIN_IID); + + /** + * Add the given queue as the new youngest member of our chain. + * It will not be addrefed. + * @param aQueue the queue. must not be null. + * @return error indication + */ + NS_IMETHOD AppendQueue(nsIEventQueue *aQueue) = 0; + + /** + * Remove this element from the chain. + * @return NS_OK + */ + NS_IMETHOD Unlink() = 0; + + /** + * Fetch (and addref) the youngest member of the chain. + * @param *aQueue the youngest queue. aQueue must not be null. + * @return error indication + */ + NS_IMETHOD GetYoungest(nsIEventQueue **aQueue) = 0; + + /** + * Fetch (and addref) the youngest member of the chain which is + * still accepting events, or at least still contains events in need + * of processing. + * @param *aQueue the youngest such queue. aQueue must not be null. + * *aQueue will be returned null, if no such queue is found. + * @return error indication -- can be NS_OK even if *aQueue is 0 + */ + NS_IMETHOD GetYoungestActive(nsIEventQueue **aQueue) = 0; + + NS_IMETHOD SetYounger(nsPIEventQueueChain *aQueue) = 0; + NS_IMETHOD GetYounger(nsIEventQueue **aQueue) = 0; + + NS_IMETHOD SetElder(nsPIEventQueueChain *aQueue) = 0; + NS_IMETHOD GetElder(nsIEventQueue **aQueue) = 0; +}; + +#endif /* nsPIEventQueueChain_h___ */ + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsProcess.h b/src/libs/xpcom18a4/xpcom/threads/nsProcess.h new file mode 100644 index 00000000..f061fe1c --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsProcess.h @@ -0,0 +1,71 @@ +/* -*- 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, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Don Bragg <dbragg@netscape.com> + * + * 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 ***** */ + +#ifndef _nsPROCESSWIN_H_ +#define _nsPROCESSWIN_H_ + +#include "nsIProcess.h" +#include "nsIFile.h" +#include "nsString.h" +#include "prproces.h" + +#define NS_PROCESS_CID \ +{0x7b4eeb20, 0xd781, 0x11d4, \ + {0x8A, 0x83, 0x00, 0x10, 0xa4, 0xe0, 0xc9, 0xca}} + +class nsProcess : public nsIProcess +{ +public: + + NS_DECL_ISUPPORTS + NS_DECL_NSIPROCESS + + nsProcess(); + +private: + ~nsProcess() {} + + nsCOMPtr<nsIFile> mExecutable; + PRInt32 mExitValue; + nsCString mTargetPath; + PRProcess *mProcess; + +}; + +#endif diff --git a/src/libs/xpcom18a4/xpcom/threads/nsProcessCommon.cpp b/src/libs/xpcom18a4/xpcom/threads/nsProcessCommon.cpp new file mode 100644 index 00000000..07087c2f --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsProcessCommon.cpp @@ -0,0 +1,363 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Don Bragg <dbragg@netscape.com> + * + * 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 ***** */ + +/***************************************************************************** + * + * nsProcess is used to execute new processes and specify if you want to + * wait (blocking) or continue (non-blocking). + * + ***************************************************************************** + */ + +#include "nsCOMPtr.h" +#include "nsMemory.h" +#include "nsProcess.h" +#include "prtypes.h" +#include "prio.h" +#include "prenv.h" +#include "nsCRT.h" + +#include <stdlib.h> + +#if defined( XP_WIN ) +#include "prmem.h" +#include "nsString.h" +#include "nsLiteralString.h" +#include "nsReadableUtils.h" +#include <windows.h> +#endif + +//-------------------------------------------------------------------// +// nsIProcess implementation +//-------------------------------------------------------------------// +NS_IMPL_ISUPPORTS1(nsProcess, nsIProcess) + +//Constructor +nsProcess::nsProcess():mExitValue(-1), + mProcess(nsnull) +{ +} + +NS_IMETHODIMP +nsProcess::Init(nsIFile* executable) +{ + PRBool isFile; + + //First make sure the file exists + nsresult rv = executable->IsFile(&isFile); + if (NS_FAILED(rv)) return rv; + if (!isFile) + return NS_ERROR_FAILURE; + + //Store the nsIFile in mExecutable + mExecutable = executable; + //Get the path because it is needed by the NSPR process creation +#ifdef XP_WIN + rv = mExecutable->GetNativeTarget(mTargetPath); + if (NS_FAILED(rv) || mTargetPath.IsEmpty() ) +#endif + rv = mExecutable->GetNativePath(mTargetPath); + + return rv; +} + + +#if defined( XP_WIN ) +static int assembleCmdLine(char *const *argv, char **cmdLine) +{ + char *const *arg; + char *p, *q; + int cmdLineSize; + int numBackslashes; + int i; + int argNeedQuotes; + + /* + * Find out how large the command line buffer should be. + */ + cmdLineSize = 0; + for (arg = argv; *arg; arg++) { + /* + * \ and " need to be escaped by a \. In the worst case, + * every character is a \ or ", so the string of length + * may double. If we quote an argument, that needs two ". + * Finally, we need a space between arguments, and + * a null byte at the end of command line. + */ + cmdLineSize += 2 * strlen(*arg) /* \ and " need to be escaped */ + + 2 /* we quote every argument */ + + 1; /* space in between, or final null */ + } + p = *cmdLine = (char *) PR_MALLOC(cmdLineSize); + if (p == NULL) { + return -1; + } + + for (arg = argv; *arg; arg++) { + /* Add a space to separates the arguments */ + if (arg != argv) { + *p++ = ' '; + } + q = *arg; + numBackslashes = 0; + argNeedQuotes = 0; + + /* If the argument contains white space, it needs to be quoted. */ + if (strpbrk(*arg, " \f\n\r\t\v")) { + argNeedQuotes = 1; + } + + if (argNeedQuotes) { + *p++ = '"'; + } + while (*q) { + if (*q == '\\') { + numBackslashes++; + q++; + } else if (*q == '"') { + if (numBackslashes) { + /* + * Double the backslashes since they are followed + * by a quote + */ + for (i = 0; i < 2 * numBackslashes; i++) { + *p++ = '\\'; + } + numBackslashes = 0; + } + /* To escape the quote */ + *p++ = '\\'; + *p++ = *q++; + } else { + if (numBackslashes) { + /* + * Backslashes are not followed by a quote, so + * don't need to double the backslashes. + */ + for (i = 0; i < numBackslashes; i++) { + *p++ = '\\'; + } + numBackslashes = 0; + } + *p++ = *q++; + } + } + + /* Now we are at the end of this argument */ + if (numBackslashes) { + /* + * Double the backslashes if we have a quote string + * delimiter at the end. + */ + if (argNeedQuotes) { + numBackslashes *= 2; + } + for (i = 0; i < numBackslashes; i++) { + *p++ = '\\'; + } + } + if (argNeedQuotes) { + *p++ = '"'; + } + } + + *p = '\0'; + return 0; +} +#endif + +// XXXldb |args| has the wrong const-ness +NS_IMETHODIMP +nsProcess::Run(PRBool blocking, const char **args, PRUint32 count, PRUint32 *pid) +{ + nsresult rv = NS_OK; + + // make sure that when we allocate we have 1 greater than the + // count since we need to null terminate the list for the argv to + // pass into PR_CreateProcess + char **my_argv = NULL; +#ifdef VBOX + my_argv = (char **)nsMemory::Alloc(sizeof(char *) * (count + 2) ); +#else + my_argv = (char **)malloc(sizeof(char *) * (count + 2) ); +#endif + if (!my_argv) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // copy the args + PRUint32 i; + for (i=0; i < count; i++) { + my_argv[i+1] = NS_CONST_CAST(char*, args[i]); + } + // we need to set argv[0] to the program name. + my_argv[0] = mTargetPath.BeginWriting(); + // null terminate the array + my_argv[count+1] = NULL; + + #if defined(XP_WIN) + STARTUPINFO startupInfo; + PROCESS_INFORMATION procInfo; + BOOL retVal; + char *cmdLine; + + if (assembleCmdLine(my_argv, &cmdLine) == -1) { + nsMemory::Free(my_argv); + return NS_ERROR_FILE_EXECUTION_FAILED; + } + + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + retVal = CreateProcess(NULL, + // NS_CONST_CAST(char*, mTargetPath.get()), + cmdLine, + NULL, /* security attributes for the new + * process */ + NULL, /* security attributes for the primary + * thread in the new process */ + FALSE, /* inherit handles */ + 0, /* creation flags */ + NULL, /* env */ + NULL, /* current drive and directory */ + &startupInfo, + &procInfo + ); + PR_FREEIF( cmdLine ); + if (blocking) { + + // if success, wait for process termination. the early returns and such + // are a bit ugly but preserving the logic of the nspr code I copied to + // minimize our risk abit. + + if ( retVal == TRUE ) { + DWORD dwRetVal; + unsigned long exitCode; + + dwRetVal = WaitForSingleObject(procInfo.hProcess, INFINITE); + if (dwRetVal == WAIT_FAILED) { + nsMemory::Free(my_argv); + return PR_FAILURE; + } + if (GetExitCodeProcess(procInfo.hProcess, &exitCode) == FALSE) { + mExitValue = exitCode; + nsMemory::Free(my_argv); + return PR_FAILURE; + } + mExitValue = exitCode; + CloseHandle(procInfo.hProcess); + } + else + rv = PR_FAILURE; + } + else { + + // map return value into success code + + if ( retVal == TRUE ) + rv = PR_SUCCESS; + else + rv = PR_FAILURE; + } + +#else + if ( blocking ) { + mProcess = PR_CreateProcess(mTargetPath.get(), my_argv, NULL, NULL); + if (mProcess) + rv = PR_WaitProcess(mProcess, &mExitValue); + } + else { + rv = PR_CreateProcessDetached(mTargetPath.get(), my_argv, NULL, NULL); + } +#endif + + // free up our argv + nsMemory::Free(my_argv); + + if (rv != PR_SUCCESS) + return NS_ERROR_FILE_EXECUTION_FAILED; + return NS_OK; +} + +NS_IMETHODIMP nsProcess::InitWithPid(PRUint32 pid) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetLocation(nsIFile** aLocation) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetPid(PRUint32 *aPid) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetProcessName(char** aProcessName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetProcessSignature(PRUint32 *aProcessSignature) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::Kill() +{ + nsresult rv = NS_OK; + if (mProcess) + rv = PR_KillProcess(mProcess); + + return rv; +} + +NS_IMETHODIMP +nsProcess::GetExitValue(PRInt32 *aExitValue) +{ + *aExitValue = mExitValue; + + return NS_OK; +} diff --git a/src/libs/xpcom18a4/xpcom/threads/nsProcessMac.cpp b/src/libs/xpcom18a4/xpcom/threads/nsProcessMac.cpp new file mode 100644 index 00000000..d6411f47 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsProcessMac.cpp @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Don Bragg <dbragg@netscape.com> + * Samir Gehani <sgehani@netscape.com> + * + * 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 ***** */ + +/***************************************************************************** + * + * nsProcess is used to execute new processes and specify if you want to + * wait (blocking) or continue (non-blocking). + * + ***************************************************************************** + */ + +#include "nsCOMPtr.h" +#include "nsMemory.h" + +#include "nsProcess.h" + +#include "prtypes.h" +#include "prio.h" +#include "prenv.h" +#include "nsCRT.h" +#include "prthread.h" + +#include <stdlib.h> +#include <Processes.h> + +#include "nsILocalFileMac.h" + +//-------------------------------------------------------------------// +// nsIProcess implementation +//-------------------------------------------------------------------// +NS_IMPL_ISUPPORTS1(nsProcess, nsIProcess) + +//Constructor +nsProcess::nsProcess() +:mExitValue(-1), + mProcess(nsnull) +{ +} + +NS_IMETHODIMP +nsProcess::Init(nsIFile* executable) +{ + PRBool isFile; + + //First make sure the file exists + nsresult rv = executable->IsFile(&isFile); + if (NS_FAILED(rv)) return rv; + + if (!isFile) + return NS_ERROR_FAILURE; + + mExecutable = executable; + + return NS_OK; +} + + +NS_IMETHODIMP +nsProcess::Run(PRBool blocking, const char **args, PRUint32 count, PRUint32 *pid) +{ + OSErr err = noErr; + LaunchParamBlockRec launchPB; + FSSpec resolvedSpec; + Boolean bDone = false; + ProcessInfoRec info; + + nsCOMPtr<nsILocalFileMac> macExecutable = do_QueryInterface(mExecutable); + macExecutable->GetFSSpec(&resolvedSpec); + + launchPB.launchAppSpec = &resolvedSpec; + launchPB.launchAppParameters = NULL; + launchPB.launchBlockID = extendedBlock; + launchPB.launchEPBLength = extendedBlockLen; + launchPB.launchFileFlags = NULL; + launchPB.launchControlFlags = launchContinue + launchNoFileFlags + launchUseMinimum; + if (!blocking) + launchPB.launchControlFlags += launchDontSwitch; + + err = LaunchApplication(&launchPB); + if (err != noErr) + return NS_ERROR_FAILURE; + + // NOTE: blocking mode assumes you are running on a thread + // other than the UI thread that has teh main event loop + if (blocking) + { + do + { + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = nil; + info.processAppSpec = nil; + err = GetProcessInformation(&launchPB.launchProcessSN, &info); + + if (err == noErr) + { + // still running so sleep some more (200 msecs) + PR_Sleep(200); + } + else + { + // no longer in process manager's internal list: so assume done + bDone = true; + } + } + while (!bDone); + } + + return NS_OK; +} + +NS_IMETHODIMP nsProcess::InitWithPid(PRUint32 pid) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetLocation(nsIFile** aLocation) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetPid(PRUint32 *aPid) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetProcessName(char** aProcessName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetProcessSignature(PRUint32 *aProcessSignature) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::Kill() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsProcess::GetExitValue(PRInt32 *aExitValue) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + diff --git a/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp b/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp new file mode 100644 index 00000000..b4cc15f4 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsThread.cpp @@ -0,0 +1,457 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org 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 "nsThread.h" +#include "prmem.h" +#include "prlog.h" +#include "nsAutoLock.h" + +PRUintn nsThread::kIThreadSelfIndex = 0; +static nsIThread *gMainThread = 0; + +#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; + +#endif /* PR_LOGGING */ + +//////////////////////////////////////////////////////////////////////////////// + +nsThread::nsThread() + : mThread(nsnull), mDead(PR_FALSE), mStartLock(nsnull) +{ +#if defined(PR_LOGGING) + // + // Initialize the global PRLogModule for nsIThread logging + // if necessary... + // + if (nsIThreadLog == nsnull) { + nsIThreadLog = PR_NewLogModule("nsIThread"); + } +#endif /* PR_LOGGING */ + + // 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!"); +} + +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 +} + +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) { + NS_RELEASE_THIS(); // most likely the final release of this thread + return NS_OK; + } + else + return NS_ERROR_FAILURE; +} + +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); + mRunnable = runnable; + + NS_ADDREF_THIS(); // released in nsThread::Exit + if (state == PR_JOINABLE_THREAD) + NS_ADDREF_THIS(); // released in nsThread::Join + mStartLock = PR_NewLock(); + if (mStartLock == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + PR_Lock(mStartLock); + mThread = PR_CreateThread(PR_USER_THREAD, Main, this, + priority, scope, state, stackSize); + PR_Unlock(mStartLock); + PR_LOG(nsIThreadLog, PR_LOG_DEBUG, + ("nsIThread %p created\n", this)); + + if (mThread == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + 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() +{ + PRStatus status; + + if (kIThreadSelfIndex == 0) { + status = PR_NewThreadPrivateIndex(&kIThreadSelfIndex, Exit); + if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + } + + status = PR_SetThreadPrivate(kIThreadSelfIndex, this); + if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + + return NS_OK; +} + +void +nsThread::WaitUntilReadyToStartMain() +{ + PR_Lock(mStartLock); + PR_Unlock(mStartLock); + PR_DestroyLock(mStartLock); + mStartLock = nsnull; +} + +NS_COM nsresult +nsIThread::GetCurrent(nsIThread* *result) +{ + return GetIThread(PR_GetCurrentThread(), result); +} + +NS_COM nsresult +nsIThread::GetIThread(PRThread* prthread, nsIThread* *result) +{ + PRStatus status; + nsThread* thread; + + if (nsThread::kIThreadSelfIndex == 0) { + status = PR_NewThreadPrivateIndex(&nsThread::kIThreadSelfIndex, nsThread::Exit); + if (status != PR_SUCCESS) return NS_ERROR_FAILURE; + } + + 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; +} + +NS_COM nsresult +nsIThread::SetMainThread() +{ + // 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); +} + +NS_COM nsresult +nsIThread::GetMainThread(nsIThread **result) +{ + NS_ASSERTION(result, "bad result pointer"); + if (gMainThread == 0) + return NS_ERROR_FAILURE; + *result = gMainThread; + NS_ADDREF(gMainThread); + return NS_OK; +} + +NS_COM PRBool +nsIThread::IsMainThread() +{ + if (gMainThread == 0) + return PR_TRUE; + + PRThread *theMainThread; + gMainThread->GetPRThread(&theMainThread); + return theMainThread == PR_GetCurrentThread(); +} + +void +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; + } +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/libs/xpcom18a4/xpcom/threads/nsThread.h b/src/libs/xpcom18a4/xpcom/threads/nsThread.h new file mode 100644 index 00000000..e75086a7 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsThread.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** 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.org 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 ***** + * 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 + */ + +#ifndef nsThread_h__ +#define nsThread_h__ + +#include "nsIRunnable.h" +#include "nsIThread.h" +#include "nsCOMPtr.h" + +class nsThread : public nsIThread +{ +public: + NS_DECL_ISUPPORTS + + // nsIThread methods: + NS_DECL_NSITHREAD + + // nsThread methods: + nsThread(); + + nsresult RegisterThreadSelf(); + void SetPRThread(PRThread* thread) { mThread = thread; } + void WaitUntilReadyToStartMain(); + + 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); + +private: + ~nsThread(); + +protected: + PRThread* mThread; + nsCOMPtr<nsIRunnable> mRunnable; + PRBool mDead; + PRLock* mStartLock; +}; + +//////////////////////////////////////////////////////////////////////////////// + +#endif // nsThread_h__ diff --git a/src/libs/xpcom18a4/xpcom/threads/nsTimerImpl.cpp b/src/libs/xpcom18a4/xpcom/threads/nsTimerImpl.cpp new file mode 100644 index 00000000..dc19452f --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsTimerImpl.cpp @@ -0,0 +1,642 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter <pavlov@netscape.com> + * + * 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 "nsTimerImpl.h" +#include "TimerThread.h" +#include "nsAutoLock.h" + +#include "nsVoidArray.h" + +#include "nsIEventQueue.h" + +#include "prmem.h" + +static PRInt32 gGenerator = 0; +static TimerThread* gThread = nsnull; +static PRBool gFireOnIdle = PR_FALSE; +static nsTimerManager* gManager = nsnull; + +#ifdef DEBUG_TIMERS +#include <math.h> + +double nsTimerImpl::sDeltaSumSquared = 0; +double nsTimerImpl::sDeltaSum = 0; +double nsTimerImpl::sDeltaNum = 0; + +static void +myNS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues, + double *meanResult, double *stdDevResult) +{ + double mean = 0.0, var = 0.0, stdDev = 0.0; + if (n > 0.0 && sumOfValues >= 0) { + mean = sumOfValues / n; + double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues); + if (temp < 0.0 || n <= 1) + var = 0.0; + else + var = temp / (n * (n - 1)); + // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this: + stdDev = var != 0.0 ? sqrt(var) : 0.0; + } + *meanResult = mean; + *stdDevResult = stdDev; +} +#endif + +NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsTimerImpl, nsITimer, nsITimerInternal) +NS_IMPL_THREADSAFE_ADDREF(nsTimerImpl) + +NS_IMETHODIMP_(nsrefcnt) nsTimerImpl::Release(void) +{ + nsrefcnt count; + + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); + NS_LOG_RELEASE(this, count, "nsTimerImpl"); + if (count == 0) { + mRefCnt = 1; /* stabilize */ + + /* enable this to find non-threadsafe destructors: */ + /* NS_ASSERT_OWNINGTHREAD(nsTimerImpl); */ + NS_DELETEXPCOM(this); + return 0; + } + + // If only one reference remains, and mArmed is set, then the ref must be + // from the TimerThread::mTimers array, so we Cancel this timer to remove + // the mTimers element, and return 0 if Cancel in fact disarmed the timer. + // + // We use an inlined version of nsTimerImpl::Cancel here to check for the + // NS_ERROR_NOT_AVAILABLE code returned by gThread->RemoveTimer when this + // timer is not found in the mTimers array -- i.e., when the timer was not + // in fact armed once we acquired TimerThread::mLock, in spite of mArmed + // being true here. That can happen if the armed timer is being fired by + // TimerThread::Run as we race and test mArmed just before it is cleared by + // the timer thread. If the RemoveTimer call below doesn't find this timer + // in the mTimers array, then the last ref to this timer is held manually + // and temporarily by the TimerThread, so we should fall through to the + // final return and return 1, not 0. + // + // The original version of this thread-based timer code kept weak refs from + // TimerThread::mTimers, removing this timer's weak ref in the destructor, + // but that leads to double-destructions in the race described above, and + // adding mArmed doesn't help, because destructors can't be deferred, once + // begun. But by combining reference-counting and a specialized Release + // method with "is this timer still in the mTimers array once we acquire + // the TimerThread's lock" testing, we defer destruction until we're sure + // that only one thread has its hot little hands on this timer. + // + // Note that both approaches preclude a timer creator, and everyone else + // except the TimerThread who might have a strong ref, from dropping all + // their strong refs without implicitly canceling the timer. Timers need + // non-mTimers-element strong refs to stay alive. + + if (count == 1 && mArmed) { + mCanceled = PR_TRUE; + + if (NS_SUCCEEDED(gThread->RemoveTimer(this))) + return 0; + } + + return count; +} + +nsTimerImpl::nsTimerImpl() : + mClosure(nsnull), + mCallbackType(CALLBACK_TYPE_UNKNOWN), + mIdle(PR_TRUE), + mFiring(PR_FALSE), + mArmed(PR_FALSE), + mCanceled(PR_FALSE), + mGeneration(0), + mDelay(0), + mTimeout(0) +{ + // XXXbsmedberg: shouldn't this be in Init()? + nsIThread::GetCurrent(getter_AddRefs(mCallingThread)); + + mCallback.c = nsnull; + +#ifdef DEBUG_TIMERS + mStart = 0; + mStart2 = 0; +#endif +} + +nsTimerImpl::~nsTimerImpl() +{ + ReleaseCallback(); +} + +//static +nsresult +nsTimerImpl::Startup() +{ + nsresult rv; + + gThread = new TimerThread(); + if (!gThread) return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(gThread); + rv = gThread->InitLocks(); + + if (NS_FAILED(rv)) { + NS_RELEASE(gThread); + } + + return rv; +} + +void nsTimerImpl::Shutdown() +{ +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + double mean = 0, stddev = 0; + myNS_MeanAndStdDev(sDeltaNum, sDeltaSum, sDeltaSumSquared, &mean, &stddev); + + PR_LOG(gTimerLog, PR_LOG_DEBUG, ("sDeltaNum = %f, sDeltaSum = %f, sDeltaSumSquared = %f\n", sDeltaNum, sDeltaSum, sDeltaSumSquared)); + PR_LOG(gTimerLog, PR_LOG_DEBUG, ("mean: %fms, stddev: %fms\n", mean, stddev)); + } +#endif + + if (!gThread) + return; + + gThread->Shutdown(); + NS_RELEASE(gThread); + + gFireOnIdle = PR_FALSE; +} + + +nsresult nsTimerImpl::InitCommon(PRUint32 aType, PRUint32 aDelay) +{ + nsresult rv; + + NS_ENSURE_TRUE(gThread, NS_ERROR_NOT_INITIALIZED); + + rv = gThread->Init(); + NS_ENSURE_SUCCESS(rv, rv); + + /** + * In case of re-Init, both with and without a preceding Cancel, clear the + * mCanceled flag and assign a new mGeneration. But first, remove any armed + * timer from the timer thread's list. + * + * If we are racing with the timer thread to remove this timer and we lose, + * the RemoveTimer call made here will fail to find this timer in the timer + * thread's list, and will return false harmlessly. We test mArmed here to + * avoid the small overhead in RemoveTimer of locking the timer thread and + * checking its list for this timer. It's safe to test mArmed even though + * it might be cleared on another thread in the next cycle (or even already + * be cleared by another CPU whose store hasn't reached our CPU's cache), + * because RemoveTimer is idempotent. + */ + if (mArmed) + gThread->RemoveTimer(this); + mCanceled = PR_FALSE; + mGeneration = PR_AtomicIncrement(&gGenerator); + + mType = (PRUint8)aType; + SetDelayInternal(aDelay); + + return gThread->AddTimer(this); +} + +NS_IMETHODIMP nsTimerImpl::InitWithFuncCallback(nsTimerCallbackFunc aFunc, + void *aClosure, + PRUint32 aDelay, + PRUint32 aType) +{ + ReleaseCallback(); + mCallbackType = CALLBACK_TYPE_FUNC; + mCallback.c = aFunc; + mClosure = aClosure; + + return InitCommon(aType, aDelay); +} + +NS_IMETHODIMP nsTimerImpl::InitWithCallback(nsITimerCallback *aCallback, + PRUint32 aDelay, + PRUint32 aType) +{ + ReleaseCallback(); + mCallbackType = CALLBACK_TYPE_INTERFACE; + mCallback.i = aCallback; + NS_ADDREF(mCallback.i); + + return InitCommon(aType, aDelay); +} + +NS_IMETHODIMP nsTimerImpl::Init(nsIObserver *aObserver, + PRUint32 aDelay, + PRUint32 aType) +{ + ReleaseCallback(); + mCallbackType = CALLBACK_TYPE_OBSERVER; + mCallback.o = aObserver; + NS_ADDREF(mCallback.o); + + return InitCommon(aType, aDelay); +} + +NS_IMETHODIMP nsTimerImpl::Cancel() +{ + mCanceled = PR_TRUE; + + if (gThread) + gThread->RemoveTimer(this); + + return NS_OK; +} + +NS_IMETHODIMP nsTimerImpl::SetDelay(PRUint32 aDelay) +{ + // If we're already repeating precisely, update mTimeout now so that the + // new delay takes effect in the future. + if (mTimeout != 0 && mType == TYPE_REPEATING_PRECISE) + mTimeout = PR_IntervalNow(); + + SetDelayInternal(aDelay); + + if (!mFiring && gThread) + gThread->TimerDelayChanged(this); + + return NS_OK; +} + +NS_IMETHODIMP nsTimerImpl::GetDelay(PRUint32* aDelay) +{ + *aDelay = mDelay; + return NS_OK; +} + +NS_IMETHODIMP nsTimerImpl::SetType(PRUint32 aType) +{ + mType = (PRUint8)aType; + // XXX if this is called, we should change the actual type.. this could effect + // repeating timers. we need to ensure in Fire() that if mType has changed + // during the callback that we don't end up with the timer in the queue twice. + return NS_OK; +} + +NS_IMETHODIMP nsTimerImpl::GetType(PRUint32* aType) +{ + *aType = mType; + return NS_OK; +} + + +NS_IMETHODIMP nsTimerImpl::GetClosure(void** aClosure) +{ + *aClosure = mClosure; + return NS_OK; +} + + +NS_IMETHODIMP nsTimerImpl::GetIdle(PRBool *aIdle) +{ + *aIdle = mIdle; + return NS_OK; +} + +NS_IMETHODIMP nsTimerImpl::SetIdle(PRBool aIdle) +{ + mIdle = aIdle; + return NS_OK; +} + +void nsTimerImpl::Fire() +{ + if (mCanceled) + return; + + PRIntervalTime now = PR_IntervalNow(); +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + PRIntervalTime a = now - mStart; // actual delay in intervals + PRUint32 b = PR_MillisecondsToInterval(mDelay); // expected delay in intervals + PRUint32 d = PR_IntervalToMilliseconds((a > b) ? a - b : b - a); // delta in ms + sDeltaSum += d; + sDeltaSumSquared += double(d) * double(d); + sDeltaNum++; + + PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] expected delay time %4dms\n", this, mDelay)); + PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] actual delay time %4dms\n", this, PR_IntervalToMilliseconds(a))); + PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] (mType is %d) -------\n", this, mType)); + PR_LOG(gTimerLog, PR_LOG_DEBUG, ("[this=%p] delta %4dms\n", this, (a > b) ? (PRInt32)d : -(PRInt32)d)); + + mStart = mStart2; + mStart2 = 0; + } +#endif + + PRIntervalTime timeout = mTimeout; + if (mType == TYPE_REPEATING_PRECISE) { + // Precise repeating timers advance mTimeout by mDelay without fail before + // calling Fire(). + timeout -= PR_MillisecondsToInterval(mDelay); + } + gThread->UpdateFilter(mDelay, timeout, now); + + mFiring = PR_TRUE; + + switch (mCallbackType) { + case CALLBACK_TYPE_FUNC: + mCallback.c(this, mClosure); + break; + case CALLBACK_TYPE_INTERFACE: + mCallback.i->Notify(this); + break; + case CALLBACK_TYPE_OBSERVER: + mCallback.o->Observe(NS_STATIC_CAST(nsITimer*,this), + NS_TIMER_CALLBACK_TOPIC, + nsnull); + break; + default:; + } + + mFiring = PR_FALSE; + +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + PR_LOG(gTimerLog, PR_LOG_DEBUG, + ("[this=%p] Took %dms to fire timer callback\n", + this, PR_IntervalToMilliseconds(PR_IntervalNow() - now))); + } +#endif + + if (mType == TYPE_REPEATING_SLACK) { + SetDelayInternal(mDelay); // force mTimeout to be recomputed. + if (gThread) + gThread->AddTimer(this); + } +} + + +struct TimerEventType : public PLEvent { + PRInt32 mGeneration; +#ifdef DEBUG_TIMERS + PRIntervalTime mInitTime; +#endif +}; + + +void* handleTimerEvent(TimerEventType* event) +{ + nsTimerImpl* timer = NS_STATIC_CAST(nsTimerImpl*, event->owner); + if (event->mGeneration != timer->GetGeneration()) + return nsnull; + +#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))); + } +#endif + + if (gFireOnIdle) { + PRBool idle = PR_FALSE; + timer->GetIdle(&idle); + if (idle) { + NS_ASSERTION(gManager, "Global Thread Manager is null!"); + if (gManager) + gManager->AddIdleTimer(timer); + return nsnull; + } + } + + timer->Fire(); + + return nsnull; +} + +void destroyTimerEvent(TimerEventType* event) +{ + 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, + (PLHandleEventProc)handleTimerEvent, + (PLDestroyEventProc)destroyTimerEvent); + + // 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; + +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + event->mInitTime = PR_IntervalNow(); + } +#endif + + // If this is a repeating precise timer, we need to calculate the time for + // the next timer to fire before we make the callback. + if (mType == TYPE_REPEATING_PRECISE) { + SetDelayInternal(mDelay); + if (gThread) + 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); +} + +void nsTimerImpl::SetDelayInternal(PRUint32 aDelay) +{ + PRIntervalTime delayInterval = PR_MillisecondsToInterval(aDelay); + if (delayInterval > DELAY_INTERVAL_MAX) { + delayInterval = DELAY_INTERVAL_MAX; + aDelay = PR_IntervalToMilliseconds(delayInterval); + } + + mDelay = aDelay; + + PRIntervalTime now = PR_IntervalNow(); + if (mTimeout == 0 || mType != TYPE_REPEATING_PRECISE) + mTimeout = now; + + mTimeout += delayInterval; + +#ifdef DEBUG_TIMERS + if (PR_LOG_TEST(gTimerLog, PR_LOG_DEBUG)) { + if (mStart == 0) + mStart = now; + else + mStart2 = now; + } +#endif +} + +/** + * Timer Manager code + */ + +NS_IMPL_THREADSAFE_ISUPPORTS1(nsTimerManager, nsITimerManager) + +nsTimerManager::nsTimerManager() +{ + mLock = PR_NewLock(); + gManager = this; +} + +nsTimerManager::~nsTimerManager() +{ + gManager = nsnull; + PR_DestroyLock(mLock); + + nsTimerImpl *theTimer; + PRInt32 count = mIdleTimers.Count(); + + for (PRInt32 i = 0; i < count; i++) { + theTimer = NS_STATIC_CAST(nsTimerImpl*, mIdleTimers[i]); + NS_IF_RELEASE(theTimer); + } +} + +NS_IMETHODIMP nsTimerManager::SetUseIdleTimers(PRBool aUseIdleTimers) +{ + if (aUseIdleTimers == PR_FALSE && gFireOnIdle == PR_TRUE) + return NS_ERROR_FAILURE; + + gFireOnIdle = aUseIdleTimers; + + return NS_OK; +} + +NS_IMETHODIMP nsTimerManager::GetUseIdleTimers(PRBool *aUseIdleTimers) +{ + *aUseIdleTimers = gFireOnIdle; + return NS_OK; +} + +NS_IMETHODIMP nsTimerManager::HasIdleTimers(PRBool *aHasTimers) +{ + nsAutoLock lock (mLock); + PRUint32 count = mIdleTimers.Count(); + *aHasTimers = (count != 0); + return NS_OK; +} + +nsresult nsTimerManager::AddIdleTimer(nsITimer* timer) +{ + if (!timer) + return NS_ERROR_FAILURE; + nsAutoLock lock(mLock); + mIdleTimers.AppendElement(timer); + NS_ADDREF(timer); + return NS_OK; +} + +NS_IMETHODIMP nsTimerManager::FireNextIdleTimer() +{ + if (!gFireOnIdle || !nsIThread::IsMainThread()) { + return NS_OK; + } + + nsTimerImpl *theTimer = nsnull; + + { + nsAutoLock lock (mLock); + PRUint32 count = mIdleTimers.Count(); + + if (count == 0) + return NS_OK; + + theTimer = NS_STATIC_CAST(nsTimerImpl*, mIdleTimers[0]); + mIdleTimers.RemoveElement(theTimer); + } + + theTimer->Fire(); + + NS_RELEASE(theTimer); + + return NS_OK; +} + + +// NOT FOR PUBLIC CONSUMPTION! +nsresult +NS_NewTimer(nsITimer* *aResult, nsTimerCallbackFunc aCallback, void *aClosure, + PRUint32 aDelay, PRUint32 aType) +{ + nsTimerImpl* timer = new nsTimerImpl(); + if (timer == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(timer); + + nsresult rv = timer->InitWithFuncCallback(aCallback, aClosure, + aDelay, aType); + if (NS_FAILED(rv)) { + NS_RELEASE(timer); + return rv; + } + + *aResult = timer; + return NS_OK; +} diff --git a/src/libs/xpcom18a4/xpcom/threads/nsTimerImpl.h b/src/libs/xpcom18a4/xpcom/threads/nsTimerImpl.h new file mode 100644 index 00000000..98596f4e --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/nsTimerImpl.h @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * ***** 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter <pavlov@netscape.com> + * + * 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 ***** */ + +#ifndef nsTimerImpl_h___ +#define nsTimerImpl_h___ + +//#define FORCE_PR_LOG /* Allow logging in the release build */ + +#include "nsITimer.h" +#include "nsVoidArray.h" +#include "nsIThread.h" +#include "nsITimerInternal.h" +#include "nsIObserver.h" + +#include "nsCOMPtr.h" + +#include "prlog.h" + +#if defined(PR_LOGGING) +static PRLogModuleInfo *gTimerLog = PR_NewLogModule("nsTimerImpl"); +#define DEBUG_TIMERS 1 +#else +#undef DEBUG_TIMERS +#endif + +#define NS_TIMER_CLASSNAME "Timer" +#define NS_TIMER_CID \ +{ /* 5ff24248-1dd2-11b2-8427-fbab44f29bc8 */ \ + 0x5ff24248, \ + 0x1dd2, \ + 0x11b2, \ + {0x84, 0x27, 0xfb, 0xab, 0x44, 0xf2, 0x9b, 0xc8} \ +} + +enum { + CALLBACK_TYPE_UNKNOWN = 0, + CALLBACK_TYPE_INTERFACE = 1, + CALLBACK_TYPE_FUNC = 2, + CALLBACK_TYPE_OBSERVER = 3 +}; + +// Two timer deadlines must differ by less than half the PRIntervalTime domain. +#define DELAY_INTERVAL_LIMIT PR_BIT(8 * sizeof(PRIntervalTime) - 1) + +// Maximum possible delay (XXX rework to use ms rather than interval ticks). +#define DELAY_INTERVAL_MAX (DELAY_INTERVAL_LIMIT - 1) + +// Is interval-time t less than u, even if t has wrapped PRIntervalTime? +#define TIMER_LESS_THAN(t, u) ((t) - (u) > DELAY_INTERVAL_LIMIT) + +class nsTimerImpl : public nsITimer, public nsITimerInternal +{ +public: + + nsTimerImpl(); + + static NS_HIDDEN_(nsresult) Startup(); + static NS_HIDDEN_(void) Shutdown(); + + friend class TimerThread; + + void Fire(); + void PostTimerEvent(); + void SetDelayInternal(PRUint32 aDelay); + + NS_DECL_ISUPPORTS + NS_DECL_NSITIMER + NS_DECL_NSITIMERINTERNAL + + PRInt32 GetGeneration() { return mGeneration; } + +private: + ~nsTimerImpl(); + + nsresult InitCommon(PRUint32 aType, PRUint32 aDelay); + + void ReleaseCallback() + { + if (mCallbackType == CALLBACK_TYPE_INTERFACE) + NS_RELEASE(mCallback.i); + else if (mCallbackType == CALLBACK_TYPE_OBSERVER) + NS_RELEASE(mCallback.o); + } + + nsCOMPtr<nsIThread> mCallingThread; + + void * mClosure; + + union { + nsTimerCallbackFunc c; + nsITimerCallback * i; + nsIObserver * o; + } mCallback; + + // These members are set by Init (called from NS_NewTimer) and never reset. + PRUint8 mCallbackType; + PRPackedBool mIdle; + + // These members are set by the initiating thread, when the timer's type is + // changed and during the period where it fires on that thread. + PRUint8 mType; + PRPackedBool mFiring; + + + // Use a PRBool (int) here to isolate loads and stores of these two members + // done on various threads under the protection of TimerThread::mLock, from + // loads and stores done on the initiating/type-changing/timer-firing thread + // to the above PRUint8/PRPackedBool members. + PRBool mArmed; + PRBool mCanceled; + + // The generation number of this timer, re-generated each time the timer is + // initialized so one-shot timers can be canceled and re-initialized by the + // arming thread without any bad race conditions. + PRInt32 mGeneration; + + PRUint32 mDelay; + PRIntervalTime mTimeout; + +#ifdef DEBUG_TIMERS + PRIntervalTime mStart, mStart2; + static double sDeltaSum; + static double sDeltaSumSquared; + static double sDeltaNum; +#endif + +}; + +#define NS_TIMERMANAGER_CONTRACTID "@mozilla.org/timer/manager;1" +#define NS_TIMERMANAGER_CLASSNAME "Timer Manager" +#define NS_TIMERMANAGER_CID \ +{ /* 4fe206fa-1dd2-11b2-8a0a-88bacbecc7d2 */ \ + 0x4fe206fa, \ + 0x1dd2, \ + 0x11b2, \ + {0x8a, 0x0a, 0x88, 0xba, 0xcb, 0xec, 0xc7, 0xd2} \ +} + +#include "nsITimerManager.h" + +class nsTimerManager : nsITimerManager +{ +public: + nsTimerManager(); + + NS_DECL_ISUPPORTS + NS_DECL_NSITIMERMANAGER + + nsresult AddIdleTimer(nsITimer* timer); +private: + ~nsTimerManager(); + + PRLock *mLock; + nsVoidArray mIdleTimers; +}; + + +#endif /* nsTimerImpl_h___ */ diff --git a/src/libs/xpcom18a4/xpcom/threads/plevent.c b/src/libs/xpcom18a4/xpcom/threads/plevent.c new file mode 100644 index 00000000..c5e9040f --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/plevent.c @@ -0,0 +1,1774 @@ +/* -*- Mode: C++; tab-width: 4; 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.org 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 ***** */ + +#if defined(_WIN32) +#include <windows.h> +#endif + +#if defined(XP_OS2) +#define INCL_DOS +#define INCL_DOSERRORS +#define INCL_WIN +#include <os2.h> +#define DefWindowProc WinDefWindowProc +#endif /* XP_OS2 */ + +#include "nspr.h" +#include "plevent.h" + +#if !defined(WIN32) +#include <errno.h> +#include <stddef.h> +#if !defined(XP_OS2) +#include <unistd.h> +#endif /* !XP_OS2 */ +#endif /* !Win32 */ + +#if defined(XP_UNIX) +/* for fcntl */ +#include <sys/types.h> +#include <fcntl.h> +#endif + +#if defined(XP_BEOS) +#include <kernel/OS.h> +#endif + +#if defined(XP_MACOSX) +#if defined(MOZ_WIDGET_COCOA) +#include <CoreFoundation/CoreFoundation.h> +#define MAC_USE_CFRUNLOOPSOURCE +#elif defined(TARGET_CARBON) +/* #include <CarbonEvents.h> */ +/* #define MAC_USE_CARBON_EVENT */ +#include <CoreFoundation/CoreFoundation.h> +#define MAC_USE_CFRUNLOOPSOURCE +#endif +#endif + +#include "private/pprthred.h" + +#if defined(VMS) +/* +** On OpenVMS, XtAppAddInput doesn't want a regular fd, instead it +** wants an event flag. So, we don't create and use a pipe for +** notification of when an event queue has something ready, instead +** we use an event flag. Shouldn't be a problem if we only have +** a few event queues. +*/ +#include <lib$routines.h> +#include <starlet.h> +#include <stsdef.h> +#endif /* VMS */ + +#if defined(_WIN32) +/* Comment out the following USE_TIMER define to prevent + * WIN32 from using a WIN32 native timer for PLEvent notification. + * With USE_TIMER defined we will use a timer when pending input + * or paint events are starved, otherwise it will use a posted + * WM_APP msg for PLEvent notification. + */ +#define USE_TIMER + +/* Threshold defined in milliseconds for determining when the input + * and paint events have been held in the WIN32 msg queue too long + */ +#define INPUT_STARVATION_LIMIT 50 +/* The paint starvation limit is set to the smallest value which + * does not cause performance degradation while running page load tests + */ +#define PAINT_STARVATION_LIMIT 750 +/* The WIN9X paint starvation limit is larger because it was + * determined that the following value was required to prevent performance + * degradation on page load tests for WIN98/95 only. + */ +#define WIN9X_PAINT_STARVATION_LIMIT 3000 + +#define TIMER_ID 0 +/* If _md_PerformanceSetting <=0 then no event starvation otherwise events will be starved */ +static PRInt32 _md_PerformanceSetting = 0; +static PRUint32 _md_StarvationDelay = 0; +static PRUint32 _md_SwitchTime = 0; +#endif + +static PRLogModuleInfo *event_lm = NULL; + +/******************************************************************************* + * Private Stuff + ******************************************************************************/ + +/* +** EventQueueType -- Defines notification type for an event queue +** +*/ +typedef enum { + EventQueueIsNative = 1, + EventQueueIsMonitored = 2 +} EventQueueType; + + +struct PLEventQueue { + const char* name; + PRCList queue; + PRMonitor* monitor; + PRThread* handlerThread; + EventQueueType type; + PRPackedBool processingEvents; + PRPackedBool notified; +#if defined(_WIN32) + PRPackedBool timerSet; +#endif + +#if defined(XP_UNIX) && !defined(XP_MACOSX) +#if defined(VMS) + int efn; +#else + PRInt32 eventPipe[2]; +#endif + PLGetEventIDFunc idFunc; + void* idFuncClosure; +#elif defined(_WIN32) || defined(XP_OS2) + HWND eventReceiverWindow; + PRBool removeMsg; +#elif defined(XP_BEOS) + port_id eventport; +#elif defined(XP_MACOSX) +#if defined(MAC_USE_CFRUNLOOPSOURCE) + CFRunLoopSourceRef mRunLoopSource; + CFRunLoopRef mMainRunLoop; + CFStringRef mRunLoopModeStr; /* vbox */ +#elif defined(MAC_USE_CARBON_EVENT) + EventHandlerUPP eventHandlerUPP; + EventHandlerRef eventHandlerRef; +#endif +#endif +}; + +#define PR_EVENT_PTR(_qp) \ + ((PLEvent*) ((char*) (_qp) - offsetof(PLEvent, link))) + +static PRStatus _pl_SetupNativeNotifier(PLEventQueue* self); +static void _pl_CleanupNativeNotifier(PLEventQueue* self); +static PRStatus _pl_NativeNotify(PLEventQueue* self); +static PRStatus _pl_AcknowledgeNativeNotify(PLEventQueue* self); +static void _md_CreateEventQueue( PLEventQueue *eventQueue ); +static PRInt32 _pl_GetEventCount(PLEventQueue* self); + + +#if defined(_WIN32) || defined(XP_OS2) +#if defined(XP_OS2) +ULONG _pr_PostEventMsgId; +#else +UINT _pr_PostEventMsgId; +#endif /* OS2 */ +static char *_pr_eventWindowClass = "XPCOM:EventWindow"; +#endif /* Win32, OS2 */ + +#if defined(_WIN32) + +static LPCTSTR _md_GetEventQueuePropName() { + static ATOM atom = 0; + if (!atom) { + atom = GlobalAddAtom("XPCOM_EventQueue"); + } + return MAKEINTATOM(atom); +} +#endif + +#if defined(MAC_USE_CARBON_EVENT) +enum { + kEventClassPL = FOUR_CHAR_CODE('PLEC'), + + kEventProcessPLEvents = 1, + + kEventParamPLEventQueue = FOUR_CHAR_CODE('OWNQ') +}; + +static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, void *inCompareData); +#endif + +/******************************************************************************* + * Event Queue Operations + ******************************************************************************/ + +/* +** _pl_CreateEventQueue() -- Create the event queue +** +** +*/ +static PLEventQueue * _pl_CreateEventQueue(const char *name, + PRThread *handlerThread, + EventQueueType qtype) +{ + PRStatus err; + PLEventQueue* self = NULL; + PRMonitor* mon = NULL; + + if (event_lm == NULL) + event_lm = PR_NewLogModule("event"); + + self = PR_NEWZAP(PLEventQueue); + if (self == NULL) return NULL; + + mon = PR_NewNamedMonitor(name); + if (mon == NULL) goto error; + + self->name = name; + self->monitor = mon; + self->handlerThread = handlerThread; + self->processingEvents = PR_FALSE; + self->type = qtype; +#if defined(_WIN32) + self->timerSet = PR_FALSE; +#endif +#if defined(_WIN32) || defined(XP_OS2) + self->removeMsg = PR_TRUE; +#endif + + self->notified = PR_FALSE; + + PR_INIT_CLIST(&self->queue); + if ( qtype == EventQueueIsNative ) { + err = _pl_SetupNativeNotifier(self); + if (err) goto error; + _md_CreateEventQueue( self ); + } + return self; + + error: + if (mon != NULL) + PR_DestroyMonitor(mon); + PR_DELETE(self); + return NULL; +} + +PR_IMPLEMENT(PLEventQueue*) +PL_CreateEventQueue(const char* name, PRThread* handlerThread) +{ + return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative )); +} + +PR_EXTERN(PLEventQueue *) +PL_CreateNativeEventQueue(const char *name, PRThread *handlerThread) +{ + return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsNative )); +} + +PR_EXTERN(PLEventQueue *) +PL_CreateMonitoredEventQueue(const char *name, PRThread *handlerThread) +{ + return( _pl_CreateEventQueue( name, handlerThread, EventQueueIsMonitored )); +} + +PR_IMPLEMENT(PRMonitor*) +PL_GetEventQueueMonitor(PLEventQueue* self) +{ + return self->monitor; +} + +static void PR_CALLBACK +_pl_destroyEvent(PLEvent* event, void* data, PLEventQueue* queue) +{ + PL_DequeueEvent(event, queue); + PL_DestroyEvent(event); +} + +PR_IMPLEMENT(void) +PL_DestroyEventQueue(PLEventQueue* self) +{ + PR_EnterMonitor(self->monitor); + + /* destroy undelivered events */ + PL_MapEvents(self, _pl_destroyEvent, NULL); + + if ( self->type == EventQueueIsNative ) + _pl_CleanupNativeNotifier(self); + + /* destroying the monitor also destroys the name */ + PR_ExitMonitor(self->monitor); + PR_DestroyMonitor(self->monitor); + PR_DELETE(self); + +} + +PR_IMPLEMENT(PRStatus) +PL_PostEvent(PLEventQueue* self, PLEvent* event) +{ + PRStatus err = PR_SUCCESS; + PRMonitor* mon; + + if (self == NULL) + return PR_FAILURE; + + mon = self->monitor; + PR_EnterMonitor(mon); + +#if defined(XP_UNIX) && !defined(XP_MACOSX) + if (self->idFunc && event) + event->id = self->idFunc(self->idFuncClosure); +#endif + + /* insert event into thread's event queue: */ + if (event != NULL) { + PR_APPEND_LINK(&event->link, &self->queue); + } + + if (self->type == EventQueueIsNative && !self->notified) { + err = _pl_NativeNotify(self); + + if (err != PR_SUCCESS) + goto error; + + self->notified = PR_TRUE; + } + + /* + * This may fall on deaf ears if we're really notifying the native + * thread, and no one has called PL_WaitForEvent (or PL_EventLoop): + */ + err = PR_Notify(mon); + +error: + PR_ExitMonitor(mon); + return err; +} + +PR_IMPLEMENT(void*) +PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event) +{ + void* result; + + if (self == NULL) + return NULL; + + PR_ASSERT(event != NULL); + + if (PR_GetCurrentThread() == self->handlerThread) { + /* Handle the case where the thread requesting the event handling + * is also the thread that's supposed to do the handling. */ + result = event->handler(event); + } + else { + int i, entryCount; + + event->lock = PR_NewLock(); + if (!event->lock) { + return NULL; + } + event->condVar = PR_NewCondVar(event->lock); + if(!event->condVar) { + PR_DestroyLock(event->lock); + event->lock = NULL; + return NULL; + } + + PR_Lock(event->lock); + + entryCount = PR_GetMonitorEntryCount(self->monitor); + + event->synchronousResult = (void*)PR_TRUE; + + PL_PostEvent(self, event); + + /* We need temporarily to give up our event queue monitor if + we're holding it, otherwise, the thread we're going to wait + for notification from won't be able to enter it to process + the event. */ + if (entryCount) { + for (i = 0; i < entryCount; i++) + PR_ExitMonitor(self->monitor); + } + + event->handled = PR_FALSE; + + while (!event->handled) { + /* wait for event to be handled or destroyed */ + PR_WaitCondVar(event->condVar, PR_INTERVAL_NO_TIMEOUT); + } + + if (entryCount) { + for (i = 0; i < entryCount; i++) + PR_EnterMonitor(self->monitor); + } + + result = event->synchronousResult; + event->synchronousResult = NULL; + PR_Unlock(event->lock); + } + + /* For synchronous events, they're destroyed here on the caller's + thread before the result is returned. See PL_HandleEvent. */ + PL_DestroyEvent(event); + + return result; +} + +PR_IMPLEMENT(PLEvent*) +PL_GetEvent(PLEventQueue* self) +{ + PLEvent* event = NULL; + PRStatus err = PR_SUCCESS; + + if (self == NULL) + return NULL; + + PR_EnterMonitor(self->monitor); + + if (!PR_CLIST_IS_EMPTY(&self->queue)) { + if ( self->type == EventQueueIsNative && + self->notified && + !self->processingEvents && + 0 == _pl_GetEventCount(self) ) + { + err = _pl_AcknowledgeNativeNotify(self); + self->notified = PR_FALSE; + } + if (err) + goto done; + + /* then grab the event and return it: */ + event = PR_EVENT_PTR(self->queue.next); + PR_REMOVE_AND_INIT_LINK(&event->link); + } + + done: + PR_ExitMonitor(self->monitor); + return event; +} + +PR_IMPLEMENT(PRBool) +PL_EventAvailable(PLEventQueue* self) +{ + PRBool result = PR_FALSE; + + if (self == NULL) + return PR_FALSE; + + PR_EnterMonitor(self->monitor); + + if (!PR_CLIST_IS_EMPTY(&self->queue)) + result = PR_TRUE; + + PR_ExitMonitor(self->monitor); + return result; +} + +PR_IMPLEMENT(void) +PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data) +{ + PRCList* qp; + + if (self == NULL) + return; + + PR_EnterMonitor(self->monitor); + qp = self->queue.next; + while (qp != &self->queue) { + PLEvent* event = PR_EVENT_PTR(qp); + qp = qp->next; + (*fun)(event, data, self); + } + PR_ExitMonitor(self->monitor); +} + +static void PR_CALLBACK +_pl_DestroyEventForOwner(PLEvent* event, void* owner, PLEventQueue* queue) +{ + PR_ASSERT(PR_GetMonitorEntryCount(queue->monitor) > 0); + if (event->owner == owner) { + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ \tdestroying event %0x for owner %0x", event, owner)); + PL_DequeueEvent(event, queue); + + if (event->synchronousResult == (void*)PR_TRUE) { + PR_Lock(event->lock); + event->synchronousResult = NULL; + event->handled = PR_TRUE; + PR_NotifyCondVar(event->condVar); + PR_Unlock(event->lock); + } + else { + PL_DestroyEvent(event); + } + } + else { + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ \tskipping event %0x for owner %0x", event, owner)); + } +} + +PR_IMPLEMENT(void) +PL_RevokeEvents(PLEventQueue* self, void* owner) +{ + if (self == NULL) + return; + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ revoking events for owner %0x", owner)); + + /* + ** First we enter the monitor so that no one else can post any events + ** to the queue: + */ + PR_EnterMonitor(self->monitor); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ owner %0x, entered monitor", owner)); + + /* + ** Discard any pending events for this owner: + */ + PL_MapEvents(self, _pl_DestroyEventForOwner, owner); + +#ifdef DEBUG + { + PRCList* qp = self->queue.next; + while (qp != &self->queue) { + PLEvent* event = PR_EVENT_PTR(qp); + qp = qp->next; + PR_ASSERT(event->owner != owner); + } + } +#endif /* DEBUG */ + + PR_ExitMonitor(self->monitor); + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ revoking events for owner %0x", owner)); +} + +static PRInt32 +_pl_GetEventCount(PLEventQueue* self) +{ + PRCList* node; + PRInt32 count = 0; + + PR_EnterMonitor(self->monitor); + node = PR_LIST_HEAD(&self->queue); + while (node != &self->queue) { + count++; + node = PR_NEXT_LINK(node); + } + PR_ExitMonitor(self->monitor); + + return count; +} + +PR_IMPLEMENT(void) +PL_ProcessPendingEvents(PLEventQueue* self) +{ + PRInt32 count; + + if (self == NULL) + return; + + + PR_EnterMonitor(self->monitor); + + if (self->processingEvents) { + _pl_AcknowledgeNativeNotify(self); + self->notified = PR_FALSE; + PR_ExitMonitor(self->monitor); + return; + } + self->processingEvents = PR_TRUE; + + /* Only process the events that are already in the queue, and + * not any new events that get added. Do this by counting the + * number of events currently in the queue + */ + count = _pl_GetEventCount(self); + PR_ExitMonitor(self->monitor); + + while (count-- > 0) { + PLEvent* event = PL_GetEvent(self); + if (event == NULL) + break; + + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event")); + PL_HandleEvent(event); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event")); + } + + PR_EnterMonitor(self->monitor); + + if (self->type == EventQueueIsNative) { + count = _pl_GetEventCount(self); + + if (count <= 0) { + _pl_AcknowledgeNativeNotify(self); + self->notified = PR_FALSE; + } + else { + _pl_NativeNotify(self); + self->notified = PR_TRUE; + } + + } + self->processingEvents = PR_FALSE; + + PR_ExitMonitor(self->monitor); +} + +/******************************************************************************* + * Event Operations + ******************************************************************************/ + +PR_IMPLEMENT(void) +PL_InitEvent(PLEvent* self, void* owner, + PLHandleEventProc handler, + PLDestroyEventProc destructor) +{ +#ifdef PL_POST_TIMINGS + self->postTime = PR_IntervalNow(); +#endif + PR_INIT_CLIST(&self->link); + self->handler = handler; + self->destructor = destructor; + self->owner = owner; + self->synchronousResult = NULL; + self->handled = PR_FALSE; + self->lock = NULL; + self->condVar = NULL; +#if defined(XP_UNIX) && !defined(XP_MACOSX) + self->id = 0; +#endif +} + +PR_IMPLEMENT(void*) +PL_GetEventOwner(PLEvent* self) +{ + return self->owner; +} + +PR_IMPLEMENT(void) +PL_HandleEvent(PLEvent* self) +{ + void* result; + if (self == NULL) + return; + + /* This event better not be on an event queue anymore. */ + PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link)); + + result = self->handler(self); + if (NULL != self->synchronousResult) { + PR_Lock(self->lock); + self->synchronousResult = result; + self->handled = PR_TRUE; + PR_NotifyCondVar(self->condVar); + PR_Unlock(self->lock); + } + else { + /* For asynchronous events, they're destroyed by the event-handler + thread. See PR_PostSynchronousEvent. */ + PL_DestroyEvent(self); + } +} +#ifdef PL_POST_TIMINGS +static long s_eventCount = 0; +static long s_totalTime = 0; +#endif + +PR_IMPLEMENT(void) +PL_DestroyEvent(PLEvent* self) +{ + if (self == NULL) + return; + + /* This event better not be on an event queue anymore. */ + PR_ASSERT(PR_CLIST_IS_EMPTY(&self->link)); + + if(self->condVar) + PR_DestroyCondVar(self->condVar); + if(self->lock) + PR_DestroyLock(self->lock); + +#ifdef PL_POST_TIMINGS + s_totalTime += PR_IntervalNow() - self->postTime; + s_eventCount++; + printf("$$$ running avg (%d) \n", PR_IntervalToMilliseconds(s_totalTime/s_eventCount)); +#endif + + self->destructor(self); +} + +PR_IMPLEMENT(void) +PL_DequeueEvent(PLEvent* self, PLEventQueue* queue) +{ + if (self == NULL) + return; + + /* Only the owner is allowed to dequeue events because once the + client has put it in the queue, they have no idea whether it's + been processed and destroyed or not. */ + + PR_ASSERT(queue->handlerThread == PR_GetCurrentThread()); + + PR_EnterMonitor(queue->monitor); + + PR_ASSERT(!PR_CLIST_IS_EMPTY(&self->link)); + +#if 0 + /* I do not think that we need to do this anymore. + if we do not acknowledge and this is the only + only event in the queue, any calls to process + the eventQ will be effective noop. + */ + if (queue->type == EventQueueIsNative) + _pl_AcknowledgeNativeNotify(queue); +#endif + + PR_REMOVE_AND_INIT_LINK(&self->link); + + PR_ExitMonitor(queue->monitor); +} + +PR_IMPLEMENT(void) +PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation, + PRUint32 starvationDelay) +{ +#if defined(_WIN32) + + _md_StarvationDelay = starvationDelay; + + if (favorPerformanceOverEventStarvation) { + _md_PerformanceSetting++; + return; + } + + _md_PerformanceSetting--; + + if (_md_PerformanceSetting == 0) { + /* Switched from allowing event starvation to no event starvation so grab + the current time to determine when to actually switch to using timers + instead of posted WM_APP messages. */ + _md_SwitchTime = PR_IntervalToMilliseconds(PR_IntervalNow()); + } + +#endif +} + +/******************************************************************************* + * Pure Event Queues + * + * For when you're only processing PLEvents and there is no native + * select, thread messages, or AppleEvents. + ******************************************************************************/ + +PR_IMPLEMENT(PLEvent*) +PL_WaitForEvent(PLEventQueue* self) +{ + PLEvent* event; + PRMonitor* mon; + + if (self == NULL) + return NULL; + + mon = self->monitor; + PR_EnterMonitor(mon); + + while ((event = PL_GetEvent(self)) == NULL) { + PRStatus err; + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ waiting for event")); + err = PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT); + if ((err == PR_FAILURE) + && (PR_PENDING_INTERRUPT_ERROR == PR_GetError())) break; + } + + PR_ExitMonitor(mon); + return event; +} + +PR_IMPLEMENT(void) +PL_EventLoop(PLEventQueue* self) +{ + if (self == NULL) + return; + + while (PR_TRUE) { + PLEvent* event = PL_WaitForEvent(self); + if (event == NULL) { + /* This can only happen if the current thread is interrupted */ + return; + } + + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event")); + PL_HandleEvent(event); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event")); + } +} + +/******************************************************************************* + * Native Event Queues + * + * For when you need to call select, or WaitNextEvent, and yet also want + * to handle PLEvents. + ******************************************************************************/ + +static PRStatus +_pl_SetupNativeNotifier(PLEventQueue* self) +{ +#if defined(VMS) + unsigned int status; + self->idFunc = 0; + self->idFuncClosure = 0; + status = LIB$GET_EF(&self->efn); + if (!$VMS_STATUS_SUCCESS(status)) + return PR_FAILURE; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ Allocated event flag %d", self->efn)); + return PR_SUCCESS; +#elif defined(XP_UNIX) && !defined(XP_MACOSX) + int err; + int flags; + + self->idFunc = 0; + self->idFuncClosure = 0; + + err = pipe(self->eventPipe); + if (err != 0) { + return PR_FAILURE; + } +#ifdef VBOX + fcntl(self->eventPipe[0], F_SETFD, FD_CLOEXEC); + fcntl(self->eventPipe[1], F_SETFD, FD_CLOEXEC); +#endif + + /* make the pipe nonblocking */ + flags = fcntl(self->eventPipe[0], F_GETFL, 0); + if (flags == -1) { + goto failed; + } + err = fcntl(self->eventPipe[0], F_SETFL, flags | O_NONBLOCK); + if (err == -1) { + goto failed; + } + flags = fcntl(self->eventPipe[1], F_GETFL, 0); + if (flags == -1) { + goto failed; + } + err = fcntl(self->eventPipe[1], F_SETFL, flags | O_NONBLOCK); + if (err == -1) { + goto failed; + } + return PR_SUCCESS; + +failed: + close(self->eventPipe[0]); + close(self->eventPipe[1]); + return PR_FAILURE; +#elif defined(XP_BEOS) + /* hook up to the nsToolkit queue, however the appshell + * isn't necessairly started, so we might have to create + * the queue ourselves + */ + char portname[64]; + char semname[64]; + PR_snprintf(portname, sizeof(portname), "event%lx", + (long unsigned) self->handlerThread); + PR_snprintf(semname, sizeof(semname), "sync%lx", + (long unsigned) self->handlerThread); + + if((self->eventport = find_port(portname)) < 0) + { + /* create port + */ + self->eventport = create_port(500, portname); + + /* We don't use the sem, but it has to be there + */ + create_sem(0, semname); + } + + return PR_SUCCESS; +#else + return PR_SUCCESS; +#endif +} + +static void +_pl_CleanupNativeNotifier(PLEventQueue* self) +{ +#if defined(VMS) + { + unsigned int status; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ Freeing event flag %d", self->efn)); + status = LIB$FREE_EF(&self->efn); + } +#elif defined(XP_UNIX) && !defined(XP_MACOSX) + close(self->eventPipe[0]); + close(self->eventPipe[1]); +#elif defined(_WIN32) + if (self->timerSet) { + KillTimer(self->eventReceiverWindow, TIMER_ID); + self->timerSet = PR_FALSE; + } + RemoveProp(self->eventReceiverWindow, _md_GetEventQueuePropName()); + + /* DestroyWindow doesn't do anything when called from a non ui thread. Since + * self->eventReceiverWindow was created on the ui thread, it must be destroyed + * on the ui thread. + */ + SendMessage(self->eventReceiverWindow, WM_CLOSE, 0, 0); + +#elif defined(XP_OS2) + WinDestroyWindow(self->eventReceiverWindow); +#elif defined(MAC_USE_CFRUNLOOPSOURCE) + + CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, kCFRunLoopCommonModes); + CFRunLoopRemoveSource(self->mMainRunLoop, self->mRunLoopSource, self->mRunLoopModeStr); /* vbox */ + CFRelease(self->mRunLoopSource); + CFRelease(self->mMainRunLoop); + CFRelease(self->mRunLoopModeStr); /* vbox */ + +#elif defined(MAC_USE_CARBON_EVENT) + EventComparatorUPP comparator = NewEventComparatorUPP(_md_CarbonEventComparator); + PR_ASSERT(comparator != NULL); + if (comparator) { + FlushSpecificEventsFromQueue(GetMainEventQueue(), comparator, self); + DisposeEventComparatorUPP(comparator); + } + DisposeEventHandlerUPP(self->eventHandlerUPP); + RemoveEventHandler(self->eventHandlerRef); +#endif +} + +#if defined(_WIN32) + +static PRBool _md_WasInputPending = PR_FALSE; +static PRUint32 _md_InputTime = 0; +static PRBool _md_WasPaintPending = PR_FALSE; +static PRUint32 _md_PaintTime = 0; +/* last mouse location */ +static POINT _md_LastMousePos; + +/******************************************************************************* + * Timer callback function. Timers are used on WIN32 instead of APP events + * when there are pending UI events because APP events can cause the GUI to lockup + * because posted messages are processed before other messages. + ******************************************************************************/ + +static void CALLBACK _md_TimerProc( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime ) +{ + PREventQueue* queue = (PREventQueue *) GetProp(hwnd, _md_GetEventQueuePropName()); + PR_ASSERT(queue != NULL); + + KillTimer(hwnd, TIMER_ID); + queue->timerSet = PR_FALSE; + queue->removeMsg = PR_FALSE; + PL_ProcessPendingEvents( queue ); + queue->removeMsg = PR_TRUE; +} + +static PRBool _md_IsWIN9X = PR_FALSE; +static PRBool _md_IsOSSet = PR_FALSE; + +static void _md_DetermineOSType() +{ + OSVERSIONINFO os; + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&os); + if (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId) { + _md_IsWIN9X = PR_TRUE; + } +} + +static PRUint32 _md_GetPaintStarvationLimit() +{ + if (! _md_IsOSSet) { + _md_DetermineOSType(); + _md_IsOSSet = PR_TRUE; + } + + if (_md_IsWIN9X) { + return WIN9X_PAINT_STARVATION_LIMIT; + } + + return PAINT_STARVATION_LIMIT; +} + + +/* + * Determine if an event is being starved (i.e the starvation limit has + * been exceeded. + * Note: this function uses the current setting and updates the contents + * of the wasPending and lastTime arguments + * + * ispending: PR_TRUE if the event is currently pending + * starvationLimit: Threshold defined in milliseconds for determining when + * the event has been held in the queue too long + * wasPending: PR_TRUE if the last time _md_EventIsStarved was called + * the event was pending. This value is updated within + * this function. + * lastTime: Holds the last time the event was in the queue. + * This value is updated within this function + * returns: PR_TRUE if the event is starved, PR_FALSE otherwise + */ + +static PRBool _md_EventIsStarved(PRBool isPending, PRUint32 starvationLimit, + PRBool *wasPending, PRUint32 *lastTime, + PRUint32 currentTime) +{ + if (*wasPending && isPending) { + /* + * It was pending previously and the event is still + * pending so check to see if the elapsed time is + * over the limit which indicates the event was starved + */ + if ((currentTime - *lastTime) > starvationLimit) { + return PR_TRUE; /* pending and over the limit */ + } + + return PR_FALSE; /* pending but within the limit */ + } + + if (isPending) { + /* + * was_pending must be false so record the current time + * so the elapsed time can be computed the next time this + * function is called + */ + *lastTime = currentTime; + *wasPending = PR_TRUE; + return PR_FALSE; + } + + /* Event is no longer pending */ + *wasPending = PR_FALSE; + return PR_FALSE; +} + +/* Determines if the there is a pending Mouse or input event */ + +static PRBool _md_IsInputPending(WORD qstatus) +{ + /* Return immediately there aren't any pending input or paints. */ + if (qstatus == 0) { + return PR_FALSE; + } + + /* Is there anything other than a QS_MOUSEMOVE pending? */ + if ((qstatus & QS_MOUSEBUTTON) || + (qstatus & QS_KEY) || + (qstatus & QS_HOTKEY)) { + return PR_TRUE; + } + + /* + * Mouse moves need extra processing to determine if the mouse + * pointer actually changed location because Windows automatically + * generates WM_MOVEMOVE events when a new window is created which + * we need to filter out. + */ + if (qstatus & QS_MOUSEMOVE) { + POINT cursorPos; + GetCursorPos(&cursorPos); + if ((_md_LastMousePos.x == cursorPos.x) && + (_md_LastMousePos.y == cursorPos.y)) { + return PR_FALSE; /* This is a fake mouse move */ + } + + /* Real mouse move */ + _md_LastMousePos.x = cursorPos.x; + _md_LastMousePos.y = cursorPos.y; + return PR_TRUE; + } + + return PR_FALSE; +} + +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ +#ifdef USE_TIMER + WORD qstatus; + + PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow()); + + /* Since calls to set the _md_PerformanceSetting can be nested + * only performance setting values <= 0 will potentially trigger + * the use of a timer. + */ + if ((_md_PerformanceSetting <= 0) && + ((now - _md_SwitchTime) > _md_StarvationDelay)) { + SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc); + self->timerSet = PR_TRUE; + _md_WasInputPending = PR_FALSE; + _md_WasPaintPending = PR_FALSE; + return PR_SUCCESS; + } + + qstatus = HIWORD(GetQueueStatus(QS_INPUT | QS_PAINT)); + + /* Check for starved input */ + if (_md_EventIsStarved( _md_IsInputPending(qstatus), + INPUT_STARVATION_LIMIT, + &_md_WasInputPending, + &_md_InputTime, + now )) { + /* + * Use a timer for notification. Timers have the lowest priority. + * They are not processed until all other events have been processed. + * This allows any starved paints and input to be processed. + */ + SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc); + self->timerSet = PR_TRUE; + + /* + * Clear any pending paint. _md_WasInputPending was cleared in + * _md_EventIsStarved. + */ + _md_WasPaintPending = PR_FALSE; + return PR_SUCCESS; + } + + if (_md_EventIsStarved( (qstatus & QS_PAINT), + _md_GetPaintStarvationLimit(), + &_md_WasPaintPending, + &_md_PaintTime, + now) ) { + /* + * Use a timer for notification. Timers have the lowest priority. + * They are not processed until all other events have been processed. + * This allows any starved paints and input to be processed + */ + SetTimer(self->eventReceiverWindow, TIMER_ID, 0 ,_md_TimerProc); + self->timerSet = PR_TRUE; + + /* + * Clear any pending input. _md_WasPaintPending was cleared in + * _md_EventIsStarved. + */ + _md_WasInputPending = PR_FALSE; + return PR_SUCCESS; + } + + /* + * Nothing is being starved so post a message instead of using a timer. + * Posted messages are processed before other messages so they have the + * highest priority. + */ +#endif + PostMessage( self->eventReceiverWindow, _pr_PostEventMsgId, + (WPARAM)0, (LPARAM)self ); + + return PR_SUCCESS; +}/* --- end _pl_NativeNotify() --- */ +#endif + + +#if defined(XP_OS2) +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + BOOL rc = WinPostMsg( self->eventReceiverWindow, _pr_PostEventMsgId, + 0, MPFROMP(self)); + return (rc == TRUE) ? PR_SUCCESS : PR_FAILURE; +}/* --- end _pl_NativeNotify() --- */ +#endif /* XP_OS2 */ + +#if defined(VMS) +/* Just set the event flag */ +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + unsigned int status; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_NativeNotify: self=%p efn=%d", + self, self->efn)); + status = SYS$SETEF(self->efn); + return ($VMS_STATUS_SUCCESS(status)) ? PR_SUCCESS : PR_FAILURE; +}/* --- end _pl_NativeNotify() --- */ +#elif defined(XP_UNIX) && !defined(XP_MACOSX) + +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ +#define NOTIFY_TOKEN 0xFA + PRInt32 count; + unsigned char buf[] = { NOTIFY_TOKEN }; + +# ifdef VBOX + /* Don't write two chars, because we'll only acknowledge one and that'll + cause trouble for anyone selecting/polling on the read descriptor. */ + if (self->notified) + return PR_SUCCESS; +# endif + + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_NativeNotify: self=%p", + self)); + count = write(self->eventPipe[1], buf, 1); + if (count == 1) + return PR_SUCCESS; + if (count == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) + return PR_SUCCESS; + return PR_FAILURE; +}/* --- end _pl_NativeNotify() --- */ +#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */ + +#if defined(XP_BEOS) +struct ThreadInterfaceData +{ + void *data; + int32 sync; +}; + +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ + struct ThreadInterfaceData id; + id.data = self; + id.sync = false; + write_port(self->eventport, 'natv', &id, sizeof(id)); + + return PR_SUCCESS; /* Is this correct? */ +} +#endif /* XP_BEOS */ + +#if defined(XP_MACOSX) +static PRStatus +_pl_NativeNotify(PLEventQueue* self) +{ +#if defined(MAC_USE_CFRUNLOOPSOURCE) + CFRunLoopSourceSignal(self->mRunLoopSource); + CFRunLoopWakeUp(self->mMainRunLoop); +#elif defined(MAC_USE_CARBON_EVENT) + OSErr err; + EventRef newEvent; + if (CreateEvent(NULL, kEventClassPL, kEventProcessPLEvents, + 0, kEventAttributeNone, &newEvent) != noErr) + return PR_FAILURE; + err = SetEventParameter(newEvent, kEventParamPLEventQueue, + typeUInt32, sizeof(PREventQueue*), &self); + if (err == noErr) { + err = PostEventToQueue(GetMainEventQueue(), newEvent, kEventPriorityLow); + ReleaseEvent(newEvent); + } + if (err != noErr) + return PR_FAILURE; +#endif + return PR_SUCCESS; +} +#endif /* defined(XP_MACOSX) */ + +static PRStatus +_pl_AcknowledgeNativeNotify(PLEventQueue* self) +{ +#if defined(_WIN32) || defined(XP_OS2) +#ifdef XP_OS2 + QMSG aMsg; +#else + MSG aMsg; +#endif + /* + * only remove msg when we've been called directly by + * PL_ProcessPendingEvents, not when we've been called by + * the window proc because the window proc will remove the + * msg for us. + */ + if (self->removeMsg) { + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_AcknowledgeNativeNotify: self=%p", self)); +#ifdef XP_OS2 + WinPeekMsg((HAB)0, &aMsg, self->eventReceiverWindow, + _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE); +#else + PeekMessage(&aMsg, self->eventReceiverWindow, + _pr_PostEventMsgId, _pr_PostEventMsgId, PM_REMOVE); + if (self->timerSet) { + KillTimer(self->eventReceiverWindow, TIMER_ID); + self->timerSet = PR_FALSE; + } +#endif + } + return PR_SUCCESS; +#elif defined(VMS) + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_AcknowledgeNativeNotify: self=%p efn=%d", + self, self->efn)); + /* + ** If this is the last entry, then clear the event flag. Also make sure + ** the flag is cleared on any spurious wakeups. + */ + sys$clref(self->efn); + return PR_SUCCESS; +#elif defined(XP_UNIX) && !defined(XP_MACOSX) + + PRInt32 count; + unsigned char c; + PR_LOG(event_lm, PR_LOG_DEBUG, + ("_pl_AcknowledgeNativeNotify: self=%p", + self)); + /* consume the byte NativeNotify put in our pipe: */ + count = read(self->eventPipe[0], &c, 1); + if ((count == 1) && (c == NOTIFY_TOKEN)) + return PR_SUCCESS; + if ((count == -1) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) + return PR_SUCCESS; + return PR_FAILURE; +#elif defined(MAC_USE_CFRUNLOOPSOURCE) /* vbox */ + /* vbox */ + CFRunLoopRunInMode(self->mRunLoopModeStr, 0.0, 1); /* vbox */ + return PR_SUCCESS; /* vbox */ +#else + + /* nothing to do on the other platforms */ + return PR_SUCCESS; +#endif +} + +PR_IMPLEMENT(PRInt32) +PL_GetEventQueueSelectFD(PLEventQueue* self) +{ + if (self == NULL) + return -1; + +#if defined(VMS) + return -(self->efn); +#elif defined(XP_UNIX) && !defined(XP_MACOSX) + return self->eventPipe[0]; +#else + return -1; /* other platforms don't handle this (yet) */ +#endif +} + +PR_IMPLEMENT(PRBool) +PL_IsQueueOnCurrentThread( PLEventQueue *queue ) +{ + PRThread *me = PR_GetCurrentThread(); + return me == queue->handlerThread; +} + +PR_EXTERN(PRBool) +PL_IsQueueNative(PLEventQueue *queue) +{ + return queue->type == EventQueueIsNative ? PR_TRUE : PR_FALSE; +} + +#if defined(_WIN32) +/* +** Global Instance handle... +** In Win32 this is the module handle of the DLL. +** +*/ +static HINSTANCE _pr_hInstance; +#endif + + +#if defined(_WIN32) + +/* +** Initialization routine for the DLL... +*/ + +BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + _pr_hInstance = hDLL; + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + _pr_hInstance = NULL; + break; + } + + return TRUE; +} +#endif + + +#if defined(_WIN32) || defined(XP_OS2) +#ifdef XP_OS2 +MRESULT EXPENTRY +_md_EventReceiverProc(HWND hwnd, ULONG uMsg, MPARAM wParam, MPARAM lParam) +#else +LRESULT CALLBACK +_md_EventReceiverProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +#endif +{ + if (_pr_PostEventMsgId == uMsg ) + { + PREventQueue *queue = (PREventQueue *)lParam; + queue->removeMsg = PR_FALSE; + PL_ProcessPendingEvents(queue); + queue->removeMsg = PR_TRUE; +#ifdef XP_OS2 + return MRFROMLONG(TRUE); +#else + return TRUE; +#endif + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +static PRBool isInitialized; +static PRCallOnceType once; +static PRLock *initLock; + +/* +** InitWinEventLib() -- Create the Windows initialization lock +** +*/ +static PRStatus InitEventLib( void ) +{ + PR_ASSERT( initLock == NULL ); + + initLock = PR_NewLock(); + return initLock ? PR_SUCCESS : PR_FAILURE; +} + +#endif /* Win32, OS2 */ + +#if defined(_WIN32) + +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ + WNDCLASS wc; + + /* + ** If this is the first call to PL_InitializeEventsLib(), + ** make the call to InitWinEventLib() to create the initLock. + ** + ** Then lock the initializer lock to insure that + ** we have exclusive control over the initialization sequence. + ** + */ + + + /* Register the windows message for XPCOM Event notification */ + _pr_PostEventMsgId = RegisterWindowMessage("XPCOM_PostEvent"); + + /* Register the class for the event receiver window */ + if (!GetClassInfo(_pr_hInstance, _pr_eventWindowClass, &wc)) { + wc.style = 0; + wc.lpfnWndProc = _md_EventReceiverProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = _pr_hInstance; + wc.hIcon = NULL; + wc.hCursor = NULL; + wc.hbrBackground = (HBRUSH) NULL; + wc.lpszMenuName = (LPCSTR) NULL; + wc.lpszClassName = _pr_eventWindowClass; + RegisterClass(&wc); + } + + /* Create the event receiver window */ + eventQueue->eventReceiverWindow = CreateWindow(_pr_eventWindowClass, + "XPCOM:EventReceiver", + 0, 0, 0, 10, 10, + NULL, NULL, _pr_hInstance, + NULL); + PR_ASSERT(eventQueue->eventReceiverWindow); + /* Set a property which can be used to retrieve the event queue + * within the _md_TimerProc callback + */ + SetProp(eventQueue->eventReceiverWindow, + _md_GetEventQueuePropName(), (HANDLE)eventQueue); + + return; +} /* end _md_CreateEventQueue() */ +#endif /* Winxx */ + +#if defined(XP_OS2) +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ + /* Must have HMQ for this & can't assume we already have appshell */ + if( FALSE == WinQueryQueueInfo( HMQ_CURRENT, NULL, 0)) + { + PPIB ppib; + PTIB ptib; + HAB hab; + HMQ hmq; + + /* Set our app to be a PM app before attempting Win calls */ + DosGetInfoBlocks(&ptib, &ppib); + ppib->pib_ultype = 3; + + hab = WinInitialize(0); + hmq = WinCreateMsgQueue(hab, 0); + PR_ASSERT(hmq); + } + + if( !_pr_PostEventMsgId) + { + WinRegisterClass( 0 /* hab_current */, + _pr_eventWindowClass, + _md_EventReceiverProc, + 0, 0); + + _pr_PostEventMsgId = WinAddAtom( WinQuerySystemAtomTable(), + "XPCOM_PostEvent"); + } + + eventQueue->eventReceiverWindow = WinCreateWindow( HWND_DESKTOP, + _pr_eventWindowClass, + "", 0, + 0, 0, 0, 0, + HWND_DESKTOP, + HWND_TOP, + 0, + NULL, + NULL); + PR_ASSERT(eventQueue->eventReceiverWindow); + + return; +} /* end _md_CreateEventQueue() */ +#endif /* XP_OS2 */ + +#if (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ + /* there's really nothing special to do here, + ** the guts of the unix stuff is in the setupnativenotify + ** and related functions. + */ + return; +} /* end _md_CreateEventQueue() */ +#endif /* (defined(XP_UNIX) && !defined(XP_MACOSX)) || defined(XP_BEOS) */ + +#if defined(MAC_USE_CFRUNLOOPSOURCE) +static void _md_EventReceiverProc(void *info) +{ + PLEventQueue *queue = (PLEventQueue*)info; + PL_ProcessPendingEvents(queue); +} + +#elif defined(MAC_USE_CARBON_EVENT) +/* +** _md_CreateEventQueue() -- ModelDependent initializer +*/ + +static pascal OSStatus _md_EventReceiverProc(EventHandlerCallRef nextHandler, + EventRef inEvent, + void* userData) +{ + if (GetEventClass(inEvent) == kEventClassPL && + GetEventKind(inEvent) == kEventProcessPLEvents) + { + PREventQueue *queue; + if (GetEventParameter(inEvent, kEventParamPLEventQueue, + typeUInt32, NULL, sizeof(PREventQueue*), NULL, + &queue) == noErr) + { + PL_ProcessPendingEvents(queue); + return noErr; + } + } + return eventNotHandledErr; +} + +static pascal Boolean _md_CarbonEventComparator(EventRef inEvent, + void *inCompareData) +{ + Boolean match = false; + + if (GetEventClass(inEvent) == kEventClassPL && + GetEventKind(inEvent) == kEventProcessPLEvents) + { + PREventQueue *queue; + match = ((GetEventParameter(inEvent, kEventParamPLEventQueue, + typeUInt32, NULL, sizeof(PREventQueue*), NULL, + &queue) == noErr) && (queue == inCompareData)); + } + return match; +} + +#endif /* defined(MAC_USE_CARBON_EVENT) */ + +#if defined(XP_MACOSX) +static void _md_CreateEventQueue( PLEventQueue *eventQueue ) +{ +#if defined(MAC_USE_CFRUNLOOPSOURCE) + CFRunLoopSourceContext sourceContext = { 0 }; + sourceContext.version = 0; + sourceContext.info = (void*)eventQueue; + sourceContext.perform = _md_EventReceiverProc; + + /* make a run loop source */ + eventQueue->mRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0 /* order */, &sourceContext); + PR_ASSERT(eventQueue->mRunLoopSource); + + eventQueue->mMainRunLoop = CFRunLoopGetCurrent(); + CFRetain(eventQueue->mMainRunLoop); + + /* and add it to the run loop */ + CFRunLoopAddSource(eventQueue->mMainRunLoop, eventQueue->mRunLoopSource, kCFRunLoopCommonModes); + + /* Add it again but with a unique mode name so we can acknowledge it + without processing any other message sources. */ + { /* vbox */ + char szModeName[80]; /* vbox */ + snprintf(szModeName, sizeof(szModeName), "VBoxXPCOMQueueMode-%p", eventQueue); /* vbox */ + eventQueue->mRunLoopModeStr = CFStringCreateWithCString(kCFAllocatorDefault, /* vbox */ + szModeName, kCFStringEncodingASCII); /* vbox */ + CFRunLoopAddSource(eventQueue->mMainRunLoop, /* vbox */ + eventQueue->mRunLoopSource, eventQueue->mRunLoopModeStr); /* vbox */ + } /* vbox */ + +#elif defined(MAC_USE_CARBON_EVENT) + eventQueue->eventHandlerUPP = NewEventHandlerUPP(_md_EventReceiverProc); + PR_ASSERT(eventQueue->eventHandlerUPP); + if (eventQueue->eventHandlerUPP) + { + EventTypeSpec eventType; + + eventType.eventClass = kEventClassPL; + eventType.eventKind = kEventProcessPLEvents; + + InstallApplicationEventHandler(eventQueue->eventHandlerUPP, 1, &eventType, + eventQueue, &eventQueue->eventHandlerRef); + PR_ASSERT(eventQueue->eventHandlerRef); + } +#endif +} /* end _md_CreateEventQueue() */ +#endif /* defined(XP_MACOSX) */ + +/* extra functions for unix */ + +#if defined(XP_UNIX) && !defined(XP_MACOSX) + +PR_IMPLEMENT(PRInt32) +PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID) +{ + PRInt32 count = 0; + PRInt32 fullCount; + + if (aSelf == NULL) + return -1; + + PR_EnterMonitor(aSelf->monitor); + + if (aSelf->processingEvents) { + PR_ExitMonitor(aSelf->monitor); + return 0; + } + + aSelf->processingEvents = PR_TRUE; + + /* Only process the events that are already in the queue, and + * not any new events that get added. Do this by counting the + * number of events currently in the queue + */ + fullCount = _pl_GetEventCount(aSelf); + PR_LOG(event_lm, PR_LOG_DEBUG, + ("$$$ fullCount is %d id is %ld\n", fullCount, aID)); + + if (fullCount == 0) { + aSelf->processingEvents = PR_FALSE; + PR_ExitMonitor(aSelf->monitor); + return 0; + } + + PR_ExitMonitor(aSelf->monitor); + + while (fullCount-- > 0) { + /* peek at the next event */ + PLEvent *event; + event = PR_EVENT_PTR(aSelf->queue.next); + if (event == NULL) + break; + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ processing event %ld\n", + event->id)); + if (event->id >= aID) { + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ skipping event and breaking")); + break; + } + + event = PL_GetEvent(aSelf); + PL_HandleEvent(event); + PR_LOG(event_lm, PR_LOG_DEBUG, ("$$$ done processing event")); + count++; + } + + PR_EnterMonitor(aSelf->monitor); + + /* if full count still had items left then there's still items left + in the queue. Let the native notify token stay. */ + + if (aSelf->type == EventQueueIsNative) { + fullCount = _pl_GetEventCount(aSelf); + + if (fullCount <= 0) { + _pl_AcknowledgeNativeNotify(aSelf); + aSelf->notified = PR_FALSE; + } + } + + aSelf->processingEvents = PR_FALSE; + + PR_ExitMonitor(aSelf->monitor); + + return count; +} + +PR_IMPLEMENT(void) +PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc, + void *aClosure) +{ + aSelf->idFunc = aFunc; + aSelf->idFuncClosure = aClosure; +} + +PR_IMPLEMENT(void) +PL_UnregisterEventIDFunc(PLEventQueue *aSelf) +{ + aSelf->idFunc = 0; + aSelf->idFuncClosure = 0; +} + +#endif /* defined(XP_UNIX) && !defined(XP_MACOSX) */ + +/* --- end plevent.c --- */ diff --git a/src/libs/xpcom18a4/xpcom/threads/plevent.h b/src/libs/xpcom18a4/xpcom/threads/plevent.h new file mode 100644 index 00000000..5bc9c89f --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/threads/plevent.h @@ -0,0 +1,690 @@ +/* -*- Mode: C++; tab-width: 4; 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.org 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 ***** */ + +/********************************************************************** +NSPL Events + +Defining Events +--------------- + +Events are essentially structures that represent argument lists for a +function that will run on another thread. All event structures you +define must include a PLEvent struct as their first field: + + typedef struct MyEventType { + PLEvent e; + // arguments follow... + int x; + char* y; + } MyEventType; + +It is also essential that you establish a model of ownership for each +argument passed in an event record, i.e. whether particular arguments +will be deleted by the event destruction callback, or whether they +only loaned to the event handler callback, and guaranteed to persist +until the time at which the handler is called. + +Sending Events +-------------- + +Events are initialized by PL_InitEvent and can be sent via +PL_PostEvent or PL_PostSynchronousEvent. Events can also have an +owner. The owner of an event can revoke all the events in a given +event-queue by calling PL_RevokeEvents. An owner might want +to do this if, for instance, it is being destroyed, and handling the +events after the owner's destruction would cause an error (e.g. an +MWContext). + +Since the act of initializing and posting an event must be coordinated +with it's possible revocation, it is essential that the event-queue's +monitor be entered surrounding the code that constructs, initializes +and posts the event: + + void postMyEvent(MyOwner* owner, int x, char* y) + { + MyEventType* event; + + PL_ENTER_EVENT_QUEUE_MONITOR(myQueue); + + // construct + event = PR_NEW(MyEventType); + if (event == NULL) goto done; + + // initialize + PL_InitEvent(event, owner, + (PLHandleEventProc)handleMyEvent, + (PLDestroyEventProc)destroyMyEvent); + event->x = x; + event->y = strdup(y); + + // post + PL_PostEvent(myQueue, &event->e); + + done: + PL_EXIT_EVENT_QUEUE_MONITOR(myQueue); + } + +If you don't call PL_InitEvent and PL_PostEvent within the +event-queue's monitor, you'll get a big red assert. + +Handling Events +--------------- + +To handle an event you must write a callback that is passed the event +record you defined containing the event's arguments: + + void* handleMyEvent(MyEventType* event) + { + doit(event->x, event->y); + return NULL; // you could return a value for a sync event + } + +Similarly for the destruction callback: + + void destroyMyEvent(MyEventType* event) + { + free(event->y); // created by strdup + free(event); + } + +Processing Events in Your Event Loop +------------------------------------ + +If your main loop only processes events delivered to the event queue, +things are rather simple. You just get the next event (which may +block), and then handle it: + + while (1) { + event = PL_GetEvent(myQueue); + PL_HandleEvent(event); + } + +However, if other things must be waited on, you'll need to obtain a +file-descriptor that represents your event queue, and hand it to select: + + fd = PL_GetEventQueueSelectFD(myQueue); + ...add fd to select set... + while (select(...)) { + if (...fd...) { + PL_ProcessPendingEvents(myQueue); + } + ... + } + +Of course, with Motif and Windows it's more complicated than that, and +on Mac it's completely different, but you get the picture. + +Revoking Events +--------------- +If at any time an owner of events is about to be destroyed, you must +take steps to ensure that no one tries to use the event queue after +the owner is gone (or a crash may result). You can do this by either +processing all the events in the queue before destroying the owner: + + { + ... + PL_ENTER_EVENT_QUEUE_MONITOR(myQueue); + PL_ProcessPendingEvents(myQueue); + DestroyMyOwner(owner); + PL_EXIT_EVENT_QUEUE_MONITOR(myQueue); + ... + } + +or by revoking the events that are in the queue for that owner. This +removes them from the queue and calls their destruction callback: + + { + ... + PL_ENTER_EVENT_QUEUE_MONITOR(myQueue); + PL_RevokeEvents(myQueue, owner); + DestroyMyOwner(owner); + PL_EXIT_EVENT_QUEUE_MONITOR(myQueue); + ... + } + +In either case it is essential that you be in the event-queue's monitor +to ensure that all events are removed from the queue for that owner, +and to ensure that no more events will be delivered for that owner. +**********************************************************************/ + +#ifndef plevent_h___ +#define plevent_h___ + +#include "prtypes.h" +#include "prclist.h" +#include "prthread.h" +#include "prlock.h" +#include "prcvar.h" +#include "prmon.h" + +/* For HWND */ +#if defined(XP_WIN32) +#include <windef.h> +#elif defined(XP_OS2) +#define INCL_DOSMISC +#define INCL_DOSPROCESS +#define INCL_DOSERRORS +#include <os2.h> +#endif + +#ifdef VBOX_WITH_XPCOM_NAMESPACE_CLEANUP +#define PL_DestroyEvent VBoxNsplPL_DestroyEvent +#define PL_HandleEvent VBoxNsplPL_HandleEvent +#define PL_InitEvent VBoxNsplPL_InitEvent +#define PL_CreateEventQueue VBoxNsplPL_CreateEventQueue +#define PL_CreateMonitoredEventQueue VBoxNsplPL_CreateMonitoredEventQueue +#define PL_CreateNativeEventQueue VBoxNsplPL_CreateNativeEventQueue +#define PL_DequeueEvent VBoxNsplPL_DequeueEvent +#define PL_DestroyEventQueue VBoxNsplPL_DestroyEventQueue +#define PL_EventAvailable VBoxNsplPL_EventAvailable +#define PL_EventLoop VBoxNsplPL_EventLoop +#define PL_GetEvent VBoxNsplPL_GetEvent +#define PL_GetEventOwner VBoxNsplPL_GetEventOwner +#define PL_GetEventQueueMonitor VBoxNsplPL_GetEventQueueMonitor +#define PL_GetEventQueueSelectFD VBoxNsplPL_GetEventQueueSelectFD +#define PL_MapEvents VBoxNsplPL_MapEvents +#define PL_PostEvent VBoxNsplPL_PostEvent +#define PL_PostSynchronousEvent VBoxNsplPL_PostSynchronousEvent +#define PL_ProcessEventsBeforeID VBoxNsplPL_ProcessEventsBeforeID +#define PL_ProcessPendingEvents VBoxNsplPL_ProcessPendingEvents +#define PL_RegisterEventIDFunc VBoxNsplPL_RegisterEventIDFunc +#define PL_RevokeEvents VBoxNsplPL_RevokeEvents +#define PL_UnregisterEventIDFunc VBoxNsplPL_UnregisterEventIDFunc +#define PL_WaitForEvent VBoxNsplPL_WaitForEvent +#define PL_IsQueueNative VBoxNsplPL_IsQueueNative +#define PL_IsQueueOnCurrentThread VBoxNsplPL_IsQueueOnCurrentThread +#define PL_FavorPerformanceHint VBoxNsplPL_FavorPerformanceHint +#endif /* VBOX_WITH_XPCOM_NAMESPACE_CLEANUP */ + +PR_BEGIN_EXTERN_C + +/* Typedefs */ + +typedef struct PLEvent PLEvent; +typedef struct PLEventQueue PLEventQueue; + +/******************************************************************************* + * Event Queue Operations + ******************************************************************************/ + +/* +** Creates a new event queue. Returns NULL on failure. +*/ +PR_EXTERN(PLEventQueue*) +PL_CreateEventQueue(const char* name, PRThread* handlerThread); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PL_CreateNativeEventQueue() +** +** DESCRIPTION: +** PL_CreateNativeEventQueue() creates an event queue that +** uses platform specific notify mechanisms. +** +** For Unix, the platform specific notify mechanism provides +** an FD that may be extracted using the function +** PL_GetEventQueueSelectFD(). The FD returned may be used in +** a select() function call. +** +** For Windows, the platform specific notify mechanism +** provides an event receiver window that is called by +** Windows to process the event using the windows message +** pump engine. +** +** INPUTS: +** name: A name, as a diagnostic aid. +** +** handlerThread: A pointer to the PRThread structure for +** the thread that will "handle" events posted to this event +** queue. +** +** RETURNS: +** A pointer to a PLEventQueue structure or NULL. +** +*/ +PR_EXTERN(PLEventQueue *) + PL_CreateNativeEventQueue( + const char *name, + PRThread *handlerThread + ); + +/* ----------------------------------------------------------------------- +** FUNCTION: PL_CreateMonitoredEventQueue() +** +** DESCRIPTION: +** PL_CreateMonitoredEventQueue() creates an event queue. No +** platform specific notify mechanism is created with the +** event queue. +** +** Users of this type of event queue must explicitly poll the +** event queue to retreive and process events. +** +** +** INPUTS: +** name: A name, as a diagnostic aid. +** +** handlerThread: A pointer to the PRThread structure for +** the thread that will "handle" events posted to this event +** queue. +** +** RETURNS: +** A pointer to a PLEventQueue structure or NULL. +** +*/ +PR_EXTERN(PLEventQueue *) + PL_CreateMonitoredEventQueue( + const char *name, + PRThread *handlerThread + ); + +/* +** Destroys an event queue. +*/ +PR_EXTERN(void) +PL_DestroyEventQueue(PLEventQueue* self); + +/* +** Returns the monitor associated with an event queue. This monitor is +** selectable. The monitor should be entered to protect against anyone +** calling PL_RevokeEvents while the event is trying to be constructed +** and delivered. +*/ +PR_EXTERN(PRMonitor*) +PL_GetEventQueueMonitor(PLEventQueue* self); + +#define PL_ENTER_EVENT_QUEUE_MONITOR(queue) \ + PR_EnterMonitor(PL_GetEventQueueMonitor(queue)) + +#define PL_EXIT_EVENT_QUEUE_MONITOR(queue) \ + PR_ExitMonitor(PL_GetEventQueueMonitor(queue)) + +/* +** Posts an event to an event queue, waking up any threads waiting for an +** event. If event is NULL, notification still occurs, but no event will +** be available. +** +** Any events delivered by this routine will be destroyed by PL_HandleEvent +** when it is called (by the event-handling thread). +*/ +PR_EXTERN(PRStatus) +PL_PostEvent(PLEventQueue* self, PLEvent* event); + +/* +** Like PL_PostEvent, this routine posts an event to the event handling +** thread, but does so synchronously, waiting for the result. The result +** which is the value of the handler routine is returned. +** +** Any events delivered by this routine will be not be destroyed by +** PL_HandleEvent, but instead will be destroyed just before the result is +** returned (by the current thread). +*/ +PR_EXTERN(void*) +PL_PostSynchronousEvent(PLEventQueue* self, PLEvent* event); + +/* +** Gets an event from an event queue. Returns NULL if no event is +** available. +*/ +PR_EXTERN(PLEvent*) +PL_GetEvent(PLEventQueue* self); + +/* +** Returns true if there is an event available for PL_GetEvent. +*/ +PR_EXTERN(PRBool) +PL_EventAvailable(PLEventQueue* self); + +/* +** This is the type of the function that must be passed to PL_MapEvents +** (see description below). +*/ +typedef void +(PR_CALLBACK *PLEventFunProc)(PLEvent* event, void* data, PLEventQueue* queue); + +/* +** Applies a function to every event in the event queue. This can be used +** to selectively handle, filter, or remove events. The data pointer is +** passed to each invocation of the function fun. +*/ +PR_EXTERN(void) +PL_MapEvents(PLEventQueue* self, PLEventFunProc fun, void* data); + +/* +** This routine walks an event queue and destroys any event whose owner is +** the owner specified. The == operation is used to compare owners. +*/ +PR_EXTERN(void) +PL_RevokeEvents(PLEventQueue* self, void* owner); + +/* +** This routine processes all pending events in the event queue. It can be +** called from the thread's main event-processing loop whenever the event +** queue's selectFD is ready (returned by PL_GetEventQueueSelectFD). +*/ +PR_EXTERN(void) +PL_ProcessPendingEvents(PLEventQueue* self); + +/******************************************************************************* + * Pure Event Queues + * + * For when you're only processing PLEvents and there is no native + * select, thread messages, or AppleEvents. + ******************************************************************************/ + +/* +** Blocks until an event can be returned from the event queue. This routine +** may return NULL if the current thread is interrupted. +*/ +PR_EXTERN(PLEvent*) +PL_WaitForEvent(PLEventQueue* self); + +/* +** One stop shopping if all you're going to do is process PLEvents. Just +** call this and it loops forever processing events as they arrive. It will +** terminate when your thread is interrupted or dies. +*/ +PR_EXTERN(void) +PL_EventLoop(PLEventQueue* self); + +/******************************************************************************* + * Native Event Queues + * + * For when you need to call select, or WaitNextEvent, and yet also want + * to handle PLEvents. + ******************************************************************************/ + +/* +** This routine allows you to grab the file descriptor associated with an +** event queue and use it in the readFD set of select. Useful for platforms +** that support select, and must wait on other things besides just PLEvents. +*/ +PR_EXTERN(PRInt32) +PL_GetEventQueueSelectFD(PLEventQueue* self); + +/* +** This routine will allow you to check to see if the given eventQueue in +** on the current thread. It will return PR_TRUE if so, else it will return +** PR_FALSE +*/ +PR_EXTERN(PRBool) + PL_IsQueueOnCurrentThread( PLEventQueue *queue ); + +/* +** Returns whether the queue is native (true) or monitored (false) +*/ +PR_EXTERN(PRBool) +PL_IsQueueNative(PLEventQueue *queue); + +/******************************************************************************* + * Event Operations + ******************************************************************************/ + +/* +** The type of an event handler function. This function is passed as an +** initialization argument to PL_InitEvent, and called by +** PL_HandleEvent. If the event is called synchronously, a void* result +** may be returned (otherwise any result will be ignored). +*/ +typedef void* +(PR_CALLBACK *PLHandleEventProc)(PLEvent* self); + +/* +** The type of an event destructor function. This function is passed as +** an initialization argument to PL_InitEvent, and called by +** PL_DestroyEvent. +*/ +typedef void +(PR_CALLBACK *PLDestroyEventProc)(PLEvent* self); + +/* +** Initializes an event. Usually events are embedded in a larger event +** structure which holds event-specific data, so this is an initializer +** for that embedded part of the structure. +*/ +PR_EXTERN(void) +PL_InitEvent(PLEvent* self, void* owner, + PLHandleEventProc handler, + PLDestroyEventProc destructor); + +/* +** Returns the owner of an event. +*/ +PR_EXTERN(void*) +PL_GetEventOwner(PLEvent* self); + +/* +** Handles an event, calling the event's handler routine. +*/ +PR_EXTERN(void) +PL_HandleEvent(PLEvent* self); + +/* +** Destroys an event, calling the event's destructor. +*/ +PR_EXTERN(void) +PL_DestroyEvent(PLEvent* self); + +/* +** Removes an event from an event queue. +*/ +PR_EXTERN(void) +PL_DequeueEvent(PLEvent* self, PLEventQueue* queue); + + +/* + * Give hint to native PL_Event notification mechanism. If the native + * platform needs to tradeoff performance vs. native event starvation + * this hint tells the native dispatch code which to favor. + * The default is to prevent event starvation. + * + * Calls to this function may be nested. When the number of calls that + * pass PR_TRUE is subtracted from the number of calls that pass PR_FALSE + * is greater than 0, performance is given precedence over preventing + * event starvation. + * + * The starvationDelay arg is only used when + * favorPerformanceOverEventStarvation is PR_FALSE. It is the + * amount of time in milliseconds to wait before the PR_FALSE actually + * takes effect. + */ +PR_EXTERN(void) +PL_FavorPerformanceHint(PRBool favorPerformanceOverEventStarvation, PRUint32 starvationDelay); + + +/******************************************************************************* + * Private Stuff + ******************************************************************************/ + +struct PLEvent { + PRCList link; + PLHandleEventProc handler; + PLDestroyEventProc destructor; + void* owner; + void* synchronousResult; + PRLock* lock; + PRCondVar* condVar; + PRBool handled; +#ifdef PL_POST_TIMINGS + PRIntervalTime postTime; +#endif +#ifdef XP_UNIX + unsigned long id; +#endif /* XP_UNIX */ + /* other fields follow... */ +}; + +/******************************************************************************/ + +/* +** Returns the event queue associated with the main thread. +** +*/ +#if defined(XP_WIN) || defined(XP_OS2) +/* ----------------------------------------------------------------------- +** FUNCTION: PL_GetNativeEventReceiverWindow() +** +** DESCRIPTION: +** PL_GetNativeEventReceiverWindow() returns the windows +** handle of the event receiver window associated with the +** referenced PLEventQueue argument. +** +** INPUTS: +** PLEventQueue pointer +** +** RETURNS: +** event receiver window handle. +** +** RESTRICTIONS: MS-Windows ONLY. +** +*/ +PR_EXTERN(HWND) + PL_GetNativeEventReceiverWindow( + PLEventQueue *eqp + ); +#endif /* XP_WIN || XP_OS2 */ + +#ifdef XP_UNIX +/* ----------------------------------------------------------------------- +** FUNCTION: PL_ProcessEventsBeforeID() +** +** DESCRIPTION: +** +** PL_ProcessEventsBeforeID() will process events in a native event +** queue that have an id that is older than the ID passed in. +** +** INPUTS: +** PLEventQueue *aSelf +** unsigned long aID +** +** RETURNS: +** PRInt32 number of requests processed, -1 on error. +** +** RESTRICTIONS: Unix only (well, X based unix only) +*/ +PR_EXTERN(PRInt32) +PL_ProcessEventsBeforeID(PLEventQueue *aSelf, unsigned long aID); + +/* This prototype is a function that can be called when an event is + posted to stick an ID on it. */ + +typedef unsigned long +(PR_CALLBACK *PLGetEventIDFunc)(void *aClosure); + + +/* ----------------------------------------------------------------------- +** FUNCTION: PL_RegisterEventIDFunc() +** +** DESCRIPTION: +** +** This function registers a function for getting the ID on unix for +** this event queue. +** +** INPUTS: +** PLEventQueue *aSelf +** PLGetEventIDFunc func +** void *aClosure +** +** RETURNS: +** void +** +** RESTRICTIONS: Unix only (well, X based unix only) */ +PR_EXTERN(void) +PL_RegisterEventIDFunc(PLEventQueue *aSelf, PLGetEventIDFunc aFunc, + void *aClosure); + +/* ----------------------------------------------------------------------- +** FUNCTION: PL_RegisterEventIDFunc() +** +** DESCRIPTION: +** +** This function unregisters a function for getting the ID on unix for +** this event queue. +** +** INPUTS: +** PLEventQueue *aSelf +** +** RETURNS: +** void +** +** RESTRICTIONS: Unix only (well, X based unix only) */ +PR_EXTERN(void) +PL_UnregisterEventIDFunc(PLEventQueue *aSelf); + +#endif /* XP_UNIX */ + + +/* ----------------------------------------------------------------------- */ + +#if defined(NO_NSPR_10_SUPPORT) +#else +/********* ???????????????? FIX ME ??????????????????????????? *****/ +/********************** Some old definitions *****************************/ + +/* Re: prevent.h->plevent.h */ +#define PREvent PLEvent +#define PREventQueue PLEventQueue +#define PR_CreateEventQueue PL_CreateEventQueue +#define PR_DestroyEventQueue PL_DestroyEventQueue +#define PR_GetEventQueueMonitor PL_GetEventQueueMonitor +#define PR_ENTER_EVENT_QUEUE_MONITOR PL_ENTER_EVENT_QUEUE_MONITOR +#define PR_EXIT_EVENT_QUEUE_MONITOR PL_EXIT_EVENT_QUEUE_MONITOR +#define PR_PostEvent PL_PostEvent +#define PR_PostSynchronousEvent PL_PostSynchronousEvent +#define PR_GetEvent PL_GetEvent +#define PR_EventAvailable PL_EventAvailable +#define PREventFunProc PLEventFunProc +#define PR_MapEvents PL_MapEvents +#define PR_RevokeEvents PL_RevokeEvents +#define PR_ProcessPendingEvents PL_ProcessPendingEvents +#define PR_WaitForEvent PL_WaitForEvent +#define PR_EventLoop PL_EventLoop +#define PR_GetEventQueueSelectFD PL_GetEventQueueSelectFD +#define PRHandleEventProc PLHandleEventProc +#define PRDestroyEventProc PLDestroyEventProc +#define PR_InitEvent PL_InitEvent +#define PR_GetEventOwner PL_GetEventOwner +#define PR_HandleEvent PL_HandleEvent +#define PR_DestroyEvent PL_DestroyEvent +#define PR_DequeueEvent PL_DequeueEvent +#define PR_GetMainEventQueue PL_GetMainEventQueue + +/********* ????????????? End Fix me ?????????????????????????????? *****/ +#endif /* NO_NSPR_10_SUPPORT */ + +PR_END_EXTERN_C + +#endif /* plevent_h___ */ |