diff --git a/widget/timer/public/nsITimer.h b/widget/timer/public/nsITimer.h index a92c2328cb6d..37050eabebd1 100644 --- a/widget/timer/public/nsITimer.h +++ b/widget/timer/public/nsITimer.h @@ -71,7 +71,7 @@ typedef void // sucession. // --- Indicate if timers on your platform support repeating timers --- -#if defined(XP_PC) || defined(XP_UNIX) || defined(XP_MAC) +#if defined(XP_PC) || defined(XP_UNIX) || defined(XP_MAC) || defined(XP_BEOS) #define REPEATING_TIMERS 1 #endif diff --git a/widget/timer/src/beos/Makefile.in b/widget/timer/src/beos/Makefile.in index 2442140c9abb..7cb1824a5291 100644 --- a/widget/timer/src/beos/Makefile.in +++ b/widget/timer/src/beos/Makefile.in @@ -26,6 +26,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk +MODULE = timer LIBRARY_NAME = timer_$(TIMER_SUFFIX) EXPORT_LIBRARY = 1 diff --git a/widget/timer/src/beos/nsTimerBeOS.cpp b/widget/timer/src/beos/nsTimerBeOS.cpp index e5cf0b2f9eaa..592591e0eba4 100644 --- a/widget/timer/src/beos/nsTimerBeOS.cpp +++ b/widget/timer/src/beos/nsTimerBeOS.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -14,20 +14,28 @@ * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All + * Copyright (C) 1998-2001 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): + * Yannick Koehler + * Chris Seawood */ +#include "nsDebug.h" +#include "nsCOMPtr.h" #include "nsVoidArray.h" #include "nsTimerBeOS.h" -#include "nsCOMPtr.h" +#include #include #include #include +#define TIMER_INTERVAL 10000 // 10 millisecs + +//#define TIMER_DEBUG 1 + static NS_DEFINE_IID(kITimerIID, NS_ITIMER_IID); struct ThreadInterfaceData @@ -54,11 +62,23 @@ static sem_id my_find_sem(const char *name) return ret; } +static int sort_timers (const void *a, const void *b) +{ + // Should state potentially stupid assumption: + // timers will never be null + if ((*(nsTimerBeOS **)a)->GetSchedTime() < (*(nsTimerBeOS **)b)->GetSchedTime()) + return -1; + if ((*(nsTimerBeOS **)a)->GetSchedTime() > (*(nsTimerBeOS **)b)->GetSchedTime()) + return 1; + return 0; +} + TimerManager nsTimerBeOS::sTimerManager; TimerManager::TimerManager() - : BList(40) + : BList(40) { + mProcessing = 0; mQuitRequested = false; mSyncSem = create_sem(0, "timer sync"); if(mSyncSem < 0) @@ -88,19 +108,12 @@ void TimerManager::AddRequest(nsITimer *inRequest) { NS_ADDREF(inRequest); // this is for the timer list - // insert sorted into timer event list - int32 count = CountItems(); - int32 pos; - for(pos = 0; pos < count; pos++) - { - nsITimer *entry = (nsITimer *)ItemAtFast(pos); - if(((nsTimerBeOS *)entry)->mSchedTime > ((nsTimerBeOS*)inRequest)->mSchedTime) - break; - } - AddItem(inRequest, pos); - - if(pos == 0) - // We need to wake the thread to wait on the newly added event + bool was_empty = IsEmpty(); + AddItem(inRequest); + SortItems(sort_timers); + + // We need to wake the thread to wait on the newly added event + if (was_empty) release_sem(mSyncSem); mLocker.Unlock(); @@ -109,20 +122,36 @@ void TimerManager::AddRequest(nsITimer *inRequest) bool TimerManager::RemoveRequest(nsITimer *inRequest) { - bool found = false; + if ((nsTimerBeOS *)mProcessing == inRequest) { +#ifdef TIMER_DEBUG + fprintf(stderr, "Request being processed, cannot remove %p\n", inRequest); +#endif + return true; + } - if(mLocker.Lock()) - { - if(RemoveItem(inRequest)) - { - NS_RELEASE(inRequest); - found = true; - } +#ifdef TIMER_DEBUG + fprintf(stderr, "Our request isn't being processed, delete it %p != %p\n", + (nsTimerBeOS *)mProcessing, inRequest); +#endif + if(mLocker.Lock()) + { + if (RemoveItem(inRequest)) + NS_RELEASE(inRequest); + mLocker.Unlock(); + } else { + NS_WARNING("Could not get lock for RemoveRequest"); + } - mLocker.Unlock(); - } + return true; +} - return found; +void TimerManager::SortRequests(void) +{ + if (mLocker.Lock()) + { + SortItems(sort_timers); + mLocker.Unlock(); + } } int32 TimerManager::sTimerThreadFunc(void *inData) @@ -134,56 +163,165 @@ int32 TimerManager::TimerThreadFunc() { char portname[64]; char semname[64]; - port_id eventport; - sem_id syncsem; + port_id eventport = 0; + sem_id syncsem = 0; PRThread *cached = (PRThread *)-1; - + bigtime_t now = system_time(); + while(! mQuitRequested) { - nsITimer *tobj = 0; + nsITimer *tobj = 0; + status_t ac_status; - mLocker.Lock(); - - bigtime_t now = system_time(); + // Only process timers once every TIMER_INTERVAL + snooze_until(now + TIMER_INTERVAL, B_SYSTEM_TIMEBASE); + now = system_time(); // Fire expired pending requests - while((tobj = FirstRequest()) != 0 && ((nsTimerBeOS*)tobj)->mSchedTime <= now) + while (1) { - nsTimerBeOS *tobjbeos = (nsTimerBeOS *)tobj; - - RemoveItem((int32)0); - mLocker.Unlock(); + nsTimerBeOS *tobjbeos; - if(! tobjbeos->mCanceled) - { - // fire it - if(tobjbeos->mThread != cached) - { - sprintf(portname, "event%lx", (uint32)tobjbeos->mThread); - sprintf(semname, "sync%lx", (uint32)tobjbeos->mThread); + if (!mLocker.Lock()) + continue; - eventport = find_port(portname); - syncsem = my_find_sem(semname); - cached = tobjbeos->mThread; - } + tobj = FirstRequest(); + mLocker.Unlock(); - // call timer synchronously so we're sure tobj is alive - ThreadInterfaceData id; - id.data = tobjbeos; - id.sync = true; - if(write_port(eventport, 'WMti', &id, sizeof(id)) == B_OK) - while(acquire_sem(syncsem) == B_INTERRUPTED) - ; - } - NS_RELEASE(tobjbeos); + // No more entries + if (!tobj) + break; - mLocker.Lock(); - } - mLocker.Unlock(); + if (mProcessing) + continue; - if(acquire_sem_etc(mSyncSem, 1, B_ABSOLUTE_TIMEOUT, - tobj ? ((nsTimerBeOS *)tobj)->mSchedTime : B_INFINITE_TIMEOUT) == B_BAD_SEM_ID) - break; + tobjbeos = (nsTimerBeOS *)tobj; + mProcessing = tobjbeos; + + // Since requests are sorted, + // exit loop if we reach a request + // that's later than now + if (tobjbeos->GetSchedTime() > now) + { + mProcessing = 0; + break; + } + + if (tobjbeos->IsCanceled()) + { + if (mLocker.Lock()) + { + if (RemoveItem((int32)0)) + NS_RELEASE(tobjbeos); + mLocker.Unlock(); + } else + { + NS_WARNING("could not get Lock()"); + } + mProcessing = 0; + continue; + } + + // At this point we should have the sem + // fire timeout + +//#define FIRE_CALLBACKS_FROM_TIMER_THREAD 1 + +#ifndef FIRE_CALLBACKS_FROM_TIMER_THREAD + PRThread *thread = tobjbeos->GetThread(); + + if(thread != cached) + { + sprintf(portname, "event%lx", (uint32)thread); + sprintf(semname, "sync%lx", (uint32)thread); + + eventport = find_port(portname); + syncsem = my_find_sem(semname); + cached = thread; + } + + // call timer synchronously so we're sure tobj is alive + ThreadInterfaceData id; + id.data = tobjbeos; + id.sync = true; + + if(write_port(eventport, 'WMti', &id, sizeof(id)) == B_OK) +#else + tobjbeos->FireTimeout(); +#endif + { + ac_status = B_INTERRUPTED; + while(ac_status == B_INTERRUPTED) + { + ac_status = acquire_sem(syncsem); +#ifdef TIMER_DEBUG + if (ac_status == B_BAD_SEM_ID) + fprintf(stderr, "Bad semaphore id for syncsem %s\n", semname); +#endif + } + } + + NS_ASSERTION(tobjbeos, "how did they delete it?"); + + // Check if request has been cancelled in callback + if (tobjbeos->IsCanceled()) + { + if (mLocker.Lock()) { + if (HasItem(tobjbeos)) + { +#ifdef TIMER_DEBUG + fprintf(stderr, "Removing previously canceled item %p\n", tobjbeos); +#endif + if (RemoveItem(tobjbeos)) + NS_RELEASE(tobjbeos); + } + mLocker.Unlock(); + } else + { + NS_WARNING("cannot get lock"); + } + mProcessing = 0; + continue; + } + + if (tobjbeos->GetType() == NS_TYPE_ONE_SHOT) + { + if (mLocker.Lock()) + { + if (RemoveItem(tobjbeos)) + NS_RELEASE(tobjbeos); + mLocker.Unlock(); + } else + { + NS_WARNING("Unable to get lock to remove processed one shot\n"); + } + } else + { + tobjbeos->SetDelay(tobjbeos->GetDelay()); + } + + mProcessing = 0; + +#ifdef TIMER_DEBUG + fprintf (stderr, "Loop again\n"); +#endif + } + + if (tobj) + { + ac_status = acquire_sem_etc(mSyncSem, 1, B_ABSOLUTE_TIMEOUT, + ((nsTimerBeOS *)tobj)->GetSchedTime()); + } else + { + ac_status = acquire_sem(mSyncSem); + } + if (ac_status == B_BAD_SEM_ID) + { +#ifdef TIMER_DEBUG + fprintf(stderr, "end loop bad sem\n"); +#endif + break; + } } return B_OK; @@ -194,6 +332,7 @@ int32 TimerManager::TimerThreadFunc() // void nsTimerBeOS::FireTimeout() { + NS_ADDREF_THIS(); if( ! mCanceled) { if(mFunc != NULL) @@ -201,6 +340,7 @@ void nsTimerBeOS::FireTimeout() else if(mCallback != NULL) mCallback->Notify(this); // But if there's an interface, notify it. } + NS_RELEASE_THIS(); } nsTimerBeOS::nsTimerBeOS() @@ -210,12 +350,17 @@ nsTimerBeOS::nsTimerBeOS() mCallback = 0; mDelay = 0; mClosure = 0; - mSchedTime = 0; + mSchedTime = 0; + mPriority = 0; + mType = NS_TYPE_ONE_SHOT; mCanceled = false; } nsTimerBeOS::~nsTimerBeOS() { +#ifdef TIMER_DEBUG + fprintf(stderr, "nsTimerBeOS Destructor\n"); +#endif Cancel(); NS_IF_RELEASE(mCallback); } @@ -225,10 +370,10 @@ void nsTimerBeOS::SetDelay(PRUint32 aDelay) mDelay = aDelay; mSchedTime = system_time() + mDelay * 1000; - NS_ADDREF(this); - if (nsTimerBeOS::sTimerManager.RemoveRequest(this)) - nsTimerBeOS::sTimerManager.AddRequest(this); - Release(); + // Since we could be resetting the delay on a timer + // that the manager already knows about, + // make the manager resort its list + nsTimerBeOS::sTimerManager.SortRequests(); } void nsTimerBeOS::SetPriority(PRUint32 aPriority) @@ -247,41 +392,47 @@ nsresult nsTimerBeOS::Init(nsTimerCallbackFunc aFunc, void *aClosure, mFunc = aFunc; mClosure = aClosure; - return Init(aDelay); + return Init(aDelay, aType); } nsresult nsTimerBeOS::Init(nsITimerCallback *aCallback, PRUint32 aDelay, PRUint32 aPriority, PRUint32 aType) { mCallback = aCallback; - NS_ADDREF(mCallback); + NS_IF_ADDREF(mCallback); - return Init(aDelay); + return Init(aDelay, aType); } -nsresult nsTimerBeOS::Init(PRUint32 aDelay) +nsresult nsTimerBeOS::Init(PRUint32 aDelay, PRUint32 aType) { mDelay = aDelay; - NS_ADDREF(this); // this is for clients of the timer + mType = aType; mSchedTime = system_time() + aDelay * 1000; mThread = PR_GetCurrentThread(); sTimerManager.AddRequest(this); - - return NS_OK; + + return NS_OK; } -NS_IMPL_ISUPPORTS(nsTimerBeOS, kITimerIID); +NS_IMPL_THREADSAFE_ISUPPORTS(nsTimerBeOS, kITimerIID); void nsTimerBeOS::Cancel() { - mCanceled = true; - nsTimerBeOS::sTimerManager.RemoveRequest(this); + if (!mCanceled) { + mCanceled = true; + nsTimerBeOS::sTimerManager.RemoveRequest(this); + } +#ifdef TIMER_DEBUG + fprintf(stderr, "Cancel: %p with mRefCnt %d\n", this, mRefCnt); +#endif } +#ifdef MOZ_MONOLITHIC_TOOLKIT nsresult NS_NewTimer(nsITimer** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); @@ -294,5 +445,5 @@ nsresult NS_NewTimer(nsITimer** aInstancePtrResult) return timer->QueryInterface(kITimerIID, (void **) aInstancePtrResult); } - +#endif diff --git a/widget/timer/src/beos/nsTimerBeOS.h b/widget/timer/src/beos/nsTimerBeOS.h index d3063ce1bb05..69144470002b 100644 --- a/widget/timer/src/beos/nsTimerBeOS.h +++ b/widget/timer/src/beos/nsTimerBeOS.h @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file @@ -14,10 +14,12 @@ * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are - * Copyright (C) 1998 Netscape Communications Corporation. All + * Copyright (C) 1998-2001 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): + * Yannick Koehler + * Chris Seawood */ #ifndef __nsTimerBeOS_h @@ -38,21 +40,22 @@ public: ~TimerManager(); void AddRequest(nsITimer *inRequest); bool RemoveRequest(nsITimer *inRequest); + void SortRequests(); private: BLocker mLocker; sem_id mSyncSem; thread_id mTimerThreadID; + void *mProcessing; bool mQuitRequested; static int32 sTimerThreadFunc(void *); int32 TimerThreadFunc(); - nsITimer *FirstRequest() { return (nsITimer *)FirstItem(); } + nsITimer *FirstRequest() { return (nsITimer *)FirstItem(); } }; class nsTimerBeOS : public nsITimer { - friend class TimerManager; public: nsTimerBeOS(); @@ -88,23 +91,27 @@ public: NS_IMETHOD_(void) FireTimeout(); - bigtime_t mSchedTime; // Time when this request should be done - PRUint32 mDelay; // The delay set in Init() + bigtime_t GetSchedTime() { return mSchedTime; } + bool IsCanceled() { return mCanceled; } + PRThread* GetThread() { return mThread; } + private: - nsresult Init(PRUint32 aDelay); // Initialize the timer. + nsresult Init(PRUint32 aDelay, PRUint32 aType); // Initialize the timer. PRUint32 mPriority; PRUint32 mType; + PRUint32 mDelay; // The delay set in Init() nsTimerCallbackFunc mFunc; // The function to call back when expired void * mClosure; // The argumnet to pass it. nsITimerCallback * mCallback; // An interface to notify when expired. bool mCanceled; PRThread * mThread; -// PRBool mRepeat; // A repeat, not implemented yet. + + bigtime_t mSchedTime; // Time when this request should be done public: - static TimerManager sTimerManager; + static TimerManager sTimerManager; }; #endif // __nsTimerBeOS_h