Finished AIFrameTimer.

Added documentation.
Added AIFrameTimer::isRunning.
Fixed a bug: mCallback was deleted before it was used.
This commit is contained in:
Aleric Inglewood
2012-02-10 01:37:43 +01:00
parent cad0597524
commit d4591828c8
2 changed files with 62 additions and 13 deletions

View File

@@ -27,8 +27,38 @@
* - Initial version, written by Aleric Inglewood @ SL
*/
#include "linden_common.h"
// An AIFrameTimer object provides a callback API for timer events.
//
// Typical usage:
//
// // Any thread.
// AIFrameTimer timer;
//
// ...
// // Any thread (after successful construction is guaranteed).
// timer.create(5.5, boost::bind(&the_callback, <optional params>)); // Call the_callback(<optional params>) in 5.5 seconds.
//
// The callback function is always called by the main thread and should therefore
// be light weight.
//
// If timer.cancel() is called before the timer expires, then the callback
// function isn't called. If cancel() is called by another thread than the
// main thread, then it is possible that the callback function is called
// even while still inside cancel(), but as soon as cancel() returned it
// is guarenteed that the callback function won't be called anymore.
// Hence, if the callback function is a member of some object then
// cancel() must be called before the destruction of that object (ie from
// it's destructor). Calling cancel() multiple times is not a problem.
// Note: if cancel() is called while the callback function is being
// called then cancel() will block until the callback function returned.
//
// The timer object can be reused (by calling create() again), but
// only after either the callback function was called, or after cancel()
// returned. Most notably, you can call create() again from inside the
// callback function to "restart" the timer.
//
#include "linden_common.h"
#include "aiframetimer.h"
static F64 const NEVER = 1e16; // 317 million years.
@@ -115,7 +145,7 @@ void AIFrameTimer::handleExpiration(F64 current_frame_time)
// function here because the trylock fails.
//
// Assuming the main thread arrived here, there are two possible states
// for the other thread that tries to delete the call back function,
// for the other thread that tries to delete the callback function,
// right after calling the cancel() function too:
//
// 1. It hasn't obtained the first lock yet, we obtain the handle.mMutex

View File

@@ -61,20 +61,37 @@ class LL_COMMON_API AIFrameTimer
// See aiframetimer.cpp for more notes.
class AIRunningFrameTimer {
private:
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
Signal* mCallback;
AIFrameTimer* mTimer;
F64 mExpire; // Time at which the timer expires, in seconds since application start (compared to LLFrameTimer::sFrameTime).
AIFrameTimer* mTimer; // The actual timer.
// Can be mutable, since only the mExpire is used for ordering this object in the multiset AIFrameTimer::sTimerList.
mutable Signal* mCallback; // Pointer to callback struct, or NULL when the object wasn't added to sTimerList yet.
public:
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(new Signal), mTimer(timer) { }
AIRunningFrameTimer(F64 expiration, AIFrameTimer* timer) : mExpire(LLFrameTimer::getElapsedSeconds() + expiration), mCallback(NULL), mTimer(timer) { }
~AIRunningFrameTimer() { delete mCallback; }
void init(signal_type::slot_type const& slot) const { mCallback->mSignal.connect(slot); }
// This function is called after the final object was added to sTimerList (where it is initialized in-place).
void init(signal_type::slot_type const& slot) const
{
// We may only call init() once.
llassert(!mCallback);
mCallback = new Signal;
mCallback->mSignal.connect(slot);
}
// Order AIFrameTimer::sTimerList so that the timer that expires first is up front.
friend bool operator<(AIRunningFrameTimer const& ft1, AIRunningFrameTimer const& ft2) { return ft1.mExpire < ft2.mExpire; }
void do_callback(void) const { mCallback->mSignal(); }
F64 expiration(void) const { return mExpire; }
AIFrameTimer* getTimer(void) const { return mTimer; }
#if LL_DEBUG
// May not copy this object after it was initialized.
AIRunningFrameTimer(AIRunningFrameTimer const& running_frame_timer) :
mExpire(running_frame_timer.mExpire), mCallback(running_frame_timer.mCallback), mTimer(running_frame_timer.mTimer)
{ llassert(!mCallback); }
#endif
};
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
@@ -98,12 +115,12 @@ class LL_COMMON_API AIFrameTimer
// Actual initialization used by AIFrameTimer::create.
void init(timer_list_type::iterator const& running_timer, signal_type::slot_type const& slot)
{
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
// the object and no other thread knows of mRunningTimer at this point.
mRunningTimer = running_timer;
mRunningTimer->init(slot);
}
{
// Locking AIFrameTimer::sMutex is not neccessary here, because we're creating
// the object and no other thread knows of mRunningTimer at this point.
mRunningTimer = running_timer;
mRunningTimer->init(slot);
}
private:
// LLMutex has no assignment operator.
@@ -129,6 +146,8 @@ class LL_COMMON_API AIFrameTimer
void create(F64 expiration, signal_type::slot_type const& slot);
void cancel(void);
bool isRunning(void) const { bool running; sMutex.lock(); running = mHandle.mRunningTimer != sTimerList.end(); sMutex.unlock(); return running; }
protected:
static void handleExpiration(F64 current_frame_time);
};