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.
21 #include <simgear/debug/logstream.hxx>
22 #include <simgear/timing/timestamp.hxx>
24 #include "exception.hxx"
25 #include "subsystem_mgr.hxx"
27 #include <simgear/math/SGMath.hxx>
30 const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
\f
31 ////////////////////////////////////////////////////////////////////////
32 // Implementation of SGSubsystem
33 ////////////////////////////////////////////////////////////////////////
36 SGSubsystem::SGSubsystem ()
41 SGSubsystem::~SGSubsystem ()
51 SGSubsystem::postinit ()
56 SGSubsystem::reinit ()
61 SGSubsystem::shutdown ()
71 SGSubsystem::unbind ()
76 SGSubsystem::suspend ()
82 SGSubsystem::suspend (bool suspended)
84 _suspended = suspended;
88 SGSubsystem::resume ()
94 SGSubsystem::is_suspended () const
101 SGSubsystem::printTimingInformation ()
103 SGTimeStamp startTime;
104 for ( eventTimeVecIterator i = timingInfo.begin();
105 i != timingInfo.end();
107 if (i == timingInfo.begin()) {
108 startTime = i->getTime();
110 SGTimeStamp endTime = i->getTime();
111 SG_LOG(SG_GENERAL, SG_ALERT, "- Getting to timestamp : "
112 << i->getName() << " takes " << endTime - startTime
121 void SGSubsystem::stamp(const string& name)
123 timingInfo.push_back(TimingInfo(name, SGTimeStamp::now()));
127 ////////////////////////////////////////////////////////////////////////
128 // Implementation of SGSubsystemGroup.
129 ////////////////////////////////////////////////////////////////////////
131 SGSubsystemGroup::SGSubsystemGroup () :
132 _fixedUpdateTime(-1.0),
133 _updateTimeRemainder(0.0)
137 SGSubsystemGroup::~SGSubsystemGroup ()
139 printTimingStatistics();
141 // reverse order to prevent order dependency problems
142 for (unsigned int i = _members.size(); i > 0; i--)
144 delete _members[i-1];
149 SGSubsystemGroup::init ()
151 for (unsigned int i = 0; i < _members.size(); i++)
152 _members[i]->subsystem->init();
156 SGSubsystemGroup::postinit ()
158 for (unsigned int i = 0; i < _members.size(); i++)
159 _members[i]->subsystem->postinit();
163 SGSubsystemGroup::reinit ()
165 for (unsigned int i = 0; i < _members.size(); i++)
166 _members[i]->subsystem->reinit();
170 SGSubsystemGroup::shutdown ()
172 // reverse order to prevent order dependency problems
173 for (unsigned int i = _members.size(); i > 0; i--)
174 _members[i-1]->subsystem->shutdown();
178 SGSubsystemGroup::bind ()
180 for (unsigned int i = 0; i < _members.size(); i++)
181 _members[i]->subsystem->bind();
185 SGSubsystemGroup::unbind ()
187 // reverse order to prevent order dependency problems
188 for (unsigned int i = _members.size(); i > 0; i--)
189 _members[i-1]->subsystem->unbind();
193 SGSubsystemGroup::update (double delta_time_sec)
196 // if dt == 0.0, we are paused, so we need to run one iteration
197 // of our members; if we have a fixed update time, we compute a
198 // loop count, and locally adjust dt
199 if ((delta_time_sec > 0.0) && (_fixedUpdateTime > 0.0)) {
200 double localDelta = delta_time_sec + _updateTimeRemainder;
201 loopCount = SGMiscd::roundToInt(localDelta / _fixedUpdateTime);
202 _updateTimeRemainder = delta_time_sec - (loopCount * _fixedUpdateTime);
203 delta_time_sec = _fixedUpdateTime;
206 while (loopCount-- > 0) {
207 for (unsigned int i = 0; i < _members.size(); i++)
209 SGTimeStamp timeStamp = SGTimeStamp::now();
210 _members[i]->update(delta_time_sec); // indirect call
211 timeStamp = SGTimeStamp::now() - timeStamp;
212 double b = timeStamp.toUSecs();
213 _members[i]->updateExecutionTime(b);
214 double threshold = _members[i]->getTimeWarningThreshold();
215 if (( b > threshold ) && (b > 10000)) {
216 _members[i]->printTimingInformation(b);
219 } // of multiple update loop
223 SGSubsystemGroup::collectDebugTiming(bool collect)
225 for (unsigned int i = 0; i < _members.size(); i++)
227 _members[i]->collectDebugTiming(collect);
232 SGSubsystemGroup::printTimingStatistics(double minMaxTime,double minJitter)
234 for (unsigned int i = _members.size(); i > 0; i--)
236 _members[i-1]->printTimingStatistics(minMaxTime, minJitter);
237 _members[i-1]->timeStat.reset();
242 SGSubsystemGroup::suspend ()
244 for (unsigned int i = 0; i < _members.size(); i++)
245 _members[i]->subsystem->suspend();
249 SGSubsystemGroup::resume ()
251 for (unsigned int i = 0; i < _members.size(); i++)
252 _members[i]->subsystem->resume();
256 SGSubsystemGroup::is_suspended () const
262 SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
265 Member * member = get_member(name, true);
266 if (member->subsystem != 0)
267 delete member->subsystem;
269 member->subsystem = subsystem;
270 member->min_step_sec = min_step_sec;
274 SGSubsystemGroup::get_subsystem (const string &name)
276 Member * member = get_member(name);
278 return member->subsystem;
284 SGSubsystemGroup::remove_subsystem (const string &name)
286 for (unsigned int i = 0; i < _members.size(); i++) {
287 if (name == _members[i]->name) {
288 _members.erase(_members.begin() + i);
295 SGSubsystemGroup::set_fixed_update_time(double dt)
297 _fixedUpdateTime = dt;
301 * Print timing statistics.
302 * Only show data if jitter exceeds minJitter or
303 * maximum time exceeds minMaxTime.
306 SGSubsystemGroup::Member::printTimingStatistics(double minMaxTime,double minJitter)
308 if (collectTimeStats) {
309 double minTime = timeStat.min() / 1000;
310 double maxTime = timeStat.max() / 1000;
311 double meanTime = timeStat.mean() / 1000;
312 double stddev = timeStat.stdDev() / 1000;
314 if ((maxTime - minTime >= minJitter)||
315 (maxTime >= minMaxTime))
318 snprintf(buffer, 256, "Timing summary for %20s.\n"
319 "- mean time: %04.2f ms.\n"
320 "- min time : %04.2f ms.\n"
321 "- max time : %04.2f ms.\n"
322 "- stddev : %04.2f ms.\n", name.c_str(), meanTime, minTime, maxTime, stddev);
323 SG_LOG(SG_GENERAL, SG_ALERT, buffer);
330 SGSubsystemGroup::has_subsystem (const string &name) const
332 return (((SGSubsystemGroup *)this)->get_member(name) != 0);
335 SGSubsystemGroup::Member *
336 SGSubsystemGroup::get_member (const string &name, bool create)
338 for (unsigned int i = 0; i < _members.size(); i++) {
339 if (_members[i]->name == name)
343 Member * member = new Member;
344 _members.push_back(member);
353 ////////////////////////////////////////////////////////////////////////
354 // Implementation of SGSubsystemGroup::Member
355 ////////////////////////////////////////////////////////////////////////
358 SGSubsystemGroup::Member::Member ()
363 collectTimeStats(false),
368 // This shouldn't be called due to subsystem pointer ownership issues.
369 SGSubsystemGroup::Member::Member (const Member &)
373 SGSubsystemGroup::Member::~Member ()
379 SGSubsystemGroup::Member::update (double delta_time_sec)
381 elapsed_sec += delta_time_sec;
382 if (elapsed_sec < min_step_sec) {
386 if (subsystem->is_suspended()) {
391 subsystem->update(elapsed_sec);
393 } catch (sg_exception& e) {
394 SG_LOG(SG_GENERAL, SG_ALERT, "caught exception processing subsystem:" << name
395 << "\nmessage:" << e.getMessage());
397 if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
398 SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
400 subsystem->suspend();
407 SGSubsystemGroup::Member::printTimingInformation(double time)
409 if (collectTimeStats) {
410 SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem Timing Alert, subsystem \"" << name << "\": " << time/1000.0 << "ms");
411 subsystem->printTimingInformation();
415 double SGSubsystemGroup::Member::getTimeWarningThreshold()
417 return (timeStat.mean() + 3 * timeStat.stdDev());
420 void SGSubsystemGroup::Member::updateExecutionTime(double time)
422 if (collectTimeStats) {
431 ////////////////////////////////////////////////////////////////////////
432 // Implementation of SGSubsystemMgr.
433 ////////////////////////////////////////////////////////////////////////
436 SGSubsystemMgr::SGSubsystemMgr ()
438 for (int i = 0; i < MAX_GROUPS; i++) {
439 _groups[i] = new SGSubsystemGroup;
443 SGSubsystemMgr::~SGSubsystemMgr ()
445 // ensure get_subsystem returns NULL from now onwards,
446 // before the SGSubsystemGroup destructors are run
447 _subsystem_map.clear();
449 for (int i = 0; i < MAX_GROUPS; i++) {
455 SGSubsystemMgr::init ()
457 for (int i = 0; i < MAX_GROUPS; i++)
462 SGSubsystemMgr::postinit ()
464 for (int i = 0; i < MAX_GROUPS; i++)
465 _groups[i]->postinit();
469 SGSubsystemMgr::reinit ()
471 for (int i = 0; i < MAX_GROUPS; i++)
472 _groups[i]->reinit();
476 SGSubsystemMgr::shutdown ()
478 // reverse order to prevent order dependency problems
479 for (int i = MAX_GROUPS-1; i >= 0; i--)
480 _groups[i]->shutdown();
485 SGSubsystemMgr::bind ()
487 for (int i = 0; i < MAX_GROUPS; i++)
492 SGSubsystemMgr::unbind ()
494 // reverse order to prevent order dependency problems
495 for (int i = MAX_GROUPS-1; i >= 0; i--)
496 _groups[i]->unbind();
500 SGSubsystemMgr::update (double delta_time_sec)
502 for (int i = 0; i < MAX_GROUPS; i++) {
503 _groups[i]->update(delta_time_sec);
508 SGSubsystemMgr::collectDebugTiming(bool collect)
510 for (int i = 0; i < MAX_GROUPS; i++) {
511 _groups[i]->collectDebugTiming(collect);
516 SGSubsystemMgr::suspend ()
518 for (int i = 0; i < MAX_GROUPS; i++)
519 _groups[i]->suspend();
523 SGSubsystemMgr::resume ()
525 for (int i = 0; i < MAX_GROUPS; i++)
526 _groups[i]->resume();
530 SGSubsystemMgr::is_suspended () const
536 SGSubsystemMgr::add (const char * name, SGSubsystem * subsystem,
537 GroupType group, double min_time_sec)
539 SG_LOG(SG_GENERAL, SG_INFO, "Adding subsystem " << name);
540 get_group(group)->set_subsystem(name, subsystem, min_time_sec);
542 if (_subsystem_map.find(name) != _subsystem_map.end()) {
543 SG_LOG(SG_GENERAL, SG_ALERT, "Adding duplicate subsystem " << name);
544 throw sg_exception("duplicate subsystem");
546 _subsystem_map[name] = subsystem;
550 SGSubsystemMgr::remove(const char* name)
552 SubsystemDict::iterator s =_subsystem_map.find(name);
553 if (s == _subsystem_map.end()) {
557 SGSubsystem* sub = s->second;
558 _subsystem_map.erase(s);
560 // tedious part - we don't know which group the subsystem belongs too
561 for (int i = 0; i < MAX_GROUPS; i++) {
562 if (_groups[i]->get_subsystem(name) == sub) {
563 _groups[i]->remove_subsystem(name);
566 } // of groups iteration
573 SGSubsystemMgr::get_group (GroupType group)
575 return _groups[group];
579 SGSubsystemMgr::get_subsystem (const string &name) const
581 SubsystemDict::const_iterator s =_subsystem_map.find(name);
583 if (s == _subsystem_map.end())
590 SGSubsystemMgr::printTimingStatistics(double minMaxTime,double minJitter)
592 for (int i = 0; i < MAX_GROUPS; i++) {
593 _groups[i]->printTimingStatistics(minMaxTime, minJitter);
594 } // of groups iteration
597 // end of subsystem_mgr.cxx