Adding support for repeating timers under BeOS (should also make anim gifs work now).

Bug #78069 r=VYA04230


git-svn-id: svn://10.0.0.236/trunk@100242 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
cls%seawood.org 2001-08-02 23:19:26 +00:00
parent b2b1c285c8
commit de606c282c
4 changed files with 251 additions and 92 deletions

View File

@ -71,7 +71,7 @@ typedef void
// sucession. // sucession.
// --- Indicate if timers on your platform support repeating timers --- // --- 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 #define REPEATING_TIMERS 1
#endif #endif

View File

@ -26,6 +26,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk include $(DEPTH)/config/autoconf.mk
MODULE = timer
LIBRARY_NAME = timer_$(TIMER_SUFFIX) LIBRARY_NAME = timer_$(TIMER_SUFFIX)
EXPORT_LIBRARY = 1 EXPORT_LIBRARY = 1

View File

@ -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 * The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file * 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 * The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are * Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All * Copyright (C) 1998-2001 Netscape Communications Corporation. All
* Rights Reserved. * Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Yannick Koehler <ykoehler@mythrium.com>
* Chris Seawood <cls@seawood.org>
*/ */
#include "nsDebug.h"
#include "nsCOMPtr.h"
#include "nsVoidArray.h" #include "nsVoidArray.h"
#include "nsTimerBeOS.h" #include "nsTimerBeOS.h"
#include "nsCOMPtr.h"
#include <OS.h>
#include <Application.h> #include <Application.h>
#include <Message.h> #include <Message.h>
#include <signal.h> #include <signal.h>
#define TIMER_INTERVAL 10000 // 10 millisecs
//#define TIMER_DEBUG 1
static NS_DEFINE_IID(kITimerIID, NS_ITIMER_IID); static NS_DEFINE_IID(kITimerIID, NS_ITIMER_IID);
struct ThreadInterfaceData struct ThreadInterfaceData
@ -54,11 +62,23 @@ static sem_id my_find_sem(const char *name)
return ret; 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 nsTimerBeOS::sTimerManager;
TimerManager::TimerManager() TimerManager::TimerManager()
: BList(40) : BList(40)
{ {
mProcessing = 0;
mQuitRequested = false; mQuitRequested = false;
mSyncSem = create_sem(0, "timer sync"); mSyncSem = create_sem(0, "timer sync");
if(mSyncSem < 0) if(mSyncSem < 0)
@ -88,19 +108,12 @@ void TimerManager::AddRequest(nsITimer *inRequest)
{ {
NS_ADDREF(inRequest); // this is for the timer list NS_ADDREF(inRequest); // this is for the timer list
// insert sorted into timer event list bool was_empty = IsEmpty();
int32 count = CountItems(); AddItem(inRequest);
int32 pos; SortItems(sort_timers);
for(pos = 0; pos < count; pos++)
{ // We need to wake the thread to wait on the newly added event
nsITimer *entry = (nsITimer *)ItemAtFast(pos); if (was_empty)
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
release_sem(mSyncSem); release_sem(mSyncSem);
mLocker.Unlock(); mLocker.Unlock();
@ -109,20 +122,36 @@ void TimerManager::AddRequest(nsITimer *inRequest)
bool TimerManager::RemoveRequest(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()) #ifdef TIMER_DEBUG
{ fprintf(stderr, "Our request isn't being processed, delete it %p != %p\n",
if(RemoveItem(inRequest)) (nsTimerBeOS *)mProcessing, inRequest);
{ #endif
NS_RELEASE(inRequest); if(mLocker.Lock())
found = true; {
} 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) int32 TimerManager::sTimerThreadFunc(void *inData)
@ -134,56 +163,165 @@ int32 TimerManager::TimerThreadFunc()
{ {
char portname[64]; char portname[64];
char semname[64]; char semname[64];
port_id eventport; port_id eventport = 0;
sem_id syncsem; sem_id syncsem = 0;
PRThread *cached = (PRThread *)-1; PRThread *cached = (PRThread *)-1;
bigtime_t now = system_time();
while(! mQuitRequested) while(! mQuitRequested)
{ {
nsITimer *tobj = 0; nsITimer *tobj = 0;
status_t ac_status;
mLocker.Lock(); // Only process timers once every TIMER_INTERVAL
snooze_until(now + TIMER_INTERVAL, B_SYSTEM_TIMEBASE);
bigtime_t now = system_time(); now = system_time();
// Fire expired pending requests // Fire expired pending requests
while((tobj = FirstRequest()) != 0 && ((nsTimerBeOS*)tobj)->mSchedTime <= now) while (1)
{ {
nsTimerBeOS *tobjbeos = (nsTimerBeOS *)tobj; nsTimerBeOS *tobjbeos;
RemoveItem((int32)0);
mLocker.Unlock();
if(! tobjbeos->mCanceled) if (!mLocker.Lock())
{ continue;
// fire it
if(tobjbeos->mThread != cached)
{
sprintf(portname, "event%lx", (uint32)tobjbeos->mThread);
sprintf(semname, "sync%lx", (uint32)tobjbeos->mThread);
eventport = find_port(portname); tobj = FirstRequest();
syncsem = my_find_sem(semname); mLocker.Unlock();
cached = tobjbeos->mThread;
}
// call timer synchronously so we're sure tobj is alive // No more entries
ThreadInterfaceData id; if (!tobj)
id.data = tobjbeos; break;
id.sync = true;
if(write_port(eventport, 'WMti', &id, sizeof(id)) == B_OK)
while(acquire_sem(syncsem) == B_INTERRUPTED)
;
}
NS_RELEASE(tobjbeos);
mLocker.Lock(); if (mProcessing)
} continue;
mLocker.Unlock();
if(acquire_sem_etc(mSyncSem, 1, B_ABSOLUTE_TIMEOUT, tobjbeos = (nsTimerBeOS *)tobj;
tobj ? ((nsTimerBeOS *)tobj)->mSchedTime : B_INFINITE_TIMEOUT) == B_BAD_SEM_ID) mProcessing = tobjbeos;
break;
// 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; return B_OK;
@ -194,6 +332,7 @@ int32 TimerManager::TimerThreadFunc()
// //
void nsTimerBeOS::FireTimeout() void nsTimerBeOS::FireTimeout()
{ {
NS_ADDREF_THIS();
if( ! mCanceled) if( ! mCanceled)
{ {
if(mFunc != NULL) if(mFunc != NULL)
@ -201,6 +340,7 @@ void nsTimerBeOS::FireTimeout()
else if(mCallback != NULL) else if(mCallback != NULL)
mCallback->Notify(this); // But if there's an interface, notify it. mCallback->Notify(this); // But if there's an interface, notify it.
} }
NS_RELEASE_THIS();
} }
nsTimerBeOS::nsTimerBeOS() nsTimerBeOS::nsTimerBeOS()
@ -210,12 +350,17 @@ nsTimerBeOS::nsTimerBeOS()
mCallback = 0; mCallback = 0;
mDelay = 0; mDelay = 0;
mClosure = 0; mClosure = 0;
mSchedTime = 0; mSchedTime = 0;
mPriority = 0;
mType = NS_TYPE_ONE_SHOT;
mCanceled = false; mCanceled = false;
} }
nsTimerBeOS::~nsTimerBeOS() nsTimerBeOS::~nsTimerBeOS()
{ {
#ifdef TIMER_DEBUG
fprintf(stderr, "nsTimerBeOS Destructor\n");
#endif
Cancel(); Cancel();
NS_IF_RELEASE(mCallback); NS_IF_RELEASE(mCallback);
} }
@ -225,10 +370,10 @@ void nsTimerBeOS::SetDelay(PRUint32 aDelay)
mDelay = aDelay; mDelay = aDelay;
mSchedTime = system_time() + mDelay * 1000; mSchedTime = system_time() + mDelay * 1000;
NS_ADDREF(this); // Since we could be resetting the delay on a timer
if (nsTimerBeOS::sTimerManager.RemoveRequest(this)) // that the manager already knows about,
nsTimerBeOS::sTimerManager.AddRequest(this); // make the manager resort its list
Release(); nsTimerBeOS::sTimerManager.SortRequests();
} }
void nsTimerBeOS::SetPriority(PRUint32 aPriority) void nsTimerBeOS::SetPriority(PRUint32 aPriority)
@ -247,41 +392,47 @@ nsresult nsTimerBeOS::Init(nsTimerCallbackFunc aFunc, void *aClosure,
mFunc = aFunc; mFunc = aFunc;
mClosure = aClosure; mClosure = aClosure;
return Init(aDelay); return Init(aDelay, aType);
} }
nsresult nsTimerBeOS::Init(nsITimerCallback *aCallback, nsresult nsTimerBeOS::Init(nsITimerCallback *aCallback,
PRUint32 aDelay, PRUint32 aPriority, PRUint32 aType) PRUint32 aDelay, PRUint32 aPriority, PRUint32 aType)
{ {
mCallback = aCallback; 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; mDelay = aDelay;
NS_ADDREF(this); // this is for clients of the timer mType = aType;
mSchedTime = system_time() + aDelay * 1000; mSchedTime = system_time() + aDelay * 1000;
mThread = PR_GetCurrentThread(); mThread = PR_GetCurrentThread();
sTimerManager.AddRequest(this); sTimerManager.AddRequest(this);
return NS_OK; return NS_OK;
} }
NS_IMPL_ISUPPORTS(nsTimerBeOS, kITimerIID); NS_IMPL_THREADSAFE_ISUPPORTS(nsTimerBeOS, kITimerIID);
void nsTimerBeOS::Cancel() void nsTimerBeOS::Cancel()
{ {
mCanceled = true; if (!mCanceled) {
nsTimerBeOS::sTimerManager.RemoveRequest(this); 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) nsresult NS_NewTimer(nsITimer** aInstancePtrResult)
{ {
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
@ -294,5 +445,5 @@ nsresult NS_NewTimer(nsITimer** aInstancePtrResult)
return timer->QueryInterface(kITimerIID, (void **) aInstancePtrResult); return timer->QueryInterface(kITimerIID, (void **) aInstancePtrResult);
} }
#endif

View File

@ -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 * The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file * 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 * The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are * Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All * Copyright (C) 1998-2001 Netscape Communications Corporation. All
* Rights Reserved. * Rights Reserved.
* *
* Contributor(s): * Contributor(s):
* Yannick Koehler <ykoehler@mythrium.com>
* Chris Seawood <cls@seawood.org>
*/ */
#ifndef __nsTimerBeOS_h #ifndef __nsTimerBeOS_h
@ -38,21 +40,22 @@ public:
~TimerManager(); ~TimerManager();
void AddRequest(nsITimer *inRequest); void AddRequest(nsITimer *inRequest);
bool RemoveRequest(nsITimer *inRequest); bool RemoveRequest(nsITimer *inRequest);
void SortRequests();
private: private:
BLocker mLocker; BLocker mLocker;
sem_id mSyncSem; sem_id mSyncSem;
thread_id mTimerThreadID; thread_id mTimerThreadID;
void *mProcessing;
bool mQuitRequested; bool mQuitRequested;
static int32 sTimerThreadFunc(void *); static int32 sTimerThreadFunc(void *);
int32 TimerThreadFunc(); int32 TimerThreadFunc();
nsITimer *FirstRequest() { return (nsITimer *)FirstItem(); } nsITimer *FirstRequest() { return (nsITimer *)FirstItem(); }
}; };
class nsTimerBeOS : public nsITimer class nsTimerBeOS : public nsITimer
{ {
friend class TimerManager;
public: public:
nsTimerBeOS(); nsTimerBeOS();
@ -88,23 +91,27 @@ public:
NS_IMETHOD_(void) FireTimeout(); NS_IMETHOD_(void) FireTimeout();
bigtime_t mSchedTime; // Time when this request should be done bigtime_t GetSchedTime() { return mSchedTime; }
PRUint32 mDelay; // The delay set in Init() bool IsCanceled() { return mCanceled; }
PRThread* GetThread() { return mThread; }
private: private:
nsresult Init(PRUint32 aDelay); // Initialize the timer. nsresult Init(PRUint32 aDelay, PRUint32 aType); // Initialize the timer.
PRUint32 mPriority; PRUint32 mPriority;
PRUint32 mType; PRUint32 mType;
PRUint32 mDelay; // The delay set in Init()
nsTimerCallbackFunc mFunc; // The function to call back when expired nsTimerCallbackFunc mFunc; // The function to call back when expired
void * mClosure; // The argumnet to pass it. void * mClosure; // The argumnet to pass it.
nsITimerCallback * mCallback; // An interface to notify when expired. nsITimerCallback * mCallback; // An interface to notify when expired.
bool mCanceled; bool mCanceled;
PRThread * mThread; PRThread * mThread;
// PRBool mRepeat; // A repeat, not implemented yet.
bigtime_t mSchedTime; // Time when this request should be done
public: public:
static TimerManager sTimerManager; static TimerManager sTimerManager;
}; };
#endif // __nsTimerBeOS_h #endif // __nsTimerBeOS_h