]> git.mxchange.org Git - simgear.git/blob - simgear/structure/event_mgr.cxx
Csaba Halasz: fix SGTimerQueue so tasks can remove themselves properly
[simgear.git] / simgear / structure / event_mgr.cxx
1 #include "event_mgr.hxx"
2
3 #include <simgear/math/SGMath.hxx>
4 #include <simgear/debug/logstream.hxx>
5
6 void SGEventMgr::add(const std::string& name, SGCallback* cb,
7                      double interval, double delay,
8                      bool repeat, bool simtime)
9 {
10     // Clamp the delay value to 1 usec, so that user code can use
11     // "zero" as a synonym for "next frame".
12     if(delay <= 0) delay = 0.000001;
13
14     SGTimer* t = new SGTimer;
15     t->interval = interval;
16     t->callback = cb;
17     t->repeat = repeat;
18     t->name = name;
19     t->running = false;
20     
21     SGTimerQueue* q = simtime ? &_simQueue : &_rtQueue;
22
23     q->insert(t, delay);
24 }
25
26 SGTimer::~SGTimer()
27 {
28     delete callback;
29     callback = NULL;
30 }
31
32 void SGTimer::run()
33 {
34     (*callback)();
35 }
36
37 void SGEventMgr::update(double delta_time_sec)
38 {
39     _simQueue.update(delta_time_sec);
40     
41     double rt = _rtProp ? _rtProp->getDoubleValue() : 0;
42     _rtQueue.update(rt);
43 }
44
45 void SGEventMgr::removeTask(const std::string& name)
46 {
47   SGTimer* t = _simQueue.findByName(name);
48   if (t) {
49     _simQueue.remove(t);
50   } else if ((t = _rtQueue.findByName(name))) {
51     _rtQueue.remove(t);
52   } else {
53     SG_LOG(SG_GENERAL, SG_WARN, "removeTask: no task found with name:" << name);
54     return;
55   }
56   if (t->running) {
57     // mark as not repeating so that the SGTimerQueue::update()
58     // will clean it up
59     t->repeat = false;
60   } else {
61     delete t;
62   }
63 }
64
65 ////////////////////////////////////////////////////////////////////////
66 // SGTimerQueue
67 // This is the priority queue implementation:
68 ////////////////////////////////////////////////////////////////////////
69
70 SGTimerQueue::SGTimerQueue(int size)
71 {
72     _now = 0;
73     _numEntries = 0;
74     _tableSize = 1;
75     while(size > _tableSize)
76         _tableSize = ((_tableSize + 1)<<1) - 1;
77
78     _table = new HeapEntry[_tableSize];
79     for(int i=0; i<_tableSize; i++) {
80         _table[i].pri = 0;
81         _table[i].timer = 0;
82     }
83 }
84
85
86 SGTimerQueue::~SGTimerQueue()
87 {
88     for(int i=0; i<_numEntries; i++) {
89         delete _table[i].timer;
90         _table[i].timer = 0;
91     }
92     _numEntries = 0;
93     delete[] _table;
94     _table = 0;
95     _tableSize = 0;
96 }
97
98 void SGTimerQueue::update(double deltaSecs)
99 {
100     _now += deltaSecs;
101     while(_numEntries && nextTime() <= _now) {
102         SGTimer* t = remove();
103         if(t->repeat)
104             insert(t, t->interval);
105         // warning: this is not thread safe
106         // but the entire timer queue isn't either
107         t->running = true;
108         t->run();
109         t->running = false;
110         if (!t->repeat)
111             delete t;
112     }
113 }
114
115 void SGTimerQueue::insert(SGTimer* timer, double time)
116 {
117     if(_numEntries >= _tableSize)
118         growArray();
119
120     _numEntries++;
121     _table[_numEntries-1].pri = -(_now + time);
122     _table[_numEntries-1].timer = timer;
123
124     siftUp(_numEntries-1);
125 }
126
127 SGTimer* SGTimerQueue::remove(SGTimer* t)
128 {
129     int entry;
130     for(entry=0; entry<_numEntries; entry++)
131         if(_table[entry].timer == t)
132             break;
133     if(entry == _numEntries)
134         return 0;
135
136     // Swap in the last item in the table, and sift down
137     swap(entry, _numEntries-1);
138     _numEntries--;
139     siftDown(entry);
140
141     return t;
142 }
143
144 SGTimer* SGTimerQueue::remove()
145 {
146     if(_numEntries == 0) {
147         return 0;
148     } else if(_numEntries == 1) {
149         _numEntries = 0;
150         return _table[0].timer;
151     }
152
153     SGTimer *result = _table[0].timer;
154     _table[0] = _table[_numEntries - 1];
155     _numEntries--;
156     siftDown(0);
157     return result;
158 }
159
160 void SGTimerQueue::siftDown(int n)
161 {
162     // While we have children bigger than us, swap us with the biggest
163     // child.
164     while(lchild(n) < _numEntries) {
165         int bigc = lchild(n);
166         if(rchild(n) < _numEntries && pri(rchild(n)) > pri(bigc))
167             bigc = rchild(n);
168         if(pri(bigc) <= pri(n))
169             break;
170         swap(n, bigc);
171         n = bigc;
172     }
173 }
174
175 void SGTimerQueue::siftUp(int n)
176 {
177     while((n != 0) && (_table[n].pri > _table[parent(n)].pri)) {
178         swap(n, parent(n));
179         n = parent(n);
180     }
181     siftDown(n);
182 }
183
184 void SGTimerQueue::growArray()
185 {
186     _tableSize = ((_tableSize+1)<<1) - 1;
187     HeapEntry *newTable = new HeapEntry[_tableSize];
188     for(int i=0; i<_numEntries; i++) {
189         newTable[i].pri  = _table[i].pri;
190         newTable[i].timer = _table[i].timer;
191     }
192     delete[] _table;
193     _table = newTable;
194 }
195
196 SGTimer* SGTimerQueue::findByName(const std::string& name) const
197 {
198   for (int i=0; i < _numEntries; ++i) {
199     if (_table[i].timer->name == name) {
200       return _table[i].timer;
201     }
202   }
203   
204   return NULL;
205 }