]> git.mxchange.org Git - simgear.git/blob - simgear/structure/subsystem_mgr.cxx
Support for incremental init of subsystems.
[simgear.git] / simgear / structure / subsystem_mgr.cxx
1 // Written by David Megginson, started 2000-12
2 //
3 // Copyright (C) 2000  David Megginson, david@megginson.com
4 //
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.
9 //
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.
14 //
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.
18 //
19 // $Id$
20
21 #ifdef HAVE_CONFIG_H
22 #  include <simgear_config.h>
23 #endif
24
25 #include <simgear/debug/logstream.hxx>
26 #include <simgear/timing/timestamp.hxx>
27
28 #include "exception.hxx"
29 #include "subsystem_mgr.hxx"
30
31 #include <simgear/math/SGMath.hxx>
32 #include "SGSmplstat.hxx"
33
34 const int SG_MAX_SUBSYSTEM_EXCEPTIONS = 4;
35
36 using std::string;
37
38 ////////////////////////////////////////////////////////////////////////
39 // Implementation of SGSubsystem
40 ////////////////////////////////////////////////////////////////////////
41
42 SGSubsystemTimingCb SGSubsystem::reportTimingCb = NULL;
43 void* SGSubsystem::reportTimingUserData = NULL;
44
45 SGSubsystem::SGSubsystem ()
46   : _suspended(false)
47 {
48 }
49
50 SGSubsystem::~SGSubsystem ()
51 {
52 }
53
54 void
55 SGSubsystem::init ()
56 {
57 }
58
59 SGSubsystem::InitStatus
60 SGSubsystem::incrementalInit ()
61 {
62   init();
63   return INIT_DONE;
64 }
65
66 void
67 SGSubsystem::postinit ()
68 {
69 }
70
71 void
72 SGSubsystem::reinit ()
73 {
74 }
75
76 void
77 SGSubsystem::shutdown ()
78 {
79 }
80
81 void
82 SGSubsystem::bind ()
83 {
84 }
85
86 void
87 SGSubsystem::unbind ()
88 {
89 }
90
91 void
92 SGSubsystem::suspend ()
93 {
94   _suspended = true;
95 }
96
97 void
98 SGSubsystem::suspend (bool suspended)
99 {
100   _suspended = suspended;
101 }
102
103 void
104 SGSubsystem::resume ()
105 {
106   _suspended = false;
107 }
108
109 bool
110 SGSubsystem::is_suspended () const
111 {
112   return _suspended;
113 }
114
115 void SGSubsystem::stamp(const string& name)
116 {
117     timingInfo.push_back(TimingInfo(name, SGTimeStamp::now()));
118 }
119
120 \f
121 ////////////////////////////////////////////////////////////////////////
122 // Implementation of SGSubsystemGroup.
123 ////////////////////////////////////////////////////////////////////////
124
125 class SGSubsystemGroup::Member
126 {    
127 private:
128     Member (const Member &member);
129 public:
130     Member ();
131     virtual ~Member ();
132     
133     virtual void update (double delta_time_sec);
134
135     void reportTiming(void) { if (reportTimingCb) reportTimingCb(reportTimingUserData, name, &timeStat); }
136     void updateExecutionTime(double time) { timeStat += time;}
137
138     SampleStatistic timeStat;
139     std::string name;
140     SGSubsystem * subsystem;
141     double min_step_sec;
142     double elapsed_sec;
143     bool collectTimeStats;
144     int exceptionCount;
145     int initTime;
146 };
147
148
149
150 SGSubsystemGroup::SGSubsystemGroup () :
151   _fixedUpdateTime(-1.0),
152   _updateTimeRemainder(0.0),
153   _initPosition(-1)
154 {
155 }
156
157 SGSubsystemGroup::~SGSubsystemGroup ()
158 {
159     // reverse order to prevent order dependency problems
160     for (unsigned int i = _members.size(); i > 0; i--)
161     {
162         delete _members[i-1];
163     }
164 }
165
166 void
167 SGSubsystemGroup::init ()
168 {
169     for (unsigned int i = 0; i < _members.size(); i++)
170         _members[i]->subsystem->init();
171 }
172
173 SGSubsystem::InitStatus
174 SGSubsystemGroup::incrementalInit()
175 {
176   if (_initPosition < 0)
177     _initPosition = 0;
178   
179   if (_initPosition >= _members.size())
180     return INIT_DONE;
181   
182   SGTimeStamp st;
183   st.stamp();
184   InitStatus memberStatus = _members[_initPosition]->subsystem->incrementalInit();
185   _members[_initPosition]->initTime += st.elapsedMSec();
186   
187   if (memberStatus == INIT_DONE)
188     ++_initPosition;
189   
190   return INIT_CONTINUE;
191 }
192
193 void
194 SGSubsystemGroup::postinit ()
195 {
196     for (unsigned int i = 0; i < _members.size(); i++)
197         _members[i]->subsystem->postinit();
198 }
199
200 void
201 SGSubsystemGroup::reinit ()
202 {
203     for (unsigned int i = 0; i < _members.size(); i++)
204         _members[i]->subsystem->reinit();
205 }
206
207 void
208 SGSubsystemGroup::shutdown ()
209 {
210     // reverse order to prevent order dependency problems
211     for (unsigned int i = _members.size(); i > 0; i--)
212         _members[i-1]->subsystem->shutdown();
213   _initPosition = -1;
214 }
215
216 void
217 SGSubsystemGroup::bind ()
218 {
219     for (unsigned int i = 0; i < _members.size(); i++)
220         _members[i]->subsystem->bind();
221 }
222
223 void
224 SGSubsystemGroup::unbind ()
225 {
226     // reverse order to prevent order dependency problems
227     for (unsigned int i = _members.size(); i > 0; i--)
228        _members[i-1]->subsystem->unbind();
229 }
230
231 void
232 SGSubsystemGroup::update (double delta_time_sec)
233 {
234     int loopCount = 1;
235     // if dt == 0.0, we are paused, so we need to run one iteration
236     // of our members; if we have a fixed update time, we compute a
237     // loop count, and locally adjust dt
238     if ((delta_time_sec > 0.0) && (_fixedUpdateTime > 0.0)) {
239       double localDelta = delta_time_sec + _updateTimeRemainder;
240       loopCount = SGMiscd::roundToInt(localDelta / _fixedUpdateTime);
241       _updateTimeRemainder = delta_time_sec - (loopCount * _fixedUpdateTime);
242       delta_time_sec = _fixedUpdateTime;
243     }
244
245     bool recordTime = (reportTimingCb != NULL);
246     SGTimeStamp timeStamp;
247     while (loopCount-- > 0) {
248       for (unsigned int i = 0; i < _members.size(); i++)
249       {
250           if (recordTime)
251               timeStamp = SGTimeStamp::now();
252
253           _members[i]->update(delta_time_sec); // indirect call
254
255           if ((recordTime)&&(reportTimingCb))
256           {
257               timeStamp = SGTimeStamp::now() - timeStamp;
258               _members[i]->updateExecutionTime(timeStamp.toUSecs());
259           }
260       }
261     } // of multiple update loop
262 }
263
264 void
265 SGSubsystemGroup::reportTiming(void)
266 {
267     for (unsigned int i = _members.size(); i > 0; i--)
268     {
269         _members[i-1]->reportTiming();
270     }
271 }
272
273 void
274 SGSubsystemGroup::suspend ()
275 {
276     for (unsigned int i = 0; i < _members.size(); i++)
277         _members[i]->subsystem->suspend();
278 }
279
280 void
281 SGSubsystemGroup::resume ()
282 {
283     for (unsigned int i = 0; i < _members.size(); i++)
284         _members[i]->subsystem->resume();
285 }
286
287 bool
288 SGSubsystemGroup::is_suspended () const
289 {
290     return false;
291 }
292
293 void
294 SGSubsystemGroup::set_subsystem (const string &name, SGSubsystem * subsystem,
295                                  double min_step_sec)
296 {
297     Member * member = get_member(name, true);
298     if (member->subsystem != 0)
299         delete member->subsystem;
300     member->name = name;
301     member->subsystem = subsystem;
302     member->min_step_sec = min_step_sec;
303 }
304
305 SGSubsystem *
306 SGSubsystemGroup::get_subsystem (const string &name)
307 {
308     Member * member = get_member(name);
309     if (member != 0)
310         return member->subsystem;
311     else
312         return 0;
313 }
314
315 void
316 SGSubsystemGroup::remove_subsystem (const string &name)
317 {
318     for (unsigned int i = 0; i < _members.size(); i++) {
319         if (name == _members[i]->name) {
320             _members.erase(_members.begin() + i);
321             return;
322         }
323     }
324 }
325
326 void
327 SGSubsystemGroup::set_fixed_update_time(double dt)
328 {
329   _fixedUpdateTime = dt;
330 }
331
332 bool
333 SGSubsystemGroup::has_subsystem (const string &name) const
334 {
335     return (((SGSubsystemGroup *)this)->get_member(name) != 0);
336 }
337
338 SGSubsystemGroup::Member *
339 SGSubsystemGroup::get_member (const string &name, bool create)
340 {
341     for (unsigned int i = 0; i < _members.size(); i++) {
342         if (_members[i]->name == name)
343             return _members[i];
344     }
345     if (create) {
346         Member * member = new Member;
347         _members.push_back(member);
348         return member;
349     } else {
350         return 0;
351     }
352 }
353
354
355 \f
356 ////////////////////////////////////////////////////////////////////////
357 // Implementation of SGSubsystemGroup::Member
358 ////////////////////////////////////////////////////////////////////////
359
360
361 SGSubsystemGroup::Member::Member ()
362     : name(""),
363       subsystem(0),
364       min_step_sec(0),
365       elapsed_sec(0),
366       exceptionCount(0),
367       initTime(0)
368 {
369 }
370
371 // This shouldn't be called due to subsystem pointer ownership issues.
372 SGSubsystemGroup::Member::Member (const Member &)
373 {
374 }
375
376 SGSubsystemGroup::Member::~Member ()
377 {
378     delete subsystem;
379 }
380
381 void
382 SGSubsystemGroup::Member::update (double delta_time_sec)
383 {
384     elapsed_sec += delta_time_sec;
385     if (elapsed_sec < min_step_sec) {
386         return;
387     }
388     
389     if (subsystem->is_suspended()) {
390         return;
391     }
392     
393     try {
394       subsystem->update(elapsed_sec);
395       elapsed_sec = 0;
396     } catch (sg_exception& e) {
397       SG_LOG(SG_GENERAL, SG_ALERT, "caught exception processing subsystem:" << name
398         << "\nmessage:" << e.getMessage());
399       
400       if (++exceptionCount > SG_MAX_SUBSYSTEM_EXCEPTIONS) {
401         SG_LOG(SG_GENERAL, SG_ALERT, "(exceptionCount=" << exceptionCount <<
402           ", suspending)");
403         subsystem->suspend();
404       }
405     }
406 }
407
408
409 ////////////////////////////////////////////////////////////////////////
410 // Implementation of SGSubsystemMgr.
411 ////////////////////////////////////////////////////////////////////////
412
413
414 SGSubsystemMgr::SGSubsystemMgr () :
415   _initPosition(-1)
416 {
417   for (int i = 0; i < MAX_GROUPS; i++) {
418     _groups[i] = new SGSubsystemGroup;
419   }
420 }
421
422 SGSubsystemMgr::~SGSubsystemMgr ()
423 {
424   // ensure get_subsystem returns NULL from now onwards,
425   // before the SGSubsystemGroup destructors are run
426   _subsystem_map.clear();
427   
428   for (int i = 0; i < MAX_GROUPS; i++) {
429     delete _groups[i];
430   }
431 }
432
433 void
434 SGSubsystemMgr::init ()
435 {
436     for (int i = 0; i < MAX_GROUPS; i++)
437             _groups[i]->init();
438 }
439
440 SGSubsystem::InitStatus
441 SGSubsystemMgr::incrementalInit()
442 {
443   if (_initPosition < 0)
444     _initPosition = 0;
445   
446   if (_initPosition >= MAX_GROUPS)
447     return INIT_DONE;
448   
449   InitStatus memberStatus = _groups[_initPosition]->incrementalInit();  
450   if (memberStatus == INIT_DONE)
451     ++_initPosition;
452   
453   return INIT_CONTINUE;
454 }
455
456 void
457 SGSubsystemMgr::postinit ()
458 {
459     for (int i = 0; i < MAX_GROUPS; i++)
460             _groups[i]->postinit();
461 }
462
463 void
464 SGSubsystemMgr::reinit ()
465 {
466     for (int i = 0; i < MAX_GROUPS; i++)
467             _groups[i]->reinit();
468 }
469
470 void
471 SGSubsystemMgr::shutdown ()
472 {
473     // reverse order to prevent order dependency problems
474     for (int i = MAX_GROUPS-1; i >= 0; i--)
475         _groups[i]->shutdown();
476   
477     _initPosition = -1;
478 }
479
480
481 void
482 SGSubsystemMgr::bind ()
483 {
484     for (int i = 0; i < MAX_GROUPS; i++)
485         _groups[i]->bind();
486 }
487
488 void
489 SGSubsystemMgr::unbind ()
490 {
491     // reverse order to prevent order dependency problems
492     for (int i = MAX_GROUPS-1; i >= 0; i--)
493         _groups[i]->unbind();
494 }
495
496 void
497 SGSubsystemMgr::update (double delta_time_sec)
498 {
499     for (int i = 0; i < MAX_GROUPS; i++) {
500         _groups[i]->update(delta_time_sec);
501     }
502 }
503
504 void
505 SGSubsystemMgr::suspend ()
506 {
507     for (int i = 0; i < MAX_GROUPS; i++)
508         _groups[i]->suspend();
509 }
510
511 void
512 SGSubsystemMgr::resume ()
513 {
514     for (int i = 0; i < MAX_GROUPS; i++)
515         _groups[i]->resume();
516 }
517
518 bool
519 SGSubsystemMgr::is_suspended () const
520 {
521     return false;
522 }
523
524 void
525 SGSubsystemMgr::add (const char * name, SGSubsystem * subsystem,
526                      GroupType group, double min_time_sec)
527 {
528     SG_LOG(SG_GENERAL, SG_INFO, "Adding subsystem " << name);
529     get_group(group)->set_subsystem(name, subsystem, min_time_sec);
530
531     if (_subsystem_map.find(name) != _subsystem_map.end()) {
532         SG_LOG(SG_GENERAL, SG_ALERT, "Adding duplicate subsystem " << name);
533         throw sg_exception("duplicate subsystem");
534     }
535     _subsystem_map[name] = subsystem;
536 }
537
538 SGSubsystem* 
539 SGSubsystemMgr::remove(const char* name)
540 {
541   SubsystemDict::iterator s =_subsystem_map.find(name);
542   if (s == _subsystem_map.end()) {
543     return NULL;
544   }
545   
546   SGSubsystem* sub = s->second;
547   _subsystem_map.erase(s);
548   
549 // tedious part - we don't know which group the subsystem belongs too
550   for (int i = 0; i < MAX_GROUPS; i++) {
551     if (_groups[i]->get_subsystem(name) == sub) {
552       _groups[i]->remove_subsystem(name);
553       break;
554     }
555   } // of groups iteration
556   
557   return sub;
558 }
559
560
561 SGSubsystemGroup *
562 SGSubsystemMgr::get_group (GroupType group)
563 {
564     return _groups[group];
565 }
566
567 SGSubsystem *
568 SGSubsystemMgr::get_subsystem (const string &name) const
569 {
570     SubsystemDict::const_iterator s =_subsystem_map.find(name);
571
572     if (s == _subsystem_map.end())
573         return 0;
574     else
575         return s->second;
576 }
577
578 /** Trigger the timing callback to report data for all subsystems. */
579 void
580 SGSubsystemMgr::reportTiming()
581 {
582     for (int i = 0; i < MAX_GROUPS; i++) {
583         _groups[i]->reportTiming();
584     } // of groups iteration
585 }
586
587 // end of subsystem_mgr.cxx