-//
-// SGEventMgr.cxx -- Event Manager
-//
-// Written by Bernie Bright, started April 2002.
-//
-// Copyright (C) 2002 Curtis L. Olson - curt@me.umn.edu
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-//
-// $Id$
-
-#ifdef HAVE_CONFIG_H
-# include <simgear_config.h>
-#endif
-
-#include <simgear/compiler.h>
-
-#include <simgear/debug/logstream.hxx>
-
#include "event_mgr.hxx"
-SGEvent::SGEvent()
- : _name(""),
- _callback(0),
- _subsystem(0),
- _repeat_value(0),
- _initial_value(0),
- _ms_to_go(0),
- _cum_time(0),
- _min_time(100000),
- _max_time(0),
- _count(0)
+void SGEventMgr::add(SGCallback* cb,
+ double interval, double delay,
+ bool repeat, bool simtime)
{
-}
+ SGTimer* t = new SGTimer;
+ t->interval = interval;
+ t->callback = cb;
+ t->mgr = this;
+ t->repeat = repeat;
+ t->simtime = simtime;
-SGEvent::SGEvent( const char* name,
- SGCallback* cb,
- interval_type repeat_value,
- interval_type initial_value )
- : _name(name),
- _callback(cb),
- _subsystem(NULL),
- _repeat_value(repeat_value),
- _initial_value(initial_value),
- _cum_time(0),
- _min_time(100000),
- _max_time(0),
- _count(0)
-{
- if (_initial_value < 0)
- {
- this->run();
- _ms_to_go = _repeat_value;
- }
- else
- {
- _ms_to_go = _initial_value;
- }
+ SGTimerQueue* q = simtime ? &_simQueue : &_rtQueue;
+
+ q->insert(t, delay);
}
-SGEvent::SGEvent( const char* name,
- SGSubsystem* subsystem,
- interval_type repeat_value,
- interval_type initial_value )
- : _name(name),
- _callback(NULL),
- _subsystem(subsystem),
- _repeat_value(repeat_value),
- _initial_value(initial_value),
- _cum_time(0),
- _min_time(100000),
- _max_time(0),
- _count(0)
+void SGTimer::run()
{
- if (_initial_value < 0)
- {
- this->run();
- _ms_to_go = _repeat_value;
- }
- else
- {
- _ms_to_go = _initial_value;
+ (*callback)();
+
+ if(repeat) {
+ SGTimerQueue* q = simtime ? &mgr->_simQueue : &mgr->_rtQueue;
+ q->insert(this, interval);
+ } else {
+ delete callback;
+ delete this;
}
}
-
-SGEvent::~SGEvent()
+void SGEventMgr::update(double delta_time_sec)
{
- //delete callback_;
+ _rtQueue.update(delta_time_sec);
+ if(!_freezeProp || _freezeProp->getBoolValue() == false)
+ _simQueue.update(delta_time_sec);
}
-void
-SGEvent::run()
-{
- SGTimeStamp start_time;
- SGTimeStamp finish_time;
-
- start_time.stamp();
-
- // run the event
- if (_callback)
- {
- (*_callback)();
- } else if (_subsystem)
- {
- _subsystem->update(_repeat_value);
- }
-
- finish_time.stamp();
-
- ++_count;
-
- unsigned long duration = finish_time - start_time;
-
- _cum_time += duration;
-
- if ( duration < _min_time ) {
- _min_time = duration;
- }
+////////////////////////////////////////////////////////////////////////
+// SGTimerQueue
+// This is the priority queue implementation:
+////////////////////////////////////////////////////////////////////////
- if ( duration > _max_time ) {
- _max_time = duration;
- }
-}
-
-void
-SGEvent::print_stats() const
+SGTimerQueue::SGTimerQueue(int size)
{
- SG_LOG( SG_EVENT, SG_INFO,
- " " << _name
- << " int=" << _repeat_value / 1000.0
- << " cum=" << _cum_time
- << " min=" << _min_time
- << " max=" << _max_time
- << " count=" << _count
- << " ave=" << _cum_time / (double)_count );
+ _now = 0;
+ _numEntries = 0;
+ _tableSize = 1;
+ while(size > _tableSize)
+ _tableSize = ((_tableSize + 1)<<1) - 1;
+
+ _table = new HeapEntry[_tableSize];
+ for(int i=0; i<_tableSize; i++) {
+ _table[i].pri = 0;
+ _table[i].timer = 0;
+ }
}
-
-SGEventMgr::SGEventMgr()
+SGTimerQueue::~SGTimerQueue()
{
+ delete[] _table;
}
-SGEventMgr::~SGEventMgr()
+void SGTimerQueue::update(double deltaSecs)
{
+ _now += deltaSecs;
+ while(_numEntries && nextTime() <= _now) {
+ SGTimer* t = remove();
+ t->run();
+ }
}
-void
-SGEventMgr::init()
+void SGTimerQueue::insert(SGTimer* timer, double time)
{
- SG_LOG( SG_EVENT, SG_INFO, "Initializing event manager" );
+ if(time < 0) *(int*)0=0;
- event_table.clear();
-}
-
-void
-SGEventMgr::reinit()
-{
-}
+ if(_numEntries >= _tableSize)
+ growArray();
+ _numEntries++;
+ _table[_numEntries-1].pri = -(_now + time);
+ _table[_numEntries-1].timer = timer;
-void
-SGEventMgr::bind()
-{
+ siftUp(_numEntries-1);
}
-void
-SGEventMgr::unbind()
+SGTimer* SGTimerQueue::remove(SGTimer* t)
{
+ int entry;
+ for(entry=0; entry<_numEntries; entry++)
+ if(_table[entry].timer == t)
+ break;
+ if(entry == _numEntries)
+ return 0;
+
+ // Swap in the last item in the table, and sift down
+ swap(entry, _numEntries-1);
+ _numEntries--;
+ siftDown(entry);
+
+ return t;
}
-void
-SGEventMgr::update( double dt )
+SGTimer* SGTimerQueue::remove()
{
- int dt_ms = int(dt * 1000);
-
- if (dt_ms < 0)
- {
- SG_LOG( SG_GENERAL, SG_ALERT,
- "SGEventMgr::update() called with negative delta T" );
- return;
+ if(_numEntries == 0) {
+ return 0;
+ } else if(_numEntries == 1) {
+ _numEntries = 0;
+ return _table[0].timer;
}
- int min_value = 0;
- event_container_type::iterator first = event_table.begin();
- event_container_type::iterator last = event_table.end();
- event_container_type::iterator event = event_table.end();
+ SGTimer *result = _table[0].timer;
+ _table[0] = _table[_numEntries - 1];
+ _numEntries--;
+ siftDown(0);
+ return result;
+}
- // Scan all events. Run one whose interval has expired.
- while (first != last)
+void SGTimerQueue::siftDown(int n)
+{
+ // While we have a child bigger than us:
+ while(((lchild(n) < (_numEntries-1))
+ && (_table[n].pri < _table[lchild(n)].pri))
+ ||
+ ((rchild(n) < (_numEntries-1))
+ && (_table[n].pri < _table[rchild(n)].pri)))
{
- if (first->update( dt_ms ))
- {
- if (first->value() < min_value)
- {
- // Select event with largest negative value.
- // Its been waiting longest.
- min_value = first->value();
- event = first;
- }
- }
- ++first;
- }
-
- if (event != last)
- {
- event->run();
-
- if (event->repeat_value() > 0)
- {
- event->reset();
- }
- else
- {
- SG_LOG( SG_GENERAL, SG_DEBUG, "Deleting event " << event->name() );
- event_table.erase( event );
+ // Swap us with the biggest child
+ if(_table[lchild(n)].pri > _table[rchild(n)].pri) {
+ swap(n, lchild(n));
+ n = lchild(n);
+ } else {
+ swap(n, rchild(n));
+ n = rchild(n);
}
}
}
-void
-SGEventMgr::add( const SGEvent& event )
+void SGTimerQueue::siftUp(int n)
{
- event_table.push_back( event );
-
- SG_LOG( SG_EVENT, SG_INFO, "registered event " << event.name()
- << " to run every " << event.repeat_value() << "ms" );
+ while((n != 0) && (_table[n].pri > _table[parent(n)].pri)) {
+ swap(n, parent(n));
+ n = parent(n);
+ }
+ siftDown(n);
}
-void
-SGEventMgr::print_stats() const
+void SGTimerQueue::growArray()
{
- SG_LOG( SG_EVENT, SG_INFO, "" );
- SG_LOG( SG_EVENT, SG_INFO, "Event Stats" );
- SG_LOG( SG_EVENT, SG_INFO, "-----------" );
-
- event_container_type::const_iterator first = event_table.begin();
- event_container_type::const_iterator last = event_table.end();
- for (; first != last; ++first)
- {
- first->print_stats();
+ _tableSize = ((_tableSize+1)<<1) - 1;
+ HeapEntry *newTable = new HeapEntry[_tableSize];
+ for(int i=0; i<_numEntries; i++) {
+ newTable[i].pri = _table[i].pri;
+ newTable[i].timer = _table[i].timer;
}
-
- SG_LOG( SG_EVENT, SG_INFO, "" );
+ delete[] _table;
+ _table = newTable;
}
-// eventmMgr.hxx -- periodic event scheduler
-//
-// Written by Curtis Olson, started December 1997.
-// Modified by Bernie Bright, April 2002.
-//
-// Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License as
-// published by the Free Software Foundation; either version 2 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-//
-// $Id$
-
-#ifndef SG_EVENT_MGR_HXX
-#define SG_EVENT_MGR_HXX 1
-
-#include <simgear/compiler.h>
-
-#include <simgear/structure/callback.hxx>
-#include <simgear/structure/subsystem_mgr.hxx>
-#include <simgear/timing/timestamp.hxx>
-
-#include <vector>
-#include <string>
-
-SG_USING_STD(vector);
-SG_USING_STD(string);
-
-
-class SGEvent
-{
+#ifndef _SG_EVENT_MGR_HXX
+#define _SG_EVENT_MGR_HXX
-public:
-
- typedef int interval_type;
+#include <simgear/props/props.hxx>
+#include <simgear/structure/subsystem_mgr.hxx>
-private:
+#include "callback.hxx"
- string _name;
- SGCallback* _callback;
- SGSubsystem* _subsystem;
- interval_type _repeat_value;
- interval_type _initial_value;
- int _ms_to_go;
+class SGEventMgr;
- unsigned long _cum_time; // cumulative processor time of this event
- unsigned long _min_time; // time of quickest execution
- unsigned long _max_time; // time of slowest execution
- unsigned long _count; // number of times executed
+struct SGTimer {
+ double interval;
+ SGCallback* callback;
+ SGEventMgr* mgr;
+ bool repeat;
+ bool simtime;
+ void run();
+};
+class SGTimerQueue {
public:
+ SGTimerQueue(int preSize=1);
+ ~SGTimerQueue();
- /**
- *
- */
- SGEvent();
-
- SGEvent( const char* desc,
- SGCallback* cb,
- interval_type repeat_value,
- interval_type initial_value );
-
- SGEvent( const char* desc,
- SGSubsystem* subsystem,
- interval_type repeat_value,
- interval_type initial_value );
-
- /**
- *
- */
- ~SGEvent();
-
- /**
- *
- */
- inline void reset()
- {
- _ms_to_go = _repeat_value;
- }
+ void update(double deltaSecs);
- /**
- * Execute this event's callback.
- */
- void run();
+ double now() { return _now; }
- inline string name() const { return _name; }
- inline interval_type repeat_value() const { return _repeat_value; }
- inline int value() const { return _ms_to_go; }
+ void insert(SGTimer* timer, double time);
+ SGTimer* remove(SGTimer* timer);
+ SGTimer* remove();
- /**
- * Display event statistics.
- */
- void print_stats() const;
+ SGTimer* nextTimer() { return _numEntries ? _table[0].timer : 0; }
+ double nextTime() { return -_table[0].pri; }
- /**
- * Update the elapsed time for this event.
- * @param dt_ms elapsed time in milliseconds.
- * @return true if elapsed time has expired.
- */
- inline bool update( int dt_ms )
- {
- return (_ms_to_go -= dt_ms) <= 0;
+private:
+ // The "priority" is stored as a negative time. This allows the
+ // implemenetation to treat the "top" of the heap as the largest
+ // value and avoids developer mindbugs. ;)
+ struct HeapEntry { double pri; SGTimer* timer; };
+
+ int parent(int n) { return ((n+1)/2) - 1; }
+ int lchild(int n) { return ((n+1)*2) - 1; }
+ int rchild(int n) { return ((n+1)*2 + 1) - 1; }
+ void swap(int a, int b) {
+ HeapEntry tmp = _table[a];
+ _table[a] = _table[b];
+ _table[b] = tmp;
}
+ void siftDown(int n);
+ void siftUp(int n);
+ void growArray();
+ void check();
+
+ double _now;
+ HeapEntry *_table;
+ int _numEntries;
+ int _tableSize;
};
-
class SGEventMgr : public SGSubsystem
{
-private:
-
- typedef SGEvent::interval_type interval_type;
- typedef vector< SGEvent > event_container_type;
-
- void add( const SGEvent& event );
-
- // registered events.
- event_container_type event_table;
+public:
+ SGEventMgr() { _freezeProp = 0; }
+ virtual void init() {}
+ virtual void update(double delta_time_sec);
-public:
- SGEventMgr();
- ~SGEventMgr();
+ void setFreezeProperty(SGPropertyNode* node) { _freezeProp = node; }
/**
- * Initialize the scheduling subsystem.
- */
- void init();
- void reinit();
- void bind();
- void unbind();
-
- /*
- * Update the elapsed time for all events.
- * @param dt elapsed time in seconds.
+ * Add a single function callback event as a repeating task.
+ * ex: addTask("foo", &Function ... )
*/
- void update( double dt );
+ template<typename FUNC>
+ inline void addTask(const char* name, const FUNC& f,
+ double interval, double delay=0, bool sim=false)
+ { add(make_callback(f), interval, delay, true, sim); }
/**
- * register a free standing function to be executed some time in the future.
- * @param desc A brief description of this callback for logging.
- * @param cb The callback function to be executed.
- * @param repeat_value repetition rate in milliseconds.
- * @param initial_value initial delay value in milliseconds. A value of
- * -1 means run immediately.
+ * Add a single function callback event as a one-shot event.
+ * ex: addEvent("foo", &Function ... )
*/
- template< typename Fun >
- inline void add( const char* name,
- const Fun& f,
- interval_type repeat_value,
- interval_type initial_value = -1 )
- {
- this->add( SGEvent( name,
- make_callback(f),
- repeat_value,
- initial_value ) );
- }
+ template<typename FUNC>
+ inline void addEvent(const char* name, const FUNC& f,
+ double delay, bool sim=false)
+ { add(make_callback(f), 0, delay, false, sim); }
/**
- * register a subsystem of which the update function will be executed some
- * time in the future.
- * @param desc A brief description of this callback for logging.
- * @param subsystem The subsystem of which the update function will be
- * executed.
- * @param repeat_value repetition rate in milliseconds.
- * @param initial_value initial delay value in milliseconds. A value of
- * -1 means run immediately.
+ * Add a object/method pair as a repeating task.
+ * ex: addTask("foo", &object, &ClassName::Method, ...)
*/
- inline void add( const char* name,
- SGSubsystem* subsystem,
- interval_type repeat_value,
- interval_type initial_value = -1 )
- {
- this->add( SGEvent( name,
- subsystem,
- repeat_value,
- initial_value ) );
- }
-
- template< class ObjPtr, typename MemFn >
- inline void add( const char* name,
- const ObjPtr& p,
- MemFn pmf,
- interval_type repeat_value,
- interval_type initial_value = -1 )
- {
- this->add( SGEvent( name,
- make_callback(p,pmf),
- repeat_value,
- initial_value ) );
- }
+ template<class OBJ, typename METHOD>
+ inline void addTask(const char* name,
+ const OBJ& o, METHOD m,
+ double interval, double delay=0, bool sim=false)
+ { add(make_callback(o,m), interval, delay, true, sim); }
/**
- * Display statistics for all registered events.
+ * Add a object/method pair as a repeating task.
+ * ex: addEvent("foo", &object, &ClassName::Method, ...)
*/
- void print_stats() const;
-};
+ template<class OBJ, typename METHOD>
+ inline void addEvent(const char* name,
+ const OBJ& o, METHOD m,
+ double delay, bool sim=false)
+ { add(make_callback(o,m), 0, delay, false, sim); }
+
+private:
+ friend class SGTimer;
+
+ void add(SGCallback* cb,
+ double interval, double delay,
+ bool repeat, bool simtime);
+ SGPropertyNode* _freezeProp;
+ SGTimerQueue _rtQueue;
+ SGTimerQueue _simQueue;
+};
-#endif //SG_EVENT_MGR_HXX
+#endif // _SG_EVENT_MGR_HXX