]> git.mxchange.org Git - simgear.git/blobdiff - simgear/structure/event_mgr.cxx
performance monitor: start measurement interval with a fresh timestamp.
[simgear.git] / simgear / structure / event_mgr.cxx
index 979420d71c7f4ee9b1c39bab06b167adbbf58b30..f448a6495bd063c951b357e070c730cee29ce878 100644 (file)
-//
-// 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_;
+    _simQueue.update(delta_time_sec);
+    
+    double rt = _rtProp ? _rtProp->getDoubleValue() : 0;
+    _rtQueue.update(rt);
 }
 
-void
-SGEvent::run()
+void SGEventMgr::removeTask(const std::string& name)
 {
-    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;
-    }
+  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;
+  }
 }
 
-void
-SGEvent::print_stats() const
-{
-    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 );
-}
-
-
+////////////////////////////////////////////////////////////////////////
+// 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;
 }