1 // Written by David Megginson, started 2000-12
3 // Copyright (C) 2000 David Megginson, david@megginson.com
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 # include <simgear_config.h>
25 #include <simgear/debug/logstream.hxx>
26 #include <simgear/timing/timestamp.hxx>
28 #include "exception.hxx"
29 #include "subsystem_mgr.hxx"
31 #include <simgear/math/SGMath.hxx>
32 #include "SGSmplstat.hxx"
34 const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
\f
35 ////////////////////////////////////////////////////////////////////////
36 // Implementation of SGSubsystem
37 ////////////////////////////////////////////////////////////////////////
40 SGSubsystem::SGSubsystem ()
45 SGSubsystem::~SGSubsystem ()
55 SGSubsystem::postinit ()
60 SGSubsystem::reinit ()
65 SGSubsystem::shutdown ()
75 SGSubsystem::unbind ()
80 SGSubsystem::suspend ()
86 SGSubsystem::suspend (bool suspended)
88 _suspended = suspended;
92 SGSubsystem::resume ()
98 SGSubsystem::is_suspended () const
105 SGSubsystem::printTimingInformation ()
107 SGTimeStamp startTime;
108 for ( eventTimeVecIterator i = timingInfo.begin();
109 i != timingInfo.end();
111 if (i == timingInfo.begin()) {
112 startTime = i->getTime();
114 SGTimeStamp endTime = i->getTime();
115 SG_LOG(SG_GENERAL, SG_ALERT, "- Getting to timestamp : "
116 << i->getName() << " takes " << endTime - startTime
125 void SGSubsystem::stamp(const string& name)
127 timingInfo.push_back(TimingInfo(name, SGTimeStamp::now()));
131 ////////////////////////////////////////////////////////////////////////
132 // Implementation of SGSubsystemGroup.
133 ////////////////////////////////////////////////////////////////////////
135 class SGSubsystemGroup::Member
138 Member (const Member &member);
143 virtual void update (double delta_time_sec);
144 void printTimingInformation(double time);
145 void printTimingStatistics(double minMaxTime=0.0,double minJitter=0.0);
146 void updateExecutionTime(double time);
147 double getTimeWarningThreshold();
148 void collectDebugTiming (bool collect) { collectTimeStats = collect; };
150 SampleStatistic timeStat;
152 SGSubsystem * subsystem;
155 bool collectTimeStats;
161 SGSubsystemGroup::SGSubsystemGroup () :
162 _fixedUpdateTime(-1.0),
163 _updateTimeRemainder(0.0)
167 SGSubsystemGroup::~SGSubsystemGroup ()
169 printTimingStatistics();
171 // reverse order to prevent order dependency problems
172 for (unsigned int i = _members.size(); i > 0; i--)
174 delete _members[i-1];
179 SGSubsystemGroup::init ()
181 for (unsigned int i = 0; i < _members.size(); i++)
182 _members[i]->subsystem->init();
186 SGSubsystemGroup::postinit ()
188 for (unsigned int i = 0; i < _members.size(); i++)
189 _members[i]->subsystem->postinit();
193 SGSubsystemGroup::reinit ()
195 for (unsigned int i = 0; i < _members.size(); i++)
196 _members[i]->subsystem->reinit();
200 SGSubsystemGroup::shutdown ()
202 // reverse order to prevent order dependency problems
203 for (unsigned int i = _members.size(); i > 0; i--)
204 _members[i-1]->subsystem->shutdown();
208 SGSubsystemGroup::bind ()
210 for (unsigned int i = 0; i < _members.size(); i++)
211 _members[i]->subsystem->bind();
215 SGSubsystemGroup::unbind ()
217 // reverse order to prevent order dependency problems
218 for (unsigned int i = _members.size(); i > 0; i--)
219 _members[i-1]->subsystem->unbind();
223 SGSubsystemGroup::update (double delta_time_sec)
226 // if dt == 0.0, we are paused, so we need to run one iteration
227 // of our members; if we have a fixed update time, we compute a
228 // loop count, and locally adjust dt
229 if ((delta_time_sec > 0.0) && (_fixedUpdateTime > 0.0)) {
230 double localDelta = delta_time_sec + _updateTimeRemainder;
231 loopCount = SGMiscd::roundToInt(localDelta / _fixedUpdateTime);
232 _updateTimeRemainder = delta_time_sec - (loopCount * _fixedUpdateTime);
233 delta_time_sec = _fixedUpdateTime;
236 while (loopCount-- > 0) {
237 for (unsigned int i = 0; i < _members.size(); i++)
239 SGTimeStamp timeStamp = SGTimeStamp::now();
240 _members[i]->update(delta_time_sec); // indirect call
241 timeStamp = SGTimeStamp::now() - timeStamp;
242 double b = timeStamp.toUSecs();
243 _members[i]->updateExecutionTime(b);
244 double threshold = _members[i]->getTimeWarningThreshold();
245 if (( b > threshold ) && (b > 10000)) {
246 _members[i]->printTimingInformation(b);
249 } // of multiple update loop
253 SGSubsystemGroup::collectDebugTiming(bool collect)
255 for (unsigned int i = 0; i < _members.size(); i++)
257 _members[i]->collectDebugTiming(collect);
262 SGSubsystemGroup::printTimingStatistics(double minMaxTime,double minJitter)
264 for (unsigned int i = _members.size(); i > 0; i--)
266 _members[i-1]->printTimingStatistics(minMaxTime, minJitter);
267 _members[i-1]->timeStat.reset();
272 SGSubsystemGroup::suspend ()
274 for (unsigned int i = 0; i < _members.size(); i++)
275 _members[i]->subsystem->suspend();
279 SGSubsystemGroup::resume ()
281 for (unsigned int i = 0; i < _members.size(); i++)
282 _members[i]->subsystem->resume();
286 SGSubsystemGroup::is_suspended () const
292 SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
295 Member * member = get_member(name, true);
296 if (member->subsystem != 0)
297 delete member->subsystem;
299 member->subsystem = subsystem;
300 member->min_step_sec = min_step_sec;
304 SGSubsystemGroup::get_subsystem (const string &name)
306 Member * member = get_member(name);
308 return member->subsystem;
314 SGSubsystemGroup::remove_subsystem (const string &name)
316 for (unsigned int i = 0; i < _members.size(); i++) {
317 if (name == _members[i]->name) {
318 _members.erase(_members.begin() + i);
325 SGSubsystemGroup::set_fixed_update_time(double dt)
327 _fixedUpdateTime = dt;
331 * Print timing statistics.
332 * Only show data if jitter exceeds minJitter or
333 * maximum time exceeds minMaxTime.
336 SGSubsystemGroup::Member::printTimingStatistics(double minMaxTime,double minJitter)
338 if (collectTimeStats) {
339 double minTime = timeStat.min() / 1000;
340 double maxTime = timeStat.max() / 1000;
341 double meanTime = timeStat.mean() / 1000;
342 double stddev = timeStat.stdDev() / 1000;
344 if ((maxTime - minTime >= minJitter)||
345 (maxTime >= minMaxTime))
348 snprintf(buffer, 256, "Timing summary for %20s.\n"
349 "- mean time: %04.2f ms.\n"
350 "- min time : %04.2f ms.\n"
351 "- max time : %04.2f ms.\n"
352 "- stddev : %04.2f ms.\n", name.c_str(), meanTime, minTime, maxTime, stddev);
353 SG_LOG(SG_GENERAL, SG_ALERT, buffer);
360 SGSubsystemGroup::has_subsystem (const string &name) const
362 return (((SGSubsystemGroup *)this)->get_member(name) != 0);
365 SGSubsystemGroup::Member *
366 SGSubsystemGroup::get_member (const string &name, bool create)
368 for (unsigned int i = 0; i < _members.size(); i++) {
369 if (_members[i]->name == name)
373 Member * member = new Member;
374 _members.push_back(member);
383 ////////////////////////////////////////////////////////////////////////
384 // Implementation of SGSubsystemGroup::Member
385 ////////////////////////////////////////////////////////////////////////
388 SGSubsystemGroup::Member::Member ()
393 collectTimeStats(false),
398 // This shouldn't be called due to subsystem pointer ownership issues.
399 SGSubsystemGroup::Member::Member (const Member &)
403 SGSubsystemGroup::Member::~Member ()
409 SGSubsystemGroup::Member::update (double delta_time_sec)
411 elapsed_sec += delta_time_sec;
412 if (elapsed_sec < min_step_sec) {
416 if (subsystem->is_suspended()) {
421 subsystem->update(elapsed_sec);
423 } catch (sg_exception& e) {
424 SG_LOG(SG_GENERAL, SG_ALERT, "caught exception processing subsystem:" << name
425 << "\nmessage:" << e.getMessage());
427 if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
428 SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
430 subsystem->suspend();
437 SGSubsystemGroup::Member::printTimingInformation(double time)
439 if (collectTimeStats) {
440 SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem Timing Alert, subsystem \"" << name << "\": " << time/1000.0 << "ms");
441 subsystem->printTimingInformation();
445 double SGSubsystemGroup::Member::getTimeWarningThreshold()
447 return (timeStat.mean() + 3 * timeStat.stdDev());
450 void SGSubsystemGroup::Member::updateExecutionTime(double time)
452 if (collectTimeStats) {
461 ////////////////////////////////////////////////////////////////////////
462 // Implementation of SGSubsystemMgr.
463 ////////////////////////////////////////////////////////////////////////
466 SGSubsystemMgr::SGSubsystemMgr ()
468 for (int i = 0; i < MAX_GROUPS; i++) {
469 _groups[i] = new SGSubsystemGroup;
473 SGSubsystemMgr::~SGSubsystemMgr ()
475 // ensure get_subsystem returns NULL from now onwards,
476 // before the SGSubsystemGroup destructors are run
477 _subsystem_map.clear();
479 for (int i = 0; i < MAX_GROUPS; i++) {
485 SGSubsystemMgr::init ()
487 for (int i = 0; i < MAX_GROUPS; i++)
492 SGSubsystemMgr::postinit ()
494 for (int i = 0; i < MAX_GROUPS; i++)
495 _groups[i]->postinit();
499 SGSubsystemMgr::reinit ()
501 for (int i = 0; i < MAX_GROUPS; i++)
502 _groups[i]->reinit();
506 SGSubsystemMgr::shutdown ()
508 // reverse order to prevent order dependency problems
509 for (int i = MAX_GROUPS-1; i >= 0; i--)
510 _groups[i]->shutdown();
515 SGSubsystemMgr::bind ()
517 for (int i = 0; i < MAX_GROUPS; i++)
522 SGSubsystemMgr::unbind ()
524 // reverse order to prevent order dependency problems
525 for (int i = MAX_GROUPS-1; i >= 0; i--)
526 _groups[i]->unbind();
530 SGSubsystemMgr::update (double delta_time_sec)
532 for (int i = 0; i < MAX_GROUPS; i++) {
533 _groups[i]->update(delta_time_sec);
538 SGSubsystemMgr::collectDebugTiming(bool collect)
540 for (int i = 0; i < MAX_GROUPS; i++) {
541 _groups[i]->collectDebugTiming(collect);
546 SGSubsystemMgr::suspend ()
548 for (int i = 0; i < MAX_GROUPS; i++)
549 _groups[i]->suspend();
553 SGSubsystemMgr::resume ()
555 for (int i = 0; i < MAX_GROUPS; i++)
556 _groups[i]->resume();
560 SGSubsystemMgr::is_suspended () const
566 SGSubsystemMgr::add (const char * name, SGSubsystem * subsystem,
567 GroupType group, double min_time_sec)
569 SG_LOG(SG_GENERAL, SG_INFO, "Adding subsystem " << name);
570 get_group(group)->set_subsystem(name, subsystem, min_time_sec);
572 if (_subsystem_map.find(name) != _subsystem_map.end()) {
573 SG_LOG(SG_GENERAL, SG_ALERT, "Adding duplicate subsystem " << name);
574 throw sg_exception("duplicate subsystem");
576 _subsystem_map[name] = subsystem;
580 SGSubsystemMgr::remove(const char* name)
582 SubsystemDict::iterator s =_subsystem_map.find(name);
583 if (s == _subsystem_map.end()) {
587 SGSubsystem* sub = s->second;
588 _subsystem_map.erase(s);
590 // tedious part - we don't know which group the subsystem belongs too
591 for (int i = 0; i < MAX_GROUPS; i++) {
592 if (_groups[i]->get_subsystem(name) == sub) {
593 _groups[i]->remove_subsystem(name);
596 } // of groups iteration
603 SGSubsystemMgr::get_group (GroupType group)
605 return _groups[group];
609 SGSubsystemMgr::get_subsystem (const string &name) const
611 SubsystemDict::const_iterator s =_subsystem_map.find(name);
613 if (s == _subsystem_map.end())
620 SGSubsystemMgr::printTimingStatistics(double minMaxTime,double minJitter)
622 for (int i = 0; i < MAX_GROUPS; i++) {
623 _groups[i]->printTimingStatistics(minMaxTime, minJitter);
624 } // of groups iteration
627 // end of subsystem_mgr.cxx