Finished AIFrameTimer.
Added documentation. Added AIFrameTimer::isRunning. Fixed a bug: mCallback was deleted before it was used.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user