-//
-// 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 "event_mgr.hxx"
+#include <simgear/math/SGMath.hxx>
#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(const std::string& name, SGCallback* cb,
+ double interval, double delay,
+ bool repeat, bool simtime)
{
+ // Clamp the delay value to 1 usec, so that user code can use
+ // "zero" as a synonym for "next frame".
+ if(delay <= 0) delay = 0.000001;
+
+ SGTimer* t = new SGTimer;
+ t->interval = interval;
+ t->callback = cb;
+ t->repeat = repeat;
+ t->name = name;
+ t->running = false;
+
+ SGTimerQueue* q = simtime ? &_simQueue : &_rtQueue;
+
+ q->insert(t, delay);
}
-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)
+SGTimer::~SGTimer()
{
- if (_initial_value < 0)
- {
- this->run();
- _ms_to_go = _repeat_value;
- }
- else
- {
- _ms_to_go = _initial_value;
- }
+ delete callback;
+ callback = NULL;
}
-SGEvent::SGEvent( const char* name,
- const SGSubsystem* subsystem,
- interval_type repeat_value,
- interval_type initial_value )
- : _name(name),
- _callback(NULL),
- _subsystem((SGSubsystem*)&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)();
}
-
-SGEvent::~SGEvent()
+void SGEventMgr::update(double delta_time_sec)
{
- //delete callback_;
-}
-
-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;
- }
-
- if ( duration > _max_time ) {
- _max_time = duration;
- }
+ _simQueue.update(delta_time_sec);
+
+ double rt = _rtProp ? _rtProp->getDoubleValue() : 0;
+ _rtQueue.update(rt);
}
-void
-SGEvent::print_stats() const
+void SGEventMgr::removeTask(const std::string& name)
{
- 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 );
+ SGTimer* t = _simQueue.findByName(name);
+ if (t) {
+ _simQueue.remove(t);
+ } else if ((t = _rtQueue.findByName(name))) {
+ _rtQueue.remove(t);
+ } else {
+ SG_LOG(SG_GENERAL, SG_WARN, "removeTask: no task found with name:" << name);
+ return;
+ }
+ if (t->running) {
+ // mark as not repeating so that the SGTimerQueue::update()
+ // will clean it up
+ t->repeat = false;
+ } else {
+ delete t;
+ }
}
+////////////////////////////////////////////////////////////////////////
+// SGTimerQueue
+// This is the priority queue implementation:
+////////////////////////////////////////////////////////////////////////
-
-SGEventMgr::SGEventMgr()
+SGTimerQueue::SGTimerQueue(int size)
{
+ _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()
{
+ for(int i=0; i<_numEntries; i++) {
+ delete _table[i].timer;
+ _table[i].timer = 0;
+ }
+ _numEntries = 0;
+ delete[] _table;
+ _table = 0;
+ _tableSize = 0;
}
-void
-SGEventMgr::init()
+void SGTimerQueue::update(double deltaSecs)
{
- SG_LOG( SG_EVENT, SG_INFO, "Initializing event manager" );
-
- event_table.clear();
+ _now += deltaSecs;
+ while(_numEntries && nextTime() <= _now) {
+ SGTimer* t = remove();
+ if(t->repeat)
+ insert(t, t->interval);
+ // warning: this is not thread safe
+ // but the entire timer queue isn't either
+ t->running = true;
+ t->run();
+ t->running = false;
+ if (!t->repeat)
+ delete t;
+ }
}
-void
-SGEventMgr::reinit()
+void SGTimerQueue::insert(SGTimer* timer, double time)
{
-}
+ 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)
- {
- 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;
+void SGTimerQueue::siftDown(int n)
+{
+ // While we have children bigger than us, swap us with the biggest
+ // child.
+ while(lchild(n) < _numEntries) {
+ int bigc = lchild(n);
+ if(rchild(n) < _numEntries && pri(rchild(n)) > pri(bigc))
+ bigc = rchild(n);
+ if(pri(bigc) <= pri(n))
+ break;
+ swap(n, bigc);
+ n = bigc;
}
+}
- 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 );
- }
+void SGTimerQueue::siftUp(int n)
+{
+ while((n != 0) && (_table[n].pri > _table[parent(n)].pri)) {
+ swap(n, parent(n));
+ n = parent(n);
}
+ siftDown(n);
}
-void
-SGEventMgr::add( const SGEvent& event )
+void SGTimerQueue::growArray()
{
- event_table.push_back( event );
-
- SG_LOG( SG_EVENT, SG_INFO, "registered event " << event.name()
- << " to run every " << event.repeat_value() << "ms" );
+ _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;
+ }
+ delete[] _table;
+ _table = newTable;
}
-void
-SGEventMgr::print_stats() const
+SGTimer* SGTimerQueue::findByName(const std::string& name) const
{
- 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();
+ for (int i=0; i < _numEntries; ++i) {
+ if (_table[i].timer->name == name) {
+ return _table[i].timer;
}
-
- SG_LOG( SG_EVENT, SG_INFO, "" );
+ }
+
+ return NULL;
}