OSDLScheduler.cc

Go to the documentation of this file.
00001 #include "OSDLScheduler.h"
00002 
00003 #include "OSDLBasic.h"                // for getExistingCommonModule
00004 #include "OSDLVideo.h"                // for redraw
00005 #include "OSDLRenderer.h"             // for Renderer
00006 #include "OSDLActiveObject.h"         // for ActiveObject
00007 
00008 
00009 #include <iostream>                   // for ostringstream
00010 using std::ostringstream ;
00011 
00012 #include <iomanip>                    // for std::ios::fixed
00013 
00014 
00015 
00016 using namespace Ceylan::Log ;         // for LogPlug
00017 
00018 // for time units and calls (ex : Millisecond, basicSleep) :
00019 using namespace Ceylan::System ;      
00020  
00021 using namespace OSDL::Events ;
00022 using namespace OSDL::Rendering ;
00023 using namespace OSDL::Engine ;
00024 
00025 using std::string ;
00026 using std::list ;
00027 using std::map ;
00028 
00029 
00030 Scheduler * Scheduler::_internalScheduler = 0 ;
00031 
00032 const Scheduler::Delay Scheduler::ShutdownBucketLevel = 100000 ;
00033 
00034 
00035 #ifdef OSDL_USES_CONFIG_H
00036 #include <OSDLConfig.h>               // for OSDL_DEBUG_SCHEDULER and al
00037 #endif // OSDL_USES_CONFIG_H
00038 
00039 
00040 
00041 #if OSDL_DEBUG_SCHEDULER
00042 
00043 #include <iostream>
00044 #define OSDL_SCHEDULE_LOG(message) std::cerr << message << std::endl ;
00045 
00046 #else // OSDL_DEBUG_SCHEDULER
00047 
00048 #define OSDL_SCHEDULE_LOG(message)
00049 
00050 #endif // OSDL_DEBUG_SCHEDULER
00051 
00052 
00053 
00054 bool Scheduler::hasRenderer() const throw()
00055 {
00056 
00057     return ( _renderer != 0 ) ;
00058     
00059 }
00060 
00061 
00062 Renderer & Scheduler::getRenderer() const throw( SchedulingException )
00063 {
00064 
00065     if ( _renderer != 0 )
00066         return * _renderer ;
00067     else
00068         throw SchedulingException( 
00069             "Scheduler::getRenderer : no renderer available." ) ;
00070         
00071 }
00072 
00073 
00074 void Scheduler::setRenderer( Rendering::Renderer & newRenderer ) throw()
00075 {
00076 
00077     if ( _renderer != 0 )
00078         delete _renderer ;
00079     
00080     _renderer = & newRenderer ;
00081         
00082 }
00083 
00084 
00085 
00086 void Scheduler::setScreenshotMode( bool on, const string & frameFilenamePrefix, 
00087     Hertz frameFrequency ) throw()
00088 {
00089 
00090     _screenshotMode = on ;
00091     _frameFilenamePrefix = frameFilenamePrefix ;
00092     
00093 }
00094 
00095                     
00096 void Scheduler::setTimeSliceDuration( Microsecond engineTickDuration ) throw()
00097 {
00098 
00099     _engineTickDuration = engineTickDuration ;
00100     
00101     // Recomputes all the ticks that depends on the engine tick :
00102     setSimulationFrequency(   _desiredSimulationFrequency ) ;
00103     setRenderingFrequency(    _desiredRenderingFrequency ) ;
00104     setScreenshotFrequency(   _desiredScreenshotFrequency ) ;
00105     setInputPollingFrequency( _desiredInputFrequency ) ;
00106     
00107     _secondToEngineTick = /* microsecond */ 1000000 / _engineTickDuration ;
00108     
00109     OSDL_SCHEDULE_LOG( 
00110         "Multiplicative factor to convert seconds into engine ticks : "
00111         + Ceylan::toString( _secondToEngineTick ) ) ;
00112         
00113 }
00114 
00115 
00116 Microsecond Scheduler::getTimeSliceDuration() const throw()
00117 {
00118 
00119     return _engineTickDuration ;
00120     
00121 }
00122 
00123 
00124 void Scheduler::setSimulationFrequency( Hertz frequency ) 
00125     throw( SchedulingException )
00126 {
00127 
00128     /*
00129      * f Hz correspond to a period of 1E6/f microseconds , therefore to
00130      * 1E6 / ( f * _engineTickDuration ) engine ticks per period.
00131      *
00132      * For example, if f = 100 Hz and engine tick duration is 1000 microseconds,
00133      * there are 1E6/(100*1000) = 10 engine ticks for each simulation tick.
00134      *
00135      */
00136      
00137      if ( frequency * _engineTickDuration > 1E6 )
00138         throw SchedulingException( "Scheduler::setSimulationFrequency : "
00139             " requested simulation frequency (" 
00140             + Ceylan::toString( frequency ) 
00141             + " Hz) is too high for engine tick,"
00142             " which lasts for " 
00143             + Ceylan::toString( _engineTickDuration ) 
00144             + " microseconds." ) ;
00145     
00146     _desiredSimulationFrequency = frequency ;
00147     _simulationPeriod = static_cast<Period>( 
00148         1E6 / ( frequency * _engineTickDuration ) ) ;
00149 
00150 #if OSDL_DEBUG
00151 
00152     LogPlug::debug( "Scheduler::setSimulationFrequency : for a requested "
00153         "simulation frequency of " + Ceylan::toString( frequency ) 
00154         + " Hz, the simulation period corresponds to " 
00155         + Ceylan::toString( _simulationPeriod ) + " engine ticks." ) ;
00156         
00157 #endif // OSDL_DEBUG
00158     
00159 }
00160 
00161 
00162 Period Scheduler::getSimulationTickCount() const throw()
00163 {
00164 
00165     return _simulationPeriod ;
00166     
00167 }
00168 
00169 
00170 void Scheduler::setRenderingFrequency( Hertz frequency ) 
00171     throw( SchedulingException )
00172 {
00173 
00174     /*
00175      * f Hz correspond to 1E6/f microseconds, therefore to
00176      * 1E6 / ( f * _engineTickDuration ) engine ticks.
00177      *
00178      * For example, if f = 40 Hz = 40 frames per seconds and engine tick
00179      * duration is 1000 microseconds, there are 1E6/(40*1000) = 25 engine 
00180      * ticks for each rendering tick.
00181      *
00182      */
00183      
00184      if ( frequency * _engineTickDuration > 1E6 )
00185         throw SchedulingException( "Scheduler::setRenderingFrequency : "
00186             "requested rendering frequency (" 
00187             + Ceylan::toString( frequency ) 
00188             + " Hz) is too high for engine tick,"
00189             " which lasts for " 
00190             + Ceylan::toString( _engineTickDuration ) 
00191             + " microseconds." ) ;
00192             
00193     _desiredRenderingFrequency = frequency ;        
00194     _renderingPeriod = static_cast<Period>( 
00195         1E6 / ( frequency * _engineTickDuration ) ) ;
00196 
00197 #if OSDL_DEBUG
00198 
00199     LogPlug::debug( "Scheduler::setRenderingFrequency : for a requested "
00200         "rendering frequency of " + Ceylan::toString( frequency ) 
00201         + " Hz, the rendering period corresponds to " 
00202         + Ceylan::toString( _renderingPeriod ) + " engine ticks." ) ;
00203         
00204 #endif // OSDL_DEBUG
00205 
00206 }
00207 
00208 
00209 Period Scheduler::getRenderingTickCount() const throw()
00210 {
00211 
00212     return _renderingPeriod ;
00213     
00214 }
00215 
00216 
00217 void Scheduler::setScreenshotFrequency( Hertz frequency ) 
00218     throw( SchedulingException )
00219 {
00220 
00221     /*
00222      * f Hz correspond to a period of 1E6/f microseconds , therefore to
00223      * 1E6 / ( f * _engineTickDuration ) engine ticks per period.
00224      *
00225      * For example, if f = 25 Hz = 25 frames per seconds, and engine tick
00226      * duration is 1000 microseconds, there are 1E6/(25*1000) = 40 engine 
00227      * ticks for each screenshot tick.
00228      *
00229      * Screenshot task will be implemented as a periodic object calling
00230      * rendering methods.
00231      *
00232      */
00233 
00234      
00235      if ( frequency * _engineTickDuration > 1E6 )
00236         throw SchedulingException( "Scheduler::setScreenshotFrequency : "
00237             " requested screenshot frequency (" 
00238             + Ceylan::toString( frequency )
00239             + " Hz) is too high for engine tick,"
00240             " which lasts for " + Ceylan::toString( _engineTickDuration ) 
00241             + " microseconds." ) ;
00242             
00243     _desiredScreenshotFrequency = frequency ;       
00244     _screenshotPeriod = static_cast<Period>( 
00245         1E6 / ( frequency * _engineTickDuration ) ) ;
00246 
00247 
00248 #if OSDL_DEBUG
00249 
00250     LogPlug::debug( "Scheduler::setScreenshotFrequency : for a requested "
00251         "screenshot frequency of " + Ceylan::toString( frequency ) 
00252         + " Hz, the screenshot period corresponds to " 
00253         + Ceylan::toString( _screenshotPeriod ) + " engine ticks." ) ;
00254         
00255 #endif // OSDL_DEBUG
00256 
00257 }
00258 
00259 
00260 Period Scheduler::getScreenshotTickCount() const throw()
00261 {
00262 
00263     return _screenshotPeriod ;
00264     
00265 }
00266 
00267 
00268 void Scheduler::setInputPollingFrequency( Hertz frequency ) 
00269     throw( SchedulingException )
00270 {
00271 
00272     /*
00273      * f Hz correspond to a period of 1E6/f microseconds , therefore to
00274      * 1E6 / ( f * _engineTickDuration ) engine ticks per period.
00275      *
00276      * For example, if f = 20 Hz = 20 frames per seconds and engine tick
00277      * duration is 1000 microseconds, there are 10E6/(20*1000) = 50 engine 
00278      * ticks for each input tick.
00279      *
00280      */
00281      
00282      if ( frequency * _engineTickDuration > 1E6 )
00283         throw SchedulingException( 
00284             "Scheduler::setInputPollingFrequency : requested input "
00285             "frequency (" + Ceylan::toString( frequency ) 
00286             + " Hz) is too high for engine tick,"
00287             " which lasts for " 
00288             + Ceylan::toString( _engineTickDuration ) + " microseconds." ) ;
00289             
00290     _desiredInputFrequency = frequency ;        
00291     _inputPeriod = static_cast<Period>( 
00292         1E6 / ( frequency * _engineTickDuration ) ) ;
00293 
00294 #if OSDL_DEBUG
00295 
00296     LogPlug::debug( "Scheduler::setInputPollingFrequency : for a requested "
00297         "input frequency of " + Ceylan::toString( frequency ) 
00298         + " Hz, the input period corresponds to " 
00299         + Ceylan::toString( _inputPeriod ) + " engine ticks." ) ;
00300         
00301 #endif // OSDL_DEBUG
00302 
00303 }
00304 
00305 
00306 void Scheduler::setIdleCallback( 
00307         Ceylan::System::Callback idleCallback, 
00308         void * callbackData, 
00309         Ceylan::System::Microsecond callbackExpectedMaxDuration )
00310     throw()
00311 {
00312 
00313     _idleCallback            = idleCallback ;
00314     _idleCallbackData        = callbackData ;
00315     
00316     if ( callbackExpectedMaxDuration != 0 )
00317         _idleCallbackMaxDuration = callbackExpectedMaxDuration ;
00318     else
00319         _idleCallbackMaxDuration = EventsModule::EvaluateCallbackduration(
00320             idleCallback, callbackData ) ;
00321     
00322 }
00323                     
00324                     
00325 Period Scheduler::getInputPollingTickCount() const throw()
00326 {
00327 
00328     return _inputPeriod ;
00329     
00330 }
00331 
00332 
00333 EngineTick Scheduler::getCurrentEngineTick() const throw() 
00334 {
00335 
00336     return _currentEngineTick ;
00337     
00338 }
00339 
00340 
00341 SimulationTick Scheduler::getCurrentSimulationTick() const throw()
00342 {
00343 
00344     return _currentSimulationTick ;
00345     
00346 }
00347 
00348 
00349 RenderingTick Scheduler::getCurrentRenderingTick() const throw()
00350 {
00351 
00352     return _currentRenderingTick ;
00353     
00354 }
00355 
00356 
00357 RenderingTick Scheduler::getCurrentInputTick() const throw()
00358 {
00359 
00360     return _currentInputTick ;
00361     
00362 }
00363 
00364 
00365 void Scheduler::registerObject( ActiveObject & objectToRegister ) throw()
00366 {
00367 
00368 #if OSDL_DEBUG
00369     if ( objectToRegister.getPeriod() == 0 
00370             && ! objectToRegister.hasProgrammedActivations() )
00371         LogPlug::error( "Scheduler::registerObject : "
00372             "following object will never be activated : "
00373             + objectToRegister.toString() ) ;
00374 #endif // OSDL_DEBUG
00375     
00376     
00377     if ( objectToRegister.getPeriod() != 0 )
00378     {
00379         // This active object is to be periodically activated :
00380         PeriodicSlot & slot = getPeriodicSlotFor( 
00381             objectToRegister.getPeriod() ) ;
00382             
00383         slot.add( objectToRegister ) ;  
00384 
00385     }
00386     
00387     
00388     if ( objectToRegister.hasProgrammedActivations() )
00389     {
00390     
00391         // Declare the programmed ticks for this object :
00392         
00393         const list<SimulationTick> & programmed =
00394             objectToRegister.getProgrammedActivations() ;
00395         
00396         for ( list<SimulationTick>::const_iterator it = programmed.begin(); 
00397             it != programmed.end(); it++ )
00398         {
00399             programTriggerFor( objectToRegister, (*it) ) ;          
00400         }
00401             
00402     }
00403     
00404     objectToRegister.setBirthTime( _currentSimulationTick ) ;
00405     
00406 }
00407 
00408 
00409 void Scheduler::schedule() throw( SchedulingException )
00410 {
00411     
00412     try
00413     {
00414     
00415         _eventsModule = & OSDL::getExistingCommonModule().getEventsModule() ;
00416         
00417     }
00418     catch( OSDL::Exception & e )
00419     {
00420         throw SchedulingException( "Scheduler::schedule : "
00421             "no events module available." ) ;
00422     }
00423     
00424     
00425     if ( _renderer != 0 )
00426     {
00427         send( "Scheduling now, using renderer : " + _renderer->toString() ) ;
00428     }
00429     else
00430     {
00431         // Retrieve video module, used instead of the renderer :
00432         try
00433         {
00434             _videoModule = & OSDL::getExistingCommonModule().getVideoModule() ;
00435         }
00436         catch( OSDL::Exception & e )
00437         {
00438             throw SchedulingException( "Scheduler::schedule : "
00439                 "no renderer nor video module available." ) ;
00440         }
00441         
00442         send( "Scheduling now, using video module (no renderer)." ) ;
00443         
00444     }
00445     
00446         
00447     if ( _screenshotMode )
00448         scheduleNoDeadline() ;
00449     else
00450         scheduleBestEffort() ;  
00451         
00452 }
00453 
00454 
00455 void Scheduler::stop() throw()
00456 {
00457 
00458     _stopRequested = true ;
00459     
00460 }
00461 
00462 
00463 const string Scheduler::toString( Ceylan::VerbosityLevels level ) const throw()
00464 {   
00465 
00466     ostringstream buf ;
00467     buf.precision( 2 ) ;
00468 
00469     buf << setiosflags( std::ios::fixed ) 
00470         << "Scheduler, whose internal frequency is " 
00471         << 1E6 / _engineTickDuration
00472         << " Hz (engine tick duration is " 
00473         + Ceylan::toString( _engineTickDuration )
00474         + " microseconds). Current engine tick is " 
00475         + Ceylan::toString( _currentEngineTick ) 
00476         + ", current simulation tick is "
00477         + Ceylan::toString( _currentSimulationTick )
00478         + ", current rendering tick is "
00479         + Ceylan::toString( _currentRenderingTick ) 
00480         + " and current input tick is " 
00481         + Ceylan::toString( _currentInputTick )
00482         + ". Screenshot mode is "
00483         + ( _screenshotMode ? "on." : "off." ) ;
00484     
00485                     
00486     if ( _scheduleStartingSecond == 0 && _scheduleStartingMicrosecond == 0 )
00487         buf << " Scheduler is stopped." ;
00488     else
00489     {
00490     
00491         Ceylan::System::Second sec ;
00492         Ceylan::System::Microsecond microsec ;
00493         Ceylan::System::getPreciseTime( sec, microsec ) ;
00494         
00495         buf << " Scheduler has been running for " 
00496             << Ceylan::System::durationToString( _scheduleStartingSecond,
00497                 _scheduleStartingMicrosecond, sec, microsec )
00498             << "." ;
00499 
00500     }
00501 
00502 
00503     if ( _renderer != 0 )
00504         buf << " Using a renderer (" 
00505             + _renderer->toString( Ceylan::low ) + ")" ;
00506     else
00507         buf << " No renderer registered, using directly video module" ;
00508 
00509     if ( _idleCallback == 0 )
00510         buf << ". Using atomic sleep idle callback" ;
00511     else
00512         buf << ". Using user-specified idle callback" ;
00513     
00514     if ( _idleCallbackData )
00515         if ( _idleCallback )
00516             buf << ", callback data has been set as well." ;
00517         else
00518             buf << ", callback data has been set, "
00519                 " whereas it is not used by default idle callback (abnormal)." ;
00520 
00521     buf << ". The estimated upper-bound for idle callback duration is "
00522         + Ceylan::toString( _idleCallbackMaxDuration )
00523         + " microseconds" ;
00524                 
00525                 
00526     if ( level == Ceylan::low )
00527         return buf.str() ;
00528             
00529     // Do not use desired frequencies, compute them instead from the periods :
00530             
00531     buf <<  ". User-defined simulation frequency is " 
00532         + Ceylan::toString( 1E6 / ( _simulationPeriod * _engineTickDuration ), 
00533             /* precision */ 2 )
00534         + " Hz (a period of " 
00535         + Ceylan::toString( _simulationPeriod ) 
00536         + " engine ticks), rendering frequency is "
00537         + Ceylan::toString( 1E6 / ( _renderingPeriod * _engineTickDuration ),
00538             /* precision */ 2 ) 
00539         + " Hz (a period of " 
00540         + Ceylan::toString( _renderingPeriod ) 
00541         + " engine ticks), input polling frequency is "
00542         + Ceylan::toString( 1E6 / ( _inputPeriod * _engineTickDuration ),
00543             /* precision */ 2 ) 
00544         + " Hz (a period of " 
00545         + Ceylan::toString( _inputPeriod ) 
00546         + " engine ticks). There are "
00547         + Ceylan::toString( _periodicSlots.size() ) 
00548         + " used periodic slot(s) and "
00549         + Ceylan::toString( _programmedActivated.size() ) 
00550         + " programmed object(s)" ;
00551         
00552         
00553     if ( level == Ceylan::medium )
00554         return buf.str() ;
00555         
00556         
00557     if ( ! _periodicSlots.empty() )
00558     {
00559         
00560         buf << ". Enumerating scheduling periodic slots : " ;
00561         
00562         std::list<string> slots ;
00563         
00564         for ( list<PeriodicSlot*>::const_iterator it = _periodicSlots.begin(); 
00565                 it != _periodicSlots.end(); it++ )
00566             slots.push_back( (*it)->toString( level ) ) ;
00567 
00568         buf << Ceylan::formatStringList( slots ) ;
00569             
00570     }
00571     
00572     if ( ! _programmedActivated.empty() )
00573     {
00574     
00575         buf << ". Enumerating scheduling programmed slots : " ;
00576         
00577         std::list<string> programmed ;
00578         
00579         for( map<SimulationTick, ListOfActiveObjects>::const_iterator it 
00580                 = _programmedActivated.begin(); it 
00581                     != _programmedActivated.end(); it++ )
00582             programmed.push_back( "For simulation tick #" 
00583                 + Ceylan::toString( (*it).first )
00584                 + ", there are " + Ceylan::toString( (*it).second.size() ) 
00585                 + " object(s) programmed" ) ;
00586                 
00587         buf << Ceylan::formatStringList( programmed ) ;
00588         
00589     }   
00590     
00591     
00592     if ( _screenshotMode )
00593     {
00594     
00595         buf << ". Current movie frame period for screenshot mode is " 
00596             + Ceylan::toString( _screenshotPeriod ) 
00597             + " engine ticks, which corresponds to a frequency of "
00598             + Ceylan::toString( 1E6 /
00599                 ( _screenshotPeriod * _engineTickDuration ), /* precision */ 2 )
00600             + " frames per second" ;    
00601 
00602         // No simulation nor rendering tick can be missed in screenshot mode.
00603         return buf.str() ;
00604     }
00605     
00606         
00607     if ( _missedSimulationTicks == 0 )
00608     {
00609     
00610         buf << ". No simulation tick was missed" ;
00611     
00612     }   
00613     else
00614     {
00615     
00616         Ceylan::System::Second sec ;
00617         Ceylan::System::Microsecond microsec ;
00618         Ceylan::System::getPreciseTime( sec, microsec ) ;
00619 
00620         
00621         buf << ". " + Ceylan::toString( _missedSimulationTicks ) 
00622             + " simulation ticks were missed, "
00623             "it sums up to an actual average simulation frequency of "  
00624             << 1E6 * ( _currentSimulationTick - _missedSimulationTicks ) / 
00625                 static_cast<Ceylan::Float64>( 
00626                     ( sec - _scheduleStartingSecond ) * 1E6
00627                     + microsec - _scheduleStartingMicrosecond ) 
00628             << " Hz" ;  
00629                             
00630     }   
00631         
00632         
00633     if ( _missedRenderingTicks == 0 )
00634         buf << ". No rendering tick was missed" ;
00635     else
00636     {
00637     
00638         Ceylan::System::Second sec ;
00639         Ceylan::System::Microsecond microsec ;
00640         Ceylan::System::getPreciseTime( sec, microsec ) ;
00641 
00642         buf << ". " + Ceylan::toString( _missedRenderingTicks ) 
00643             + " rendering ticks were missed, "
00644             "it sums up to an actual average rendering frequency of "
00645             << 1E6 * ( _currentRenderingTick - _missedRenderingTicks ) / 
00646                 static_cast<Ceylan::Float64>( 
00647                     ( sec - _scheduleStartingSecond ) * 1E6
00648                     + microsec - _scheduleStartingMicrosecond )
00649             << " Hz" ;  
00650     }
00651             
00652             
00653     if ( _missedInputPollingTicks == 0 )
00654         buf << ". No input tick was missed" ;
00655     else
00656     {
00657     
00658         Ceylan::System::Second sec ;
00659         Ceylan::System::Microsecond microsec ;
00660         Ceylan::System::getPreciseTime( sec, microsec ) ;
00661 
00662         buf << ". " + Ceylan::toString( _missedInputPollingTicks ) 
00663             + " input ticks were missed, "
00664             "it sums up to an actual average input frequency of "
00665             << 1E6 * ( _currentInputTick - _missedInputPollingTicks ) / 
00666                 static_cast<Ceylan::Float64>( 
00667                     ( sec - _scheduleStartingSecond ) * 1E6
00668                     + microsec - _scheduleStartingMicrosecond ) 
00669             << " Hz" ;  
00670     }       
00671     
00672     return buf.str() ;
00673                 
00674 }
00675 
00676 
00677 
00678 // Static section.
00679 
00680 
00681 Scheduler & Scheduler::GetExistingScheduler() throw( SchedulingException )
00682 {
00683 
00684     if ( Scheduler::_internalScheduler == 0 )
00685         throw SchedulingException( 
00686             "Scheduler::GetExistingScheduler : no scheduler available." ) ;
00687             
00688     return * Scheduler::_internalScheduler ;
00689 
00690 }
00691 
00692 
00693 Scheduler & Scheduler::GetScheduler() throw()
00694 {
00695 
00696     if ( Scheduler::_internalScheduler == 0 )
00697     {
00698     
00699         LogPlug::debug( "Scheduler::GetScheduler : "
00700             "no scheduler available, creating new one" ) ;
00701             
00702         Scheduler::_internalScheduler = new Scheduler() ;
00703         
00704     }
00705     else
00706     {
00707         LogPlug::debug( "Scheduler::GetScheduler : "
00708             "returning already constructed instance of scheduler, "
00709             "no creation." ) ;
00710     }
00711 
00712     LogPlug::debug( "Scheduler::GetScheduler : returning Scheduler instance "
00713         + Ceylan::toString( Scheduler::_internalScheduler ) ) ;
00714 
00715     return * Scheduler::_internalScheduler ;
00716 
00717 }
00718 
00719 
00720 void Scheduler::DeleteExistingScheduler() throw( SchedulingException )
00721 {
00722 
00723     if ( Scheduler::_internalScheduler != 0 )
00724         throw SchedulingException( "Scheduler::DeleteExistingScheduler : "
00725             "there was no already existing scheduler." ) ;
00726             
00727 #if OSDL_DEBUG_SCHEDULER
00728     LogPlug::debug( 
00729         "Scheduler::DeleteExistingScheduler : effective deleting." ) ;
00730 #endif // OSDL_DEBUG_SCHEDULER
00731         
00732     delete Scheduler::_internalScheduler ;
00733     Scheduler::_internalScheduler = 0 ;
00734 
00735 }
00736 
00737 
00738 void Scheduler::DeleteScheduler() throw()
00739 {
00740 
00741     if ( Scheduler::_internalScheduler != 0 )
00742     {
00743     
00744 #if OSDL_DEBUG_SCHEDULER
00745         LogPlug::debug( "Scheduler::DeleteScheduler : effective deleting." ) ;
00746 #endif // OSDL_DEBUG_SCHEDULER
00747         
00748         delete Scheduler::_internalScheduler ;
00749         Scheduler::_internalScheduler = 0 ;
00750     }
00751     else
00752     {
00753     
00754 #if OSDL_DEBUG_SCHEDULER
00755         LogPlug::debug( "Scheduler::DeleteScheduler : no deleting needed." ) ;
00756 #endif // OSDL_DEBUG_SCHEDULER
00757         
00758     }
00759 
00760 }
00761 
00762 
00763 
00764 
00765 // Protected members below :
00766 
00767 
00768 Scheduler::Scheduler() throw() :
00769     _screenshotMode( false ),
00770     _desiredScreenshotFrequency( DefaultMovieFrameFrequency ),
00771     _screenshotPeriod( 0 ),
00772     _periodicSlots(),
00773     _programmedActivated(),
00774     _engineTickDuration( 0 ),
00775     _secondToEngineTick( 0 ),
00776     _currentEngineTick( 0 ),
00777     _currentSimulationTick( 0 ),
00778     _currentRenderingTick( 0 ),
00779     _currentInputTick( 0 ),
00780     _simulationPeriod( 0 ),
00781     _renderingPeriod( 0 ),
00782     _inputPeriod( 0 ),
00783     _desiredSimulationFrequency( DefaultSimulationFrequency ),
00784     _desiredRenderingFrequency( DefaultRenderingFrequency ),
00785     _desiredInputFrequency( DefaultInputFrequency ),
00786     _idleCallback( 0 ),
00787     _idleCallbackData( 0 ),
00788     _idleCallbackMaxDuration( 0 ),
00789     _idleCallsCount( 0 ),
00790     _stopRequested( false ),
00791     _scheduleStartingSecond( 0 ),
00792     _scheduleStartingMicrosecond( 0 ),
00793     _recoveredSimulationTicks( 0 ),
00794     _missedSimulationTicks( 0 ),
00795     _recoveredRenderingTicks( 0 ),
00796     _missedRenderingTicks( 0 ),
00797     _recoveredInputPollingTicks( 0 ),
00798     _missedInputPollingTicks( 0 ),
00799     _scheduleFailureCount( 0 ),
00800     _eventsModule( 0 ),
00801     _renderer( 0 ),
00802     _videoModule( 0 )
00803 {
00804 
00805     // Force precomputation for scheduling granularity, since it takes time :
00806     send( "On scheduler creation, "
00807         "detected operating system scheduling granularity is about "
00808         + Ceylan::toString( getSchedulingGranularity() ) + " microseconds." ) ;
00809 
00810     // Update _simulationPeriod, _renderingPeriod and _screenshotPeriod :
00811     setTimeSliceDuration( DefaultEngineTickDuration ) ;
00812     
00813     send( "Scheduler created." ) ;
00814     
00815 }
00816 
00817 
00818 Scheduler::~Scheduler() throw()
00819 {
00820 
00821     for ( list<PeriodicSlot *>::iterator it = _periodicSlots.begin(); 
00822             it != _periodicSlots.end(); it++ )
00823         delete (*it ) ;
00824     
00825     if ( _renderer != 0 )
00826         delete _renderer ;
00827         
00828     send( "Scheduler deleted." ) ;
00829     
00830 }
00831 
00832 
00833 void Scheduler::scheduleBestEffort() throw( SchedulingException )
00834 {
00835 
00836     /*
00837      * Set up idle callback (with default strategy) if not done already :
00838      * (will use atomic sleeps, ith a 10% margin in anticipated delay)
00839      *
00840      */
00841     if ( _idleCallback == 0 && _idleCallbackMaxDuration == 0 )
00842         _idleCallbackMaxDuration = 
00843             static_cast<Microsecond>( 1.1 * getSchedulingGranularity() ) ;
00844 
00845     EngineTick idleCallbackDuration = static_cast<EngineTick>( 
00846         Ceylan::Maths::Ceil( 
00847             static_cast<Ceylan::Float32>( _idleCallbackMaxDuration ) /
00848             _engineTickDuration ) ) ;
00849         
00850     _idleCallsCount = 0 ;
00851     
00852     send( "Scheduler starting in soft real-time best effort mode. " 
00853         "Scheduler informations : " + toString( Ceylan::low ) ) ;
00854     
00855     
00856     // Let's prepare to the run :
00857     
00858     _stopRequested = false ;
00859     
00860     _recoveredSimulationTicks = 0 ;
00861     _missedSimulationTicks    = 0 ;
00862     
00863     _recoveredRenderingTicks = 0 ;
00864     _missedRenderingTicks    = 0 ;
00865     
00866     _recoveredInputPollingTicks = 0 ;
00867     _missedInputPollingTicks    = 0 ;
00868     
00869     
00870 #if OSDL_DEBUG_SCHEDULER
00871     
00872     /*
00873      * If OSDL_DEBUG_SCHEDULER is set, run-time informations about the 
00874      * scheduler will be archived and analyzed after it stopped, like black
00875      * boxes.
00876      *
00877      */
00878      
00879     list<SimulationTick> metSimulations ;
00880     list<SimulationTick> recoveredSimulations ;
00881     list<SimulationTick> missedSimulations ;
00882     
00883     list<RenderingTick>  metRenderings ;    
00884     list<RenderingTick>  recoveredRenderings ;  
00885     list<RenderingTick>  missedRenderings ;
00886         
00887     list<InputTick>      metInputPollings ; 
00888     list<InputTick>      recoveredInputPollings ;   
00889     list<InputTick>      missedInputPollings ;  
00890     
00891 #endif // OSDL_DEBUG_SCHEDULER
00892     
00893     
00894     /*
00895      * Initial time is first measured and converted to engine, simulation,
00896      * rendering and input times :
00897      * _currentEngineTick, for an engine tick duration of 1000 (microseconds),
00898      * should wrap around if the program runs for more than 48 days. 
00899      *
00900      * If engine tick duration is divided by two, then the period until
00901      * wrap-around will be divided by two as well.
00902      *
00903      */ 
00904     getPreciseTime( _scheduleStartingSecond, _scheduleStartingMicrosecond ) ;
00905      
00906     /*
00907      * Starts with all zero ticks : 
00908      * (not using 0 constants allows to tweak the engine to make it start at
00909      * arbitrary engine tick,for debugging).
00910      *
00911      */
00912     _currentEngineTick     = computeEngineTickFromCurrentTime() ;   
00913     
00914     _currentSimulationTick = _currentEngineTick / _simulationPeriod  ;  
00915     _currentRenderingTick  = _currentEngineTick / _renderingPeriod;
00916     _currentInputTick      = _currentEngineTick / _inputPeriod;
00917     
00918     OSDL_SCHEDULE_LOG( "Simulation period : " << _simulationPeriod ) ;
00919     OSDL_SCHEDULE_LOG( "Rendering period : "  << _renderingPeriod  ) ;
00920     OSDL_SCHEDULE_LOG( "Input period : "      << _inputPeriod      ) ;
00921         
00922     EngineTick nextSimulationDeadline = _currentEngineTick + _simulationPeriod ;
00923     EngineTick nextRenderingDeadline  = _currentEngineTick + _renderingPeriod ;
00924     EngineTick nextInputDeadline      = _currentEngineTick + _inputPeriod ;
00925     
00926     EngineTick nextDeadline = Ceylan::Maths::Min( nextSimulationDeadline,
00927         nextRenderingDeadline, nextInputDeadline ) ;
00928     
00929     
00930     /*
00931      * By construction the actual frequencies can only be less than the
00932      * expected ones, which are difficult to enforce : we would never have 
00933      * 100 Hz, but often 95 Hz or so, because of OS process switching, if no 
00934      * tolerance was used for deadlines. 
00935      *
00936      * The algorithm checks the delay D (time exceeding a missed deadline) for
00937      * a given tick, if the delay is small enough
00938      * (ex for rendering ticks : D < rendering tolerance = period / 4 = 6 ms ),
00939      * then the scheduler does as if the delay had not existed : it does not
00940      * cancel or skip anything, instead it activates the rendering as usual,
00941      * despite the delay. 
00942      *
00943      * Otherwise, if the actions are really triggered too late (delay higher
00944      * than the tolerance), then the scheduler use the on*Skip methods.
00945      *
00946      *
00947      * Overloaded situations are detected thanks to a kind of leaking 
00948      * bucket : each delay of d ms fills the bucket by an amount that increase
00949      * with d (depending on the tolerance being exceeded or not), each elapsed
00950      * engine tick  makes the bucket leak a bit. 
00951      *
00952      * If the bucket gets filled up to a given threshold, then the system is
00953      * deemed overloaded (its ressources cannot keep up with what the 
00954      * application demands) and counter-measures are triggered (ex : more skips 
00955      * are triggered).
00956      *
00957      */
00958     
00959     // Delays will fill it, whereas it leaks regularly :
00960     Delay delayBucket = 0 ;
00961     
00962     // Maximum fill bucket encountered :
00963     Delay maxDelayBucket = 0 ;
00964 
00965     /*
00966      * Records all delays to compute average fill level 
00967      * (beware to the overflow) :
00968      *
00969      */
00970     Delay delayCumulativeBucket = 0 ;
00971     
00972     // When the bucket reaches that level, the machine is actually overloaded :
00973     const Delay bucketFillThreshold = 750 ;
00974 
00975     /*
00976      * Leaking factor : at each engine tick, 
00977      * (current fill level) = (previous fill level) * bucketLeakingFactor
00978      *
00979      */
00980     const Ceylan::Float32 bucketLeakingFactor = 0.9 ;
00981     
00982     _scheduleFailureCount = 0 ;
00983     
00984     
00985     /*
00986      * If a simulation tick is late of up to 50 ms, it is considered on time
00987      * neverthess, and activation takes place as normal.
00988      *
00989      * Higher delays will lead to simulation skips.
00990      *
00991      * The threshold is quite high, as we really want to avoid skipping 
00992      * simulation steps. 
00993      *
00994      * For simulation, the threshold is based on an actual (soft-realtime)
00995      * delay, not a fraction of the simulation period.
00996      *
00997      */
00998     const Microsecond simulationToleranceTime = 50000 ;
00999     
01000     const EngineTick simulationToleranceTick = static_cast<EngineTick>( 
01001         simulationToleranceTime / _engineTickDuration ) ; 
01002     
01003     send( "Simulation tolerance time is " 
01004         + Ceylan::toString( simulationToleranceTime ) + " microseconds, i.e. "
01005         + Ceylan::toString( simulationToleranceTick ) + " engine ticks." ) ;
01006         
01007         
01008     /*
01009      * If a rendering tick is late of up to 2 ms, it is considered on time
01010      * neverthess, and activation takes place as normal.
01011      *
01012      * Higher delays will lead to rendering skips.
01013      *
01014      * The threshold is quite low, as skipping rendering steps is not that
01015      * serious : next rendering will replace it, we do not <b>have</b> to
01016      * force this particular rendering to happen.
01017      *
01018      * For rendering, the threshold is based on a fraction of the rendering
01019      * period, as what it searched for is stability between renderings.
01020      *
01021      */
01022     
01023     /*
01024      * A rendering is accepted if it would be no more late than one quarter 
01025      * of its period :
01026      *
01027      */
01028     const EngineTick renderingToleranceTick = _renderingPeriod / 4 ;
01029     
01030     send( "Rendering tolerance is "
01031         + Ceylan::toString( renderingToleranceTick ) 
01032         + " engine ticks, which translates into a tolerance of " 
01033         + Ceylan::toString( renderingToleranceTick * _engineTickDuration )
01034         + " microseconds." ) ;
01035         
01036         
01037     /*
01038      * If an input tick is late of up to 2 ms, it is considered on time
01039      * neverthess, and activation takes place as normal.
01040      *
01041      * Higher delays will lead to input skips.
01042      *
01043      * The threshold is quite low, as skipping input steps is not that
01044      * serious : next input step will replace it, we do not <b>have</b> to
01045      * force this particular input polling to happen.
01046      *
01047      * For input polling, the threshold is based on a fraction of the input
01048      * period, as what it searched for is stability between renderings.
01049      *
01050      */
01051 
01052     /*
01053      * A rendering is accepted if it would be no more late than half 
01054      * of its period :
01055      *
01056      */
01057     const EngineTick inputToleranceTick = _inputPeriod / 2 ;
01058     
01059     send( "Input polling tolerance is "
01060         + Ceylan::toString( inputToleranceTick ) 
01061         + " engine ticks, which translates into a tolerance of " 
01062         + Ceylan::toString( inputToleranceTick * _engineTickDuration )
01063         + " microseconds." ) ;
01064         
01065         
01066     // Store scheduling starting time : 
01067     getPreciseTime( _scheduleStartingSecond, _scheduleStartingMicrosecond ) ;
01068     
01069     
01070     // Enter the schedule loop :
01071     
01072     while ( ! _stopRequested )
01073     {
01074         
01075         
01076         OSDL_SCHEDULE_LOG( "[ E : " << _currentEngineTick 
01077             << " ; S : " << _currentSimulationTick
01078             << " ; R : " << _currentRenderingTick       
01079             << " ; I : " << _currentInputTick 
01080             << " ; B : " << delayBucket << " ]" ) ;
01081 
01082 
01083 #if OSDL_DEBUG_SCHEDULER
01084 
01085         // Will default settings, one log per 1s :
01086         if ( _currentEngineTick % 1000 == 0 )
01087             LogPlug::debug( "[ E : " + Ceylan::toString( _currentEngineTick )
01088                 + " ; S : " + Ceylan::toString( _currentSimulationTick )
01089                 + " ; R : " + Ceylan::toString( _currentRenderingTick )
01090                 + " ; I : " + Ceylan::toString( _currentInputTick )
01091                 + " ; B : " + Ceylan::toString( delayBucket )
01092                 + " ]" ) ;          
01093                 
01094 #endif // OSDL_DEBUG_SCHEDULER
01095 
01096 
01097         delayCumulativeBucket += delayBucket ;
01098         
01099         // Bucket leaks regularly :
01100         delayBucket = static_cast<Delay>( delayBucket * bucketLeakingFactor ) ;
01101                 
01102         /* 
01103          * Normally, for each simulation tick, either scheduleSimulation 
01104          * or, if late, onSimulationSkipped are called.
01105          *
01106          * Similarly, for each rendering tick, either scheduleRendering 
01107          * or, if late, onRenderingSkipped are called.
01108          *
01109          * It is soft real-time because the engine tick is derived directly
01110          * from the system clock, it cannot drift.
01111          *
01112          */
01113                                 
01114         // We hereby suppose we are just at the beginning of a new engine tick.
01115         
01116         
01117         // What is to be done on this new engine tick ?
01118         
01119         
01120         // Perform all scheduling actions for this tick :
01121         if ( _currentEngineTick == nextSimulationDeadline )
01122         {
01123             
01124             //OSDL_SCHEDULE_LOG( "--> Simulation deadline met" ) ;
01125                 
01126 #if OSDL_DEBUG_SCHEDULER
01127             metSimulations.push_back( _currentSimulationTick ) ;
01128 #endif // OSDL_DEBUG_SCHEDULER
01129             
01130             scheduleSimulation( _currentSimulationTick ) ;
01131             
01132             _currentSimulationTick++ ;
01133             nextSimulationDeadline += _simulationPeriod ;
01134             
01135         }
01136         // Time has elapsed during simulation....
01137 
01138 
01139         if ( _currentEngineTick == nextRenderingDeadline )
01140         {
01141         
01142             //OSDL_SCHEDULE_LOG( "--> Rendering deadline met" ) ;
01143                 
01144 #if OSDL_DEBUG_SCHEDULER
01145             metRenderings.push_back( _currentRenderingTick ) ;
01146 #endif // OSDL_DEBUG_SCHEDULER
01147 
01148             scheduleRendering( _currentRenderingTick ) ;
01149 
01150             _currentRenderingTick++ ;
01151             nextRenderingDeadline += _renderingPeriod ;
01152             
01153         }
01154         // Still more time elapsed in the rendering....
01155 
01156 
01157         if ( _currentEngineTick == nextInputDeadline )
01158         {
01159         
01160             //OSDL_SCHEDULE_LOG( "--> Input deadline met" ) ;
01161                 
01162 #if OSDL_DEBUG_SCHEDULER
01163             metInputPollings.push_back( _currentInputTick ) ;
01164 #endif // OSDL_DEBUG_SCHEDULER
01165 
01166             scheduleInput( _currentInputTick ) ;
01167 
01168             _currentInputTick++ ;
01169             nextInputDeadline += _inputPeriod ;
01170             
01171         }
01172         // Everything done, hoping not too much time elapsed....
01173 
01174         
01175         /*
01176          * Check we did not miss any deadline : if these scheduling actions
01177          * (notably, the rendering) last more than the current engine 
01178          * tick, it is not a problem.
01179          *
01180          * What must not occur is that these actions lasted so long that 
01181          * the first next deadline (either simulation, rendering or input), 
01182          * was missed.
01183          *
01184          * The next '+1' is here because the soonest possible deadlines 
01185          * will be on next engine tick.
01186          * Engine time is regularly updated since any call may last for 
01187          * non negligible durations.
01188          * 
01189          * As multiple steps can be missed, a while structure is required.
01190          *
01191          */
01192         _currentEngineTick = computeEngineTickFromCurrentTime() ;
01193         
01194         while ( nextSimulationDeadline < _currentEngineTick + 1 )   
01195         {           
01196         
01197             // Manage all missed simulation steps and warn :
01198             EngineTick missedTicks = 
01199                 _currentEngineTick + 1 - nextSimulationDeadline ;
01200             
01201             if ( missedTicks > simulationToleranceTick )
01202             {
01203             
01204                 OSDL_SCHEDULE_LOG( "##### Simulation deadline missed of "
01205                     + Ceylan::toString( missedTicks )
01206                     + " engine ticks (" 
01207                     + Ceylan::toString( missedTicks * _engineTickDuration )  
01208                     + " microseconds), cancelling activations." ) ;
01209             
01210                 /*
01211                  * Missing a simulation deadline is serious, so the bucket
01212                  * needs to fill quickly if tolerance is exceeded. 
01213                  *
01214                  * However there happens sometimes rare glitches (Dirac peaks)
01215                  * that lead to very significant delays.
01216                  *
01217                  * They are one-shot, and with an affine delay function they
01218                  * would saturate for too long the bucket, whereas the 
01219                  * computer can actually face the average load quite well.
01220                  *
01221                  * Hence we chose an increasing delay function which does not
01222                  * lead to explosive values for high arguments : the square
01223                  * root, more precisely f : d -> 35.log( d )
01224                  * f(500) is about 138
01225                  * 
01226                  * (let's hope the square root is not too expensive)
01227                  *
01228                  * Check with gnuplot :
01229                  * set xrange [0:1000]
01230                  * set autoscale
01231                  * plot 20 * log( 2 * x )
01232                  *
01233                  */
01234                 delayBucket += 20 * static_cast<Delay>( 
01235                     Ceylan::Maths::Log( 2.0f * missedTicks ) ) ;
01236 
01237 #if OSDL_DEBUG_SCHEDULER
01238                 missedSimulations.push_back( _currentSimulationTick ) ;
01239 #endif // OSDL_DEBUG_SCHEDULER
01240             
01241                 
01242                 /*
01243                  * If ever this call is longer than the simulation period, the  
01244                  * scheduler will go in an infinite loop :
01245                  *
01246                  */
01247                 onSimulationSkipped( _currentSimulationTick ) ;
01248             
01249                 
01250             }   
01251             else
01252             {
01253             
01254                 // No onSimulationRecovered really needed :
01255                 _recoveredSimulationTicks++ ;
01256                 
01257                 // This will not be resolved on next engine tick finally :
01258                 missedTicks-- ;
01259                 
01260                 OSDL_SCHEDULE_LOG( "@@@@@ Simulation deadline recovered from "
01261                     + Ceylan::toString( missedTicks )
01262                     + " engine ticks delay (" 
01263                     + Ceylan::toString( missedTicks * _engineTickDuration )  
01264                     + " microseconds)." ) ;
01265                 
01266                 // Small constant penalty : 
01267                 delayBucket += 8 ;
01268 
01269 #if OSDL_DEBUG_SCHEDULER
01270                 recoveredSimulations.push_back( _currentSimulationTick ) ;
01271 #endif // OSDL_DEBUG_SCHEDULER
01272             
01273                 scheduleSimulation( _currentSimulationTick ) ;
01274             
01275             }
01276 
01277             _currentSimulationTick++ ;
01278             
01279             nextSimulationDeadline += _simulationPeriod ;
01280             
01281             _currentEngineTick = computeEngineTickFromCurrentTime() ;
01282                         
01283         }
01284         
01285         
01286         /*
01287          * As simulation ticks are skipped before rendering ticks, the 
01288          * laters should be, on average, more frequently skipped than the
01289          * formers, since skipping simulation ticks might take some time, 
01290          * even as much time as if there had not been skipped, since the 
01291          * objects can call their onActivation method for their onSkip method.
01292          * 
01293          * This is a way of setting a higher priority to simulation ticks,
01294          * compared to rendering ticks.
01295          *
01296          */      
01297         while ( nextRenderingDeadline < _currentEngineTick + 1 )    
01298         {           
01299         
01300             // Manage all missed rendering steps and warn :
01301             EngineTick missedTicks = 
01302                 _currentEngineTick + 1 - nextRenderingDeadline ;
01303 
01304             if ( missedTicks > renderingToleranceTick )
01305             {
01306 
01307                 OSDL_SCHEDULE_LOG( "##### Rendering deadline missed of "
01308                     + Ceylan::toString( missedTicks )
01309                     + " engine ticks (" 
01310                     + Ceylan::toString( missedTicks * _engineTickDuration )  
01311                     + " microseconds), cancelling rendering." ) ;
01312 
01313                 // Missing a rendering deadline is annoying :
01314                 delayBucket += 2 * static_cast<Delay>( 
01315                     Ceylan::Maths::Log( 2.0f * missedTicks ) ) ;
01316                     
01317                                 
01318 #if OSDL_DEBUG_SCHEDULER
01319                 missedRenderings.push_back( _currentRenderingTick ) ;
01320 #endif // OSDL_DEBUG_SCHEDULER
01321             
01322                 onRenderingSkipped( _currentRenderingTick ) ;
01323             
01324             }
01325             else
01326             {   
01327             
01328                 // No onRenderingRecovered really needed :
01329                 _recoveredRenderingTicks++ ;
01330             
01331                 // This will not be resolved on next engine tick finally :
01332                 missedTicks-- ;
01333 
01334                 OSDL_SCHEDULE_LOG( "@@@@@ Rendering deadline recovered from "
01335                     + Ceylan::toString( missedTicks )
01336                     + " engine ticks delay (" 
01337                     + Ceylan::toString( missedTicks * _engineTickDuration )  
01338                     + " microseconds)." ) ;
01339                 
01340                 // Small constant penalty : 
01341                 delayBucket += 4 ;
01342 
01343 #if OSDL_DEBUG_SCHEDULER
01344                 recoveredRenderings.push_back( _currentRenderingTick ) ;
01345 #endif // OSDL_DEBUG_SCHEDULER
01346 
01347                 scheduleRendering( _currentRenderingTick ) ;
01348             
01349             }
01350             
01351             _currentRenderingTick++ ;
01352             
01353             nextRenderingDeadline += _renderingPeriod ;
01354             
01355             _currentEngineTick = computeEngineTickFromCurrentTime() ;
01356                         
01357         }
01358         
01359         
01360         /*
01361          * Lastly, take care of the input polling :
01362          *
01363          */
01364         while ( nextInputDeadline < _currentEngineTick + 1 )    
01365         {           
01366         
01367             /*
01368              * Manage all missed input steps and warn (this is not too 
01369              * serious skip) :
01370              *
01371              */
01372             EngineTick missedTicks = 
01373                 _currentEngineTick + 1 - nextInputDeadline ;
01374 
01375             if ( missedTicks > inputToleranceTick )
01376             {
01377             
01378                 OSDL_SCHEDULE_LOG( "##### Input deadline missed of "
01379                     + Ceylan::toString( missedTicks )
01380                     + " engine ticks (" 
01381                     + Ceylan::toString( missedTicks * _engineTickDuration )  
01382                     + " microseconds), cancelling input polling." ) ;
01383 
01384                 // Missing an input deadline should be avoided :
01385                 delayBucket += static_cast<Delay>( 
01386                     Ceylan::Maths::Log( 2.0f * missedTicks ) ) ;
01387             
01388 #if OSDL_DEBUG_SCHEDULER
01389                 missedInputPollings.push_back( _currentInputTick ) ;
01390 #endif // OSDL_DEBUG_SCHEDULER
01391             
01392                 onInputSkipped( _currentInputTick ) ;
01393             
01394             }
01395             else
01396             {
01397             
01398                 // No onInputPollingRecovered really needed :
01399                 _recoveredInputPollingTicks++ ;
01400                 
01401                 // This will not be resolved on next engine tick finally :
01402                 missedTicks-- ;
01403 
01404                 OSDL_SCHEDULE_LOG( "@@@@@ Input deadline recovered from "
01405                     + Ceylan::toString( missedTicks )
01406                     + " engine ticks delay (" 
01407                     + Ceylan::toString( missedTicks * _engineTickDuration )  
01408                     + " microseconds)." ) ;
01409                 
01410                 // Small constant penalty : 
01411                 delayBucket += 2 ;
01412 
01413 #if OSDL_DEBUG_SCHEDULER
01414                 recoveredInputPollings.push_back( _currentInputTick ) ;
01415 #endif // OSDL_DEBUG_SCHEDULER
01416 
01417                 scheduleInput( _currentInputTick ) ;
01418             
01419             
01420             }   
01421 
01422             _currentInputTick++ ;
01423             
01424             nextInputDeadline += _inputPeriod ;
01425             
01426             _currentEngineTick = computeEngineTickFromCurrentTime() ;
01427                         
01428         }
01429                 
01430         if ( maxDelayBucket < delayBucket )
01431             maxDelayBucket = delayBucket ;
01432 
01433         // Maybe counter-measures will be taken here :
01434         if ( delayBucket > bucketFillThreshold )
01435             onScheduleFailure( delayBucket ) ;
01436 
01437         /*
01438          * Will wait until next deadline, possibly skipping several engine
01439          * ticks :
01440          *
01441          */
01442         nextDeadline = Ceylan::Maths::Min( nextSimulationDeadline,
01443             nextRenderingDeadline, nextInputDeadline ) ;
01444 
01445         if ( nextDeadline < _currentEngineTick )
01446         {
01447         
01448             string message = "We are at engine tick #" 
01449                 + Ceylan::toString( _currentEngineTick ) 
01450                 + ", already late for next deadline, which is at "
01451                 + Ceylan::toString( nextDeadline ) + "." ;
01452             
01453             OSDL_SCHEDULE_LOG( message ) ;
01454             LogPlug::warning( message ) ;
01455             
01456             // Delay will be taken care of at next iteration :
01457             continue ;
01458         }
01459         else if ( nextDeadline <= _currentEngineTick + 1 )
01460         {
01461             
01462             /*
01463              * nextDeadline is this current engine tick, or the next one.
01464              * In both cases it is to be handled immediately, no special
01465              * jump to perform :
01466              *
01467              */
01468             OSDL_SCHEDULE_LOG( 
01469                 "No jump could be performed, just continuing." ) ;
01470             
01471         }
01472         else
01473         {
01474             // Here nextDeadline > _currentEngineTick + 1, hence jumpLength > 0.
01475         
01476             /*
01477              * Computes the intermediate delays we are jumping over :
01478              *
01479              */
01480             EngineTick jumpLength = nextDeadline - _currentEngineTick - 1 ;
01481 
01482             OSDL_SCHEDULE_LOG( "Next deadline is " 
01483                 + Ceylan::toString( jumpLength + 1 ) 
01484                 + " engine tick(s) away." ) ;
01485 
01486 
01487             // Leaks as if there had been no jump in engine ticks :
01488             while ( jumpLength > 0 )
01489             {
01490         
01491                 delayBucket = static_cast<Delay>( 
01492                     delayBucket * bucketLeakingFactor ) ;
01493                 delayCumulativeBucket += delayBucket ;
01494                 jumpLength-- ;
01495             
01496             } ;
01497 
01498         }
01499         
01500         /*
01501          * Do nothing : wait, first with idle callback (which results in
01502          * atomic sleeps by default), then for fine-grain with soft busy
01503          * waitings.
01504          *
01505          */
01506         
01507         // Atomic sleeps :       
01508         while ( _currentEngineTick + idleCallbackDuration < nextDeadline )
01509         {
01510             
01511             /*
01512              * OSDL_SCHEDULE_LOG( 
01513              * "Waiting for the end of engine tick " << _currentEngineTick ) ;
01514              *
01515              */
01516             onIdle() ;
01517             
01518             _currentEngineTick = computeEngineTickFromCurrentTime() ;
01519             
01520         }   
01521         
01522         
01523         // Then busy waiting (still quite soft due to getPreciseTime calls) :
01524         while ( _currentEngineTick < nextDeadline )
01525             _currentEngineTick = computeEngineTickFromCurrentTime() ;
01526         
01527         /*
01528          * OSDL_SCHEDULE_LOG( 
01529          * "End of schedule loop, engine tick being " << _currentEngineTick ) ;
01530          *
01531          */
01532         
01533         
01534     } // End of scheduler overall loop
01535 
01536 
01537     Second scheduleStoppingSecond ;
01538     Microsecond scheduleStoppingMicrosecond ;
01539     
01540     getPreciseTime( scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
01541     
01542     // It is equal to 1E6 / ( total runtime in microseconds ) :
01543     Ceylan::Float64 totalRuntimeFactor ;
01544     
01545     // Avoid overflows :
01546     if ( scheduleStoppingSecond - _scheduleStartingSecond <
01547             MaximumDurationWithMicrosecondAccuracy )
01548     {   
01549          
01550         totalRuntimeFactor = 1E6 / getDurationBetween( 
01551             _scheduleStartingSecond, _scheduleStartingMicrosecond,
01552             scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
01553             
01554     }
01555     else
01556     {
01557     
01558         // It is just a (rather good , error below 1/4100) approximation :
01559         totalRuntimeFactor =  1.0f / 
01560             ( scheduleStoppingSecond - _scheduleStartingSecond ) ;
01561         
01562     }
01563     
01564     
01565     string table = 
01566         "<table border=1>"
01567         "   <tr>"
01568         "       <th>Tick types</th>"
01569         "       <th>Requested frequency</th>" 
01570         "       <th>Agreed frequency</th>" 
01571         "       <th>Measured frequency</th>" 
01572         "       <th>Tick duration (microsec)</th>" 
01573         "       <th>Tick duration (engine ticks)</th>" 
01574         "       <th>Stopped at tick</th>" 
01575         "       <th>Recovered count</th>"
01576         "       <th>Recovered percentage</th>"
01577         "       <th>Missed count</th>"
01578         "       <th>Missed percentage</th>"
01579         "   </tr>" ;
01580     
01581     
01582     Ceylan::Uint8 precision = 2 /* digits after the dot */ ;
01583     
01584     // Engine :
01585     table += 
01586         "   <tr>"
01587         "       <td>Engine tick</td>"
01588         "       <td>" 
01589         + Ceylan::toString( 1E6 / DefaultEngineTickDuration, precision ) 
01590         + " Hz</td>"
01591         "       <td>" 
01592         + Ceylan::toString( 1E6 / DefaultEngineTickDuration, precision ) 
01593         + " Hz</td>"
01594         "       <td>" 
01595         + Ceylan::toString( _currentEngineTick * totalRuntimeFactor,
01596             precision )
01597         + " Hz</td>"
01598         "       <td>" + Ceylan::toString( _engineTickDuration ) + "</td>"
01599         "       <td>1</td>"
01600         "       <td>" + Ceylan::toString( _currentEngineTick ) + "</td>"
01601         "       <td>N/A</td>"       
01602         "       <td>N/A</td>"       
01603         "       <td>N/A</td>"       
01604         "       <td>N/A</td>"       
01605         "   </tr>" ;
01606         
01607         
01608     // Simulation : 
01609     table += 
01610         "   <tr>"
01611         "       <td>Simulation tick</td>"
01612         "       <td>" 
01613         + Ceylan::toString( _desiredSimulationFrequency ) 
01614         + " Hz</td>"
01615         "       <td>" 
01616         + Ceylan::toString( 1E6 / ( _simulationPeriod * _engineTickDuration ), 
01617             precision ) + " Hz</td>"
01618         "       <td>"
01619         + Ceylan::toString( 
01620             ( _currentSimulationTick - _missedSimulationTicks ) *
01621                 totalRuntimeFactor, precision ) + " Hz</td>"
01622         "       <td>" + Ceylan::toString( 
01623             _simulationPeriod * _engineTickDuration ) + "</td>"
01624         "       <td>" + Ceylan::toString( _simulationPeriod ) + "</td>"
01625         "       <td>" + Ceylan::toString( _currentSimulationTick ) + "</td>"
01626         "       <td>" + Ceylan::toString( _recoveredSimulationTicks ) + "</td>" 
01627         "       <td>" + Ceylan::toString( 
01628             100.0f * _recoveredSimulationTicks / _currentSimulationTick, 
01629             precision ) 
01630         + "%</td>"  
01631         "       <td>" + Ceylan::toString( _missedSimulationTicks ) + "</td>"    
01632         "       <td>" + Ceylan::toString( 
01633             100.0f * _missedSimulationTicks / _currentSimulationTick, 
01634             precision ) 
01635         + "%</td>"  
01636         "   </tr>" ;
01637         
01638         
01639     // Rendering :  
01640     table += 
01641         "   <tr>"
01642         "       <td>Rendering tick</td>"
01643         "       <td>" 
01644         + Ceylan::toString( _desiredRenderingFrequency ) 
01645         + " Hz</td>"
01646         "       <td>" 
01647         + Ceylan::toString( 1E6 / ( _renderingPeriod * _engineTickDuration ), 
01648             precision ) + " Hz</td>"
01649         "       <td>"
01650         + Ceylan::toString( 
01651             ( _currentRenderingTick - _missedRenderingTicks ) * 
01652                 totalRuntimeFactor, precision ) + " Hz</td>"
01653         "       <td>" + Ceylan::toString( 
01654             _renderingPeriod * _engineTickDuration ) + "</td>"
01655         "       <td>" + Ceylan::toString( _renderingPeriod ) + "</td>"
01656         "       <td>" + Ceylan::toString( _currentRenderingTick ) + "</td>"
01657         "       <td>" + Ceylan::toString( _recoveredRenderingTicks ) + "</td>"  
01658         "       <td>" + Ceylan::toString( 
01659             100.0f * _recoveredRenderingTicks / _currentRenderingTick, 
01660                 precision ) 
01661         + "%</td>"  
01662         "       <td>" + Ceylan::toString( _missedRenderingTicks ) + "</td>" 
01663         "       <td>" + Ceylan::toString( 
01664             100.0f * _missedRenderingTicks / _currentRenderingTick, precision ) 
01665         + "%</td>"  
01666         "   </tr>" ;
01667         
01668     // Input :  
01669     table += 
01670         "   <tr>"
01671         "       <td>Input polling tick</td>"
01672         "       <td>" 
01673         + Ceylan::toString( _desiredInputFrequency ) 
01674         + " Hz</td>"
01675         "       <td>" 
01676         + Ceylan::toString( 1E6 / ( _inputPeriod * _engineTickDuration ), 
01677             precision ) + " Hz</td>"
01678         "       <td>"
01679         + Ceylan::toString( 
01680             ( _currentInputTick - _missedInputPollingTicks ) * 
01681                 totalRuntimeFactor, precision ) + " Hz</td>"
01682         "       <td>" + Ceylan::toString( 
01683             _inputPeriod * _engineTickDuration ) + "</td>"
01684         "       <td>" + Ceylan::toString( _inputPeriod ) + "</td>"
01685         "       <td>" + Ceylan::toString( _currentInputTick ) + "</td>"
01686         "       <td>" + Ceylan::toString( _recoveredInputPollingTicks ) 
01687         + "</td>"   
01688         "       <td>" + Ceylan::toString( 
01689             100.0f * _recoveredInputPollingTicks / _currentInputTick, 
01690                 precision ) 
01691         + "%</td>"  
01692         "       <td>" + Ceylan::toString( _missedInputPollingTicks ) + "</td>"  
01693         "       <td>" + Ceylan::toString( 
01694             100.0f * _missedInputPollingTicks / _currentInputTick, precision ) 
01695         + "%</td>"  
01696         "   </tr>" ;
01697         
01698     table += "</table>" ;   
01699     
01700     if ( Ceylan::TextDisplayable::GetOutputFormat() ==
01701         Ceylan::TextDisplayable::html )
01702     send( table ) ;
01703     
01704     list<string> summary ;
01705     
01706     string durationComment = "The scheduler ran for " 
01707         + Ceylan::Timestamp::DescribeDuration( 
01708             scheduleStoppingSecond - _scheduleStartingSecond ) ;
01709             
01710     
01711     if ( scheduleStoppingSecond - _scheduleStartingSecond <
01712             MaximumDurationWithMicrosecondAccuracy )            
01713         durationComment += ", more precisely " 
01714             + Ceylan::toString( getDurationBetween(
01715                 _scheduleStartingSecond, _scheduleStartingMicrosecond,
01716                 scheduleStoppingSecond, scheduleStoppingMicrosecond ) )
01717             + " microseconds." ;
01718     else
01719         durationComment += "." ;
01720             
01721     summary.push_back( durationComment ) ;
01722     
01723     summary.push_back( Ceylan::toString( _idleCallsCount ) 
01724         + " idle calls have been made." ) ;
01725 
01726     summary.push_back( "Each idle call was expected to last for "
01727         + Ceylan::toString( _idleCallbackMaxDuration ) + " microseconds, i.e. "
01728         + Ceylan::toString( idleCallbackDuration ) + " engine ticks." ) ;
01729 
01730     summary.push_back( "Average bucket level has been " 
01731         + Ceylan::toString( 
01732             ( 1.0f * delayCumulativeBucket ) / _currentEngineTick,
01733             /* precision */ 2 ) 
01734         + ", maximum bucket level has been "
01735         + Ceylan::toString( maxDelayBucket ) 
01736         + ", bucket fill threshold is " 
01737         + Ceylan::toString( bucketFillThreshold )
01738         + ", this threshold has been reached "
01739         + Ceylan::toString( _scheduleFailureCount )
01740         + " times, shutdown bucket level is "
01741         + Ceylan::toString( ShutdownBucketLevel) + "." ) ;
01742     
01743     send( "Scheduler stopping, run summary is : " 
01744         + Ceylan::formatStringList( summary ) ) ;
01745     
01746     send( "Full scheduler infos : " + toString( Ceylan::high ) ) ;  
01747 
01748     _scheduleStartingSecond = 0 ;
01749     _scheduleStartingMicrosecond = 0 ;
01750     
01751             
01752 #if OSDL_DEBUG_SCHEDULER
01753     
01754     /*
01755      * Collect some informations, and check them to assess the scheduler
01756      * behaviour.
01757      *
01758      */
01759 
01760     bool beVerbose = true ;
01761     bool beVeryVerbose = true ;
01762     
01763     
01764     
01765     // For simulation ticks :           
01766     
01767     SimulationTick totalSimulationTicks = metSimulations.size() 
01768         + recoveredSimulations.size() + missedSimulations.size() ;
01769         
01770     LogPlug::debug( "Total simulation ticks : " 
01771         + Ceylan::toString( totalSimulationTicks ) + "." ) ;
01772 
01773     LogPlug::debug( "Directly met simulation ticks : " 
01774         + Ceylan::toString( metSimulations.size() ) 
01775         + " (" + Ceylan::toString( 100.0f * metSimulations.size() 
01776             / totalSimulationTicks, /* precision */ 2 ) + "%)." ) ;
01777                         
01778     LogPlug::debug( "Recovered (indirectly met) simulation ticks : " 
01779         + Ceylan::toString( recoveredSimulations.size() ) 
01780         + " (" + Ceylan::toString( 100.0f * recoveredSimulations.size() 
01781             / totalSimulationTicks, /* precision */ 2 ) + "%)." ) ;
01782 
01783     if ( recoveredSimulations.size() != _recoveredSimulationTicks )
01784         LogPlug::error( "Inconsistency in recovered simulation count." ) ;
01785 
01786                 
01787     LogPlug::debug( "Missed simulation ticks : " 
01788         + Ceylan::toString( missedSimulations.size() ) 
01789         + " (" + Ceylan::toString( 100.0f * missedSimulations.size() 
01790             / totalSimulationTicks, /* precision */ 2 ) + "%)." ) ;
01791     
01792     if ( missedSimulations.size() != _missedSimulationTicks )
01793         LogPlug::error( "Inconsistency in missed simulation count." ) ;
01794 
01795     if ( _currentSimulationTick - _recoveredSimulationTicks 
01796             - _missedSimulationTicks != metSimulations.size() )
01797         LogPlug::error( "Inconsistency in overall simulation count." ) ;
01798             
01799              
01800     string res ;
01801     
01802 
01803     if ( beVerbose )
01804     {
01805     
01806         if ( beVeryVerbose )
01807         {
01808         
01809             for ( list<SimulationTick>::const_iterator it 
01810                     = metSimulations.begin(); it != metSimulations.end(); it++ )
01811                 res +=  Ceylan::toString( *it ) + " - " ;
01812          
01813             LogPlug::debug( "Met simulation ticks : " + res ) ;
01814             res.clear() ;
01815     
01816         }
01817         
01818         
01819         for ( list<SimulationTick>::const_iterator it 
01820                 = recoveredSimulations.begin(); 
01821                     it != recoveredSimulations.end(); it++ )
01822             res +=  Ceylan::toString( *it ) + " - " ;
01823 
01824         LogPlug::debug( "Recovered simulation ticks : " + res ) ;
01825         res.clear() ;
01826         
01827         for ( list<SimulationTick>::const_iterator it 
01828                 = missedSimulations.begin(); it != missedSimulations.end(); 
01829                 it++ )
01830             res +=  Ceylan::toString( *it ) + " - " ;
01831 
01832         LogPlug::debug( "Missed simulation ticks : " + res ) ;
01833         res.clear() ;
01834     }
01835     
01836         
01837     /*
01838      * Check that all simulation ticks were handled one and only one time, 
01839      * one way or another (scheduled : met or recovered, or skipped).
01840      *
01841      */
01842     SimulationTick newSimulationTick ;
01843     
01844     bool * simulationTicks = new bool[ _currentSimulationTick ] ;
01845     
01846     for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
01847         simulationTicks[i] = false ;
01848         
01849     for ( list<SimulationTick>::const_iterator it = metSimulations.begin(); 
01850         it != metSimulations.end(); it++ )
01851     {
01852     
01853         newSimulationTick = (*it) ;
01854         
01855         if ( simulationTicks[ newSimulationTick ] == false )
01856             simulationTicks[ newSimulationTick ] = true ;
01857         else
01858             LogPlug::error( "Met simulation tick #" 
01859                 + Ceylan::toString( newSimulationTick ) 
01860                 + " should not have been scheduled more than once." ) ; 
01861                         
01862     }
01863     
01864     
01865     for ( list<SimulationTick>::const_iterator it =
01866         recoveredSimulations.begin(); it != recoveredSimulations.end(); it++ )
01867     {
01868     
01869         newSimulationTick = (*it) ;
01870         
01871         if ( simulationTicks[ newSimulationTick ] == false )
01872             simulationTicks[ newSimulationTick ] = true ;
01873         else
01874             LogPlug::error( "Recovered simulation tick #" 
01875                 + Ceylan::toString( newSimulationTick ) 
01876                 + " should not have been scheduled more than once." ) ; 
01877                         
01878     }
01879     
01880     
01881     for ( list<SimulationTick>::const_iterator it = missedSimulations.begin(); 
01882         it != missedSimulations.end(); it++ )
01883     {
01884     
01885         newSimulationTick = (*it) ;
01886         
01887         if ( simulationTicks[ newSimulationTick ] == false )
01888             simulationTicks[ newSimulationTick ] = true ;
01889         else
01890             LogPlug::error( "Missed simulation tick #" 
01891                 + Ceylan::toString( newSimulationTick ) 
01892                 + " should not have been skipped, "
01893                 "since had already been taken into account." ) ;
01894                             
01895     }
01896     
01897     for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
01898         if ( simulationTicks[ i ] == false )
01899             LogPlug::error( "Simulation tick #" + Ceylan::toString( i ) 
01900                 + " has never been scheduled." ) ;  
01901                 
01902     delete simulationTicks ;
01903     
01904 
01905 
01906     // For rendering ticks :            
01907 
01908     RenderingTick totalRenderingTicks = metRenderings.size() 
01909         + recoveredRenderings.size() + missedRenderings.size() ;
01910 
01911     LogPlug::debug( "Total rendering ticks : " 
01912         + Ceylan::toString( totalRenderingTicks ) + "." ) ;
01913         
01914     LogPlug::debug( "Directly met rendering ticks : " 
01915         + Ceylan::toString( metRenderings.size() ) 
01916         + " (" + Ceylan::toString( 100.0f * metRenderings.size() 
01917             / totalRenderingTicks, /* precision */ 2 ) + "%)." ) ;
01918                 
01919     LogPlug::debug( "Recovered (indirectly met) rendering ticks : " 
01920         + Ceylan::toString( recoveredRenderings.size() ) 
01921         + " (" + Ceylan::toString( 100.0f * recoveredRenderings.size() 
01922             / totalRenderingTicks, /* precision */ 2 ) + "%)." ) ;
01923 
01924     if ( recoveredRenderings.size() != _recoveredRenderingTicks )
01925         LogPlug::error( "Inconsistency in recovered rendering count." ) ;
01926 
01927                 
01928     LogPlug::debug( "Missed rendering ticks : " 
01929         + Ceylan::toString( missedRenderings.size() ) 
01930         + " (" + Ceylan::toString( 100.0f * missedRenderings.size() 
01931             / totalRenderingTicks, /* precision */ 2 ) + "%)." ) ;
01932 
01933     if ( missedRenderings.size() != _missedRenderingTicks )
01934         LogPlug::error( "Inconsistency in missed rendering count." ) ;
01935 
01936     if ( _currentRenderingTick - _recoveredRenderingTicks 
01937             - _missedRenderingTicks != metRenderings.size() )
01938         LogPlug::error( "Inconsistency in overall rendering count." ) ;
01939                 
01940         
01941                 
01942     if ( beVerbose )
01943     {
01944     
01945         if ( beVeryVerbose )
01946         {
01947         
01948             for ( list<RenderingTick>::const_iterator it 
01949                     = metRenderings.begin(); it != metRenderings.end(); it++ )
01950                 res +=  Ceylan::toString( *it ) + " - " ;
01951          
01952             LogPlug::debug( "Met rendering ticks : " + res ) ;
01953             res.clear() ;
01954     
01955         }
01956         
01957         for ( list<RenderingTick>::const_iterator it 
01958                 = recoveredRenderings.begin(); 
01959                 it != recoveredRenderings.end(); it++ )
01960             res +=  Ceylan::toString( *it ) + " - " ;
01961 
01962         LogPlug::debug( "Recovered rendering ticks : " + res ) ;
01963         res.clear() ;
01964 
01965         for ( list<RenderingTick>::const_iterator it 
01966                 = missedRenderings.begin(); it != missedRenderings.end(); it++ )
01967             res +=  Ceylan::toString( *it ) + " - " ;
01968 
01969         LogPlug::debug( "Missed rendering ticks : " + res ) ;
01970         res.clear() ;
01971 
01972     }
01973     
01974                 
01975     /*
01976      * Check that all rendering ticks were handled one and only one time, 
01977      * one way or another (scheduled : met or recovered, or skipped).
01978      *
01979      */
01980     
01981     RenderingTick newRenderingTick ;
01982     
01983     bool * renderingTicks = new bool[ _currentRenderingTick ] ;
01984     
01985     for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
01986         renderingTicks[i] = false ;
01987         
01988     for ( list<RenderingTick>::const_iterator it = metRenderings.begin(); 
01989         it != metRenderings.end(); it++ )
01990     {
01991     
01992         newRenderingTick = (*it) ;
01993         if ( renderingTicks[ newRenderingTick ] == false )
01994             renderingTicks[ newRenderingTick ] = true ;
01995         else
01996             LogPlug::error( "Met rendering tick #" 
01997                 + Ceylan::toString( newRenderingTick ) 
01998                 + " should not have been scheduled more than once." ) ;     
01999                 
02000     }
02001     
02002     for ( list<RenderingTick>::const_iterator it = 
02003         recoveredRenderings.begin(); it != recoveredRenderings.end(); it++ )
02004     {
02005     
02006         newRenderingTick = (*it) ;
02007         if ( renderingTicks[ newRenderingTick ] == false )
02008             renderingTicks[ newRenderingTick ] = true ;
02009         else
02010             LogPlug::error( "Recovered rendering tick #" 
02011                 + Ceylan::toString( newRenderingTick ) 
02012                 + " should not have been scheduled more than once." ) ;     
02013                 
02014     }
02015     
02016     
02017     for ( list<RenderingTick>::const_iterator it = missedRenderings.begin(); 
02018         it != missedRenderings.end(); it++ )
02019     {
02020         newRenderingTick = (*it) ;
02021         if ( renderingTicks[ newRenderingTick ] == false )
02022             renderingTicks[ newRenderingTick ] = true ;
02023         else
02024             LogPlug::error( "Missed rendering tick #" 
02025                 + Ceylan::toString( newRenderingTick ) 
02026                 + " should not have been been skipped, "
02027                 "since had already been taken into account." ) ;
02028                             
02029     }
02030     
02031     for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
02032         if ( renderingTicks[ i ] == false )
02033             LogPlug::error( "Rendering tick #" + Ceylan::toString( i ) 
02034                 + " has never been scheduled." ) ;  
02035 
02036     delete renderingTicks ;
02037 
02038 
02039 
02040     // For input ticks :            
02041 
02042 
02043     InputTick totalInputTicks = metInputPollings.size() 
02044         + recoveredInputPollings.size() + missedInputPollings.size() ;
02045 
02046     LogPlug::debug( "Total input ticks : " 
02047         + Ceylan::toString( totalInputTicks ) + "." ) ;
02048         
02049     LogPlug::debug( "Directly met input ticks : " 
02050         + Ceylan::toString( metInputPollings.size() ) 
02051         + " (" + Ceylan::toString( 100.0f * metInputPollings.size() 
02052             / totalInputTicks, /* precision */ 2 ) + "%)." ) ;
02053                 
02054     LogPlug::debug( "Recovered (indirectly met) input ticks : " 
02055         + Ceylan::toString( recoveredInputPollings.size() ) 
02056         + " (" + Ceylan::toString( 100.0f * recoveredInputPollings.size() 
02057             / totalInputTicks, /* precision */ 2 ) + "%)." ) ;
02058 
02059     if ( recoveredInputPollings.size() != _recoveredInputPollingTicks )
02060         LogPlug::error( "Inconsistency in recovered input polling count." ) ;
02061             
02062                 
02063     LogPlug::debug( "Missed input ticks : " 
02064         + Ceylan::toString( missedInputPollings.size() ) 
02065         + " (" + Ceylan::toString( 100.0f * missedInputPollings.size() 
02066             / totalInputTicks, /* precision */ 2 ) + "%)." ) ;
02067                 
02068     if ( missedInputPollings.size() != _missedInputPollingTicks )
02069         LogPlug::error( "Inconsistency in missed input polling count." ) ;
02070 
02071     if ( _currentInputTick - _recoveredInputPollingTicks 
02072             - _missedInputPollingTicks != metInputPollings.size() )
02073         LogPlug::error( "Inconsistency in overall input polling count." ) ;
02074         
02075                 
02076     if ( beVerbose )
02077     {
02078     
02079         if ( beVeryVerbose )
02080         {
02081         
02082             for ( list<InputTick>::const_iterator it 
02083                     = metInputPollings.begin(); 
02084                     it != metInputPollings.end(); it++ )
02085                 res +=  Ceylan::toString( *it ) + " - " ;
02086          
02087             LogPlug::debug( "Met input ticks : " + res ) ;
02088             res.clear() ;
02089     
02090         }
02091         
02092         for ( list<InputTick>::const_iterator it 
02093                 = recoveredInputPollings.begin(); 
02094                 it != recoveredInputPollings.end(); it++ )
02095             res +=  Ceylan::toString( *it ) + " - " ;
02096 
02097         LogPlug::debug( "Recovered input ticks : " + res ) ;
02098         res.clear() ;
02099 
02100         for ( list<InputTick>::const_iterator it 
02101                 = missedInputPollings.begin(); 
02102                 it != missedInputPollings.end(); it++ )
02103             res +=  Ceylan::toString( *it ) + " - " ;
02104 
02105         LogPlug::debug( "Missed input ticks : " + res ) ;
02106         res.clear() ;
02107 
02108     }
02109                 
02110                 
02111     /*
02112      * Check that all input ticks were handled one and only one time, one 
02113      * way or another :
02114      * (scheduled or skipped).
02115      *
02116      */
02117     
02118     InputTick newInputTick ;
02119     
02120     bool * inputTicks = new bool[ _currentInputTick ] ;
02121     
02122     for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
02123         inputTicks[i] = false ;
02124         
02125     for ( list<InputTick>::const_iterator it = metInputPollings.begin(); 
02126         it != metInputPollings.end(); it++ )
02127     {
02128     
02129         newInputTick = (*it) ;
02130         if ( inputTicks[ newInputTick ] == false )
02131             inputTicks[ newInputTick ] = true ;
02132         else
02133             LogPlug::error( "Met input tick #" 
02134                 + Ceylan::toString( newInputTick ) 
02135                 + " should not have been scheduled more than once." ) ;
02136                 
02137     }
02138     
02139     for ( list<InputTick>::const_iterator it = recoveredInputPollings.begin(); 
02140         it != recoveredInputPollings.end(); it++ )
02141     {
02142     
02143         newInputTick = (*it) ;
02144         if ( inputTicks[ newInputTick ] == false )
02145             inputTicks[ newInputTick ] = true ;
02146         else
02147             LogPlug::error( "Recovered input tick #" 
02148                 + Ceylan::toString( newInputTick ) 
02149                 + " should not have been scheduled more than once." ) ;
02150                 
02151     }
02152     
02153     
02154     for ( list<InputTick>::const_iterator it = missedInputPollings.begin(); 
02155         it != missedInputPollings.end(); it++ )
02156     {
02157         newInputTick = (*it) ;
02158         if ( inputTicks[ newInputTick ] == false )
02159             inputTicks[ newInputTick ] = true ;
02160         else
02161             LogPlug::error( "Missed input tick #" 
02162                 + Ceylan::toString( newInputTick ) 
02163                 + " should not have been been skipped, "
02164                 "since had already been taken into account." ) ;
02165                         
02166     }
02167     
02168     for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
02169         if ( inputTicks[ i ] == false )
02170             LogPlug::error( "Input tick #" + Ceylan::toString( i ) 
02171                 + " has never been scheduled." ) ;  
02172 
02173     delete inputTicks ;
02174     
02175 #endif // OSDL_DEBUG_SCHEDULER
02176     
02177 }
02178 
02179     
02180     
02181     
02182     
02183     
02184 
02185 void Scheduler::scheduleNoDeadline( bool pollInputs ) 
02186     throw( SchedulingException )
02187 {
02188     
02189     // Let's prepare to the run, for which no time is to be considered :
02190     
02191     _stopRequested = false ;    
02192     
02193 #if OSDL_DEBUG_SCHEDULER
02194     
02195     /*
02196      * If OSDL_DEBUG_SCHEDULER is set, run-time informations about 
02197      * the scheduler will be archived and analyzed after it stopped, 
02198      * like black boxes.
02199      *
02200      */
02201      
02202     list<SimulationTick> metSimulations ;
02203     list<RenderingTick>  metRenderings ;    
02204     list<InputTick>      metInputPollings ; 
02205     
02206 #endif // OSDL_DEBUG_SCHEDULER
02207 
02208     
02209     // Store scheduling starting time : 
02210     getPreciseTime( _scheduleStartingSecond, _scheduleStartingMicrosecond ) ;
02211          
02212     // Starts with all zero ticks : 
02213     _currentEngineTick     = 0 ;
02214         
02215     _currentSimulationTick = 0 ;    
02216     _currentRenderingTick  = 0 ;
02217     _currentInputTick      = 0 ;
02218     
02219     OSDL_SCHEDULE_LOG( "Simulation period : " << _simulationPeriod ) ;
02220     OSDL_SCHEDULE_LOG( "Rendering period : "  << _renderingPeriod ) ;
02221     OSDL_SCHEDULE_LOG( "Input period : "      << _inputPeriod ) ;
02222         
02223     // No real deadline in this mode, just checkpoints that cannot be missed :  
02224     EngineTick nextSimulationDeadline = _currentEngineTick + _simulationPeriod ;
02225     EngineTick nextRenderingDeadline  = _currentEngineTick + _renderingPeriod ;
02226     EngineTick nextInputDeadline      = _currentEngineTick + _inputPeriod ;
02227     
02228     EngineTick countBeforeSleep = 0 ;
02229     
02230     // Enter the schedule loop :
02231     
02232     while ( ! _stopRequested )
02233     {
02234             
02235         OSDL_SCHEDULE_LOG( "[ E : " << _currentEngineTick 
02236             << " ; S : " << _currentSimulationTick
02237             << " ; R : " << _currentRenderingTick       
02238             << " ; I : " << _currentInputTick << " ]" ) ;
02239 
02240         countBeforeSleep++ ;
02241         
02242         /* 
02243          * Normally, for each simulation tick, one scheduleSimulation is called.
02244          *
02245          * Similarly, for each rendering tick, one scheduleRendering is called.
02246          *
02247          * Same thing for inputs.
02248          *
02249          */
02250             
02251                     
02252         // We hereby suppose we are just at the beginning of a new engine tick.
02253         
02254         // What is to be done on this new engine tick ?
02255         
02256         // Perform all scheduling actions for this tick :
02257         if ( _currentEngineTick == nextSimulationDeadline )
02258         {
02259         
02260             //OSDL_SCHEDULE_LOG( "--> Simulation deadline met" ) ;
02261                 
02262 #if OSDL_DEBUG_SCHEDULER
02263             metSimulations.push_back( _currentSimulationTick ) ;
02264 #endif // OSDL_DEBUG_SCHEDULER
02265             
02266             scheduleSimulation( _currentSimulationTick ) ;
02267             
02268             _currentSimulationTick++ ;
02269             nextSimulationDeadline += _simulationPeriod ;
02270             
02271         }
02272 
02273         if ( _currentEngineTick == nextRenderingDeadline )
02274         {
02275         
02276             //OSDL_SCHEDULE_LOG( "--> Rendering deadline met" ) ;
02277                 
02278 #if OSDL_DEBUG_SCHEDULER
02279             metRenderings.push_back( _currentRenderingTick ) ;
02280 #endif // OSDL_DEBUG_SCHEDULER
02281 
02282             scheduleRendering( _currentRenderingTick ) ;
02283 
02284             _currentRenderingTick++ ;
02285             nextRenderingDeadline += _renderingPeriod ;
02286         }
02287         
02288         if ( pollInputs && _currentEngineTick == nextInputDeadline )
02289         {
02290         
02291             //OSDL_SCHEDULE_LOG( "--> Input deadline met" ) ;
02292                 
02293 #if OSDL_DEBUG_SCHEDULER
02294             metInputPollings.push_back( _currentInputTick ) ;
02295 #endif // OSDL_DEBUG_SCHEDULER
02296 
02297             scheduleInput( _currentInputTick ) ;
02298 
02299             _currentInputTick++ ;
02300             nextInputDeadline += _inputPeriod ;
02301         }
02302 
02303         _currentEngineTick++ ;
02304         
02305         // Be nice with the operating system :
02306         if ( countBeforeSleep == 500 )
02307         {
02308             countBeforeSleep = 0 ;
02309             atomicSleep() ;
02310         }   
02311         
02312         /*
02313          * OSDL_SCHEDULE_LOG( "End of schedule loop, engine tick being " 
02314          * << _currentEngineTick ) ;
02315          *
02316          */
02317         
02318     }
02319     
02320     Second scheduleStoppingSecond ;
02321     Microsecond scheduleStoppingMicrosecond ;
02322     
02323     getPreciseTime( scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
02324 
02325 
02326     // It is equal to 1E6 / ( total runtime in microseconds ) :
02327     Ceylan::Float64 totalRuntimeFactor ;
02328     
02329     // Avoid overflows :
02330     if ( scheduleStoppingSecond - _scheduleStartingSecond <
02331             MaximumDurationWithMicrosecondAccuracy )
02332     {   
02333          
02334         totalRuntimeFactor = 1E6 / getDurationBetween( 
02335             _scheduleStartingSecond, _scheduleStartingMicrosecond,
02336             scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
02337             
02338     }
02339     else
02340     {
02341     
02342         // It is just a (rather good , error below 1/4100) approximation :
02343         totalRuntimeFactor =  1.0f / 
02344             ( scheduleStoppingSecond - _scheduleStartingSecond ) ;
02345         
02346     }
02347 
02348     
02349     send( "Scheduler stopping. Scheduler infos : " 
02350         + toString( Ceylan::high ) ) ;  
02351     
02352 
02353     ostringstream buf ;
02354     
02355     buf.precision( 4 ) ;
02356         
02357     buf << "Actual average engine frequency was "  
02358         << Ceylan::toString( totalRuntimeFactor * _currentEngineTick, 
02359             /* precision */ 2 )
02360         << " Hz, average simulation frequency was " 
02361         << Ceylan::toString( totalRuntimeFactor * _currentSimulationTick,
02362             /* precision */ 2 )
02363         << " Hz, average rendering frequency was " 
02364         << Ceylan::toString( totalRuntimeFactor * _currentRenderingTick,
02365             /* precision */ 2 )
02366         << " Hz, average input frequency was " 
02367         << Ceylan::toString( totalRuntimeFactor * _currentInputTick, 
02368             /* precision */ 2 )
02369         << " Hz." ;
02370          
02371     send( buf.str() ) ;
02372             
02373 #if OSDL_DEBUG_SCHEDULER
02374     
02375     
02376     /*
02377      * Collect some informations, and check them to assess the scheduler
02378      * behaviour.
02379      *
02380      */
02381     
02382     LogPlug::debug( "Total simulation ticks : " 
02383         + Ceylan::toString( metSimulations.size() ) + "." ) ;
02384             
02385 
02386     /*
02387      * Check that all simulation ticks were scheduled one and only one time.
02388      *
02389      */
02390     SimulationTick newSimulationTick ;
02391     
02392     bool * simulationTicks = new bool[ _currentSimulationTick ] ;
02393     
02394     for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
02395         simulationTicks[i] = false ;
02396         
02397     for ( list<SimulationTick>::const_iterator it = metSimulations.begin(); 
02398         it != metSimulations.end(); it++ )
02399     {
02400     
02401         newSimulationTick = (*it) ;
02402         if ( simulationTicks[ newSimulationTick ] == false )
02403             simulationTicks[ newSimulationTick ] = true ;
02404         else
02405             LogPlug::error( "Simulation tick #" 
02406                 + Ceylan::toString( newSimulationTick ) 
02407                 + " should not have been scheduled more than once." ) ;     
02408                 
02409     }   
02410     
02411     for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
02412         if ( simulationTicks[ i ] == false )
02413             LogPlug::error( "Simulation tick #" + Ceylan::toString( i ) 
02414                 + " has never been scheduled." ) ;  
02415 
02416     delete simulationTicks ;
02417                 
02418                 
02419                 
02420                 
02421     LogPlug::debug( "Total rendering ticks : " 
02422         + Ceylan::toString( metRenderings.size() ) + "." ) ;
02423                 
02424     /*
02425      * Check that all rendering ticks were scheduled one and only one time.
02426      *
02427      */
02428     
02429     RenderingTick newRenderingTick ;
02430     
02431     bool * renderingTicks = new bool[ _currentRenderingTick ] ;
02432     
02433     for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
02434         renderingTicks[i] = false ;
02435         
02436     for ( list<RenderingTick>::const_iterator it = metRenderings.begin(); 
02437         it != metRenderings.end(); it++ )
02438     {
02439     
02440         newRenderingTick = (*it) ;
02441         if ( renderingTicks[ newRenderingTick ] == false )
02442             renderingTicks[ newRenderingTick ] = true ;
02443         else
02444             LogPlug::error( "Rendering tick #" 
02445                 + Ceylan::toString( newRenderingTick ) 
02446                 + " should not have been scheduled more than once." ) ;
02447                 
02448     }
02449     
02450     for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
02451         if ( renderingTicks[ i ] == false )
02452             LogPlug::error( "Rendering tick #" + Ceylan::toString( i ) 
02453                 + " has never been scheduled." ) ;  
02454 
02455     delete renderingTicks ;
02456     
02457     
02458 
02459     LogPlug::debug( "Total input polling ticks : " 
02460         + Ceylan::toString( metInputPollings.size() ) + "." ) ;
02461 
02462     /*
02463      * Check that all input ticks were scheduled one and only one time.
02464      *
02465      */
02466     if ( pollInputs )
02467     {
02468     
02469         InputTick newInputTick ;
02470     
02471         bool * inputTicks = new bool[ _currentInputTick ] ;
02472         
02473         for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
02474             inputTicks[i] = false ;
02475         
02476         for ( list<InputTick>::const_iterator it = metInputPollings.begin(); 
02477             it != metInputPollings.end(); it++ )
02478         {
02479         
02480             newInputTick = (*it) ;
02481             if ( inputTicks[ newInputTick ] == false )
02482                 inputTicks[ newInputTick ] = true ;
02483             else
02484                 LogPlug::error( "Input tick #" 
02485                     + Ceylan::toString( newInputTick ) 
02486                     + " should not have been scheduled more than once." ) ;
02487                     
02488         }
02489         
02490         for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
02491             if ( inputTicks[ i ] == false )
02492                 LogPlug::error( "Input tick #" + Ceylan::toString( i ) 
02493                     + " has never been scheduled." ) ;  
02494         
02495         delete inputTicks ;
02496                 
02497     }
02498                         
02499 #endif // OSDL_DEBUG_SCHEDULER
02500 
02501     
02502 }
02503 
02504 
02505 
02506 EngineTick Scheduler::computeEngineTickFromCurrentTime() throw()
02507 {
02508 
02509     Second currentSecond ;
02510     Microsecond currentMicrosecond ;
02511     getPreciseTime( currentSecond, currentMicrosecond ) ;
02512     
02513     /*
02514      * _secondToEngineTick necessary to avoid overflow when duration in 
02515      * seconds exceeds 4200 !
02516      *
02517     
02518     LogPlug::debug( "Current : " + Ceylan::toString( currentSecond ) 
02519         + "s and " + Ceylan::toString( currentMicrosecond ) 
02520         + " microsec, started : " + Ceylan::toString( _scheduleStartingSecond ) 
02521         + "s and " + Ceylan::toString( _scheduleStartingMicrosecond ) 
02522         + "microsec." ) ;
02523         
02524      */
02525     
02526     /*
02527      * Avoids that 'currentMicrosecond - _scheduleStartingMicrosecond'
02528      * becomes negative and overflows :
02529      *
02530      */
02531     if ( currentMicrosecond < _scheduleStartingMicrosecond )
02532     {
02533         
02534         // Normally that cannot happen at second #0 :
02535 #if OSDL_DEBUG_SCHEDULER
02536         if ( currentSecond == 0 )
02537         {
02538         
02539             LogPlug::fatal( "Scheduler::computeEngineTickFromCurrentTime : "
02540                 "abnormal clock skew." ) ;
02541                 
02542             _stopRequested = true ;
02543                 
02544         }   
02545 #endif // OSDL_DEBUG_SCHEDULER
02546         
02547         currentSecond-- ;
02548         currentMicrosecond += 1000000 ;
02549         
02550     }
02551         
02552     return static_cast<EngineTick>( 
02553         ( currentSecond - _scheduleStartingSecond ) * _secondToEngineTick
02554         + ( currentMicrosecond - _scheduleStartingMicrosecond ) 
02555             / _engineTickDuration ) ;
02556         
02557 }
02558 
02559 
02560 void Scheduler::scheduleSimulation( SimulationTick current ) throw()
02561 {
02562 
02563     //OSDL_SCHEDULE_LOG( "--> current simulation tick : " << current ) ;
02564     
02565     OSDL_SCHEDULE_LOG( "--- simulating ! " ) ;
02566         
02567     // Activate all objects programmed for this specific time :
02568     scheduleProgrammedObjects( current ) ;
02569         
02570     // Activate all objects registered in this periodic slot :
02571     schedulePeriodicObjects( current ) ;
02572 
02573 }
02574 
02575 
02576 void Scheduler::scheduleProgrammedObjects( 
02577     SimulationTick currentSimulationTick ) throw()
02578 {
02579 
02580     map<SimulationTick, ListOfActiveObjects>::iterator it 
02581         = _programmedActivated.find( currentSimulationTick ) ;
02582         
02583     if ( it != _programmedActivated.end() )
02584     {
02585         scheduleActiveObjectList( currentSimulationTick, (*it).second ) ;
02586         
02587         // Empty this simulation tick when done with it :
02588         _programmedActivated.erase( it ) ;
02589     }
02590     
02591     // else : no key for this simulation tick ? Nothing to do !  
02592     
02593 }
02594 
02595 
02596 void Scheduler::schedulePeriodicObjects( SimulationTick current ) throw()
02597 {
02598 
02599     // Request each periodic slot to activate relevant objects :
02600     
02601     for ( list<PeriodicSlot*>::iterator it = _periodicSlots.begin(); 
02602         it != _periodicSlots.end(); it++ )
02603     {
02604         (*it)->onNextTick( current ) ;
02605     }
02606         
02607 }
02608 
02609 
02610 void Scheduler::scheduleRendering( RenderingTick current ) throw()
02611 {
02612 
02613     OSDL_SCHEDULE_LOG( "--- rendering !" ) ;
02614 
02615     if ( _renderer != 0 )
02616         _renderer->render( current ) ;
02617     else
02618     {
02619 
02620 #if OSDL_DEBUG
02621         if ( _videoModule == 0 )
02622             Ceylan::emergencyShutdown( "Scheduler::scheduleRendering : "
02623                 "no video module available." ) ;        
02624 #endif // OSDL_DEBUG
02625         _videoModule->redraw() ;    
02626     }   
02627         
02628 }
02629 
02630 
02631 void Scheduler::scheduleInput( InputTick current ) throw()
02632 {
02633 
02634     OSDL_SCHEDULE_LOG( "--- input polling !" ) ;
02635 
02636 #if OSDL_DEBUG
02637     if ( _eventsModule == 0 )
02638         Ceylan::emergencyShutdown( "Scheduler::scheduleRendering : "
02639             "no events module available." ) ;       
02640 #endif // OSDL_DEBUG
02641     
02642     _eventsModule->updateInputState() ; 
02643                 
02644 }
02645 
02646 
02647 void Scheduler::scheduleActiveObjectList( 
02648     Events::RenderingTick currentSimulationTick, 
02649     ListOfActiveObjects & objectList ) throw()
02650 {
02651 
02652     for ( ListOfActiveObjects::iterator it = objectList.begin(); 
02653         it != objectList.end(); it++ )
02654     {
02655         (*it)->onActivation( currentSimulationTick ) ;
02656     }
02657         
02658 }
02659 
02660      
02661 void Scheduler::onSimulationSkipped( SimulationTick skipped ) 
02662     throw( SchedulingException )
02663 {
02664 
02665     _missedSimulationTicks++ ;
02666     
02667         
02668     /*
02669      * First, check objects which should have been triggered because 
02670      * of periodic activation :
02671      *
02672      */
02673     
02674     /*
02675      * Request each periodic slot to propagate the news to its relevant 
02676      * objects :
02677      *
02678      */
02679     
02680     for ( list<PeriodicSlot*>::iterator it = _periodicSlots.begin(); 
02681         it != _periodicSlots.end(); it++ )
02682     {
02683         (*it)->onSimulationSkipped( skipped ) ;
02684     }
02685     
02686     // Second, send notification to programmed objects :
02687 
02688     map<SimulationTick, ListOfActiveObjects>::iterator it 
02689         = _programmedActivated.find( skipped ) ;
02690         
02691     if ( it != _programmedActivated.end() )
02692     {
02693         for ( ListOfActiveObjects::iterator itObjects = (*it).second.begin() ;
02694                 itObjects != (*it).second.end(); itObjects++ )
02695             (*itObjects)->onSkip( skipped ) ;
02696     }
02697     
02698 #if OSDL_DEBUG_SCHEDULER    
02699     LogPlug::warning( "Simulation tick " + Ceylan::toString( skipped ) 
02700         + " had to be skipped." ) ;
02701 #endif // OSDL_DEBUG_SCHEDULER
02702     
02703 }
02704 
02705 
02706 void Scheduler::onRenderingSkipped( RenderingTick skipped ) 
02707     throw( SchedulingException ) 
02708 {
02709 
02710     _missedRenderingTicks++ ;
02711     
02712     if ( _renderer != 0 )
02713         _renderer->onRenderingSkipped( skipped ) ;
02714         
02715 #if OSDL_DEBUG_SCHEDULER
02716     LogPlug::warning( "Rendering tick " + Ceylan::toString( skipped ) 
02717         + " had to be skipped." ) ;
02718 #endif // OSDL_DEBUG_SCHEDULER
02719         
02720 }
02721 
02722 
02723 void Scheduler::onInputSkipped( InputTick skipped ) 
02724     throw( SchedulingException ) 
02725 {
02726 
02727     _missedInputPollingTicks++ ;
02728         
02729     // Do nothing else, skipped inputs do not matter that much.
02730     
02731 }
02732 
02733 
02734 void Scheduler::onIdle() throw()
02735 {
02736 
02737     OSDL_SCHEDULE_LOG( "--- idle callback called !" ) ;
02738 
02739     _idleCallsCount++ ;
02740 
02741     if ( _idleCallback != 0 )
02742     {
02743     
02744         // Call the user-supplied idle callback :
02745         (*_idleCallback)( _idleCallbackData ) ;
02746         
02747     }
02748     else
02749     {
02750     
02751         /*
02752          * Issues an atomic sleep, chosen so that the minimum real predictable 
02753          * sleeping time can be performed, scheduler-wise :
02754          *
02755          */
02756          Ceylan::System::atomicSleep() ;
02757          
02758     }   
02759     
02760 }
02761 
02762 
02763 void Scheduler::onScheduleFailure( Delay currentBucket ) throw()
02764 {
02765 
02766     _scheduleFailureCount++ ;
02767     
02768     // One may loosen frequencies and simplify scheduled tasks as well :
02769     
02770     if ( currentBucket < ShutdownBucketLevel )
02771     {
02772     
02773         string message = "Non-fatal schedule failure for engine tick #"
02774             + Ceylan::toString( _currentEngineTick )
02775             + ", delay bucket reached level "
02776             + Ceylan::toString( currentBucket ) 
02777             + ", this computer does not seem able to satisfy "
02778             "the requested load." ;
02779             
02780         // Just warn :
02781         OSDL_SCHEDULE_LOG( "!!!!! " + message ) ;
02782         
02783         LogPlug::error( message) ;  
02784         
02785     }
02786     else
02787     {
02788 
02789         string message = "Fatal schedule failure for engine tick #"
02790             + Ceylan::toString( _currentEngineTick )
02791             + ", delay bucket reached level "
02792             + Ceylan::toString( currentBucket ) 
02793             + ", this computer cannot take in charge the requested load, "
02794             "stopping the scheduler now." ;
02795             
02796         // Warn and stop :
02797         OSDL_SCHEDULE_LOG( "XXXXX " + message ) ;
02798         
02799         LogPlug::fatal( message) ;  
02800         
02801         stop() ;
02802     
02803     }
02804     
02805 }
02806 
02807 
02808 void Scheduler::programTriggerFor( ActiveObject & objectToProgram, 
02809     SimulationTick targetTick ) throw()
02810 {
02811 
02812     // Add the offset of current simulation tick if triggers are not absolute :
02813     if ( ! objectToProgram.areProgrammedActivationsAbsolute() )
02814         targetTick += _currentSimulationTick ;
02815         
02816     map<SimulationTick, ListOfActiveObjects>::iterator it 
02817         = _programmedActivated.find( targetTick ) ;
02818         
02819     if ( it != _programmedActivated.end() )
02820     {
02821         // There is already an object list available for this tick :
02822         (*it).second.push_back( & objectToProgram ) ;
02823     }
02824     else
02825     {
02826         // First object for this simulation tick :
02827         
02828         ListOfActiveObjects newList ;
02829         newList.push_back( & objectToProgram ) ; 
02830         _programmedActivated[ targetTick ] = newList ;
02831                 
02832     }
02833     
02834 }
02835                     
02836                     
02837 PeriodicSlot & Scheduler::getPeriodicSlotFor( Events::Period period ) throw()
02838 {
02839 
02840     for ( list<PeriodicSlot*>::iterator it = _periodicSlots.begin(); 
02841         it != _periodicSlots.end(); it++ )
02842     {
02843     
02844         if ( (*it)->getPeriod() == period )
02845             return * (*it) ;
02846         
02847         if ( (*it)->getPeriod() > period )
02848         {
02849         
02850             /*
02851              * No slot already available, create it and insert it 
02852              * <b>before</b> current one.
02853              *
02854              */
02855             PeriodicSlot * newSlot = new PeriodicSlot( period ) ;
02856             _periodicSlots.insert( it, newSlot ) ;
02857             return * newSlot ; 
02858         }   
02859 
02860     }   
02861     
02862     /*
02863      * Period not found, higher than all others : create it at the end 
02864      * of the list :
02865      *
02866      */
02867     PeriodicSlot * newSlot = new PeriodicSlot( period ) ;
02868     _periodicSlots.push_back( newSlot ) ;
02869     
02870     return * newSlot ; 
02871     
02872 }
02873 
02874 

Generated on Fri Mar 30 14:47:00 2007 for OSDL by  doxygen 1.5.1