00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "OSDLScheduler.h"
00028
00029 #include "OSDLBasic.h"
00030 #include "OSDLVideo.h"
00031 #include "OSDLRenderer.h"
00032 #include "OSDLActiveObject.h"
00033 #include "OSDLPeriodicalActiveObject.h"
00034 #include "OSDLProgrammedActiveObject.h"
00035
00036
00037
00038 #include <iostream>
00039 using std::ostringstream ;
00040
00041 #include <iomanip>
00042
00043
00044
00045 using namespace Ceylan::Log ;
00046
00047
00048 using namespace Ceylan::System ;
00049
00050
00051 using namespace OSDL ;
00052 using namespace OSDL::Events ;
00053 using namespace OSDL::Rendering ;
00054 using namespace OSDL::Engine ;
00055
00056 using std::string ;
00057 using std::list ;
00058 using std::map ;
00059
00060 using Ceylan::Maths::Hertz ;
00061
00062
00063
00064 Scheduler * Scheduler::_internalScheduler = 0 ;
00065
00066
00067 const Delay Scheduler::ShutdownBucketLevel = 1000000 ;
00068
00069
00070
00071 #ifdef OSDL_USES_CONFIG_H
00072 #include <OSDLConfig.h>
00073 #endif // OSDL_USES_CONFIG_H
00074
00075
00076
00077 #if OSDL_DEBUG_SCHEDULER
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 #define OSDL_SCHEDULE_LOG(message) //send( message )
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099 #else // OSDL_DEBUG_SCHEDULER
00100
00101 #define OSDL_SCHEDULE_LOG(message)
00102
00103 #endif // OSDL_DEBUG_SCHEDULER
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152 bool Scheduler::hasRenderer() const
00153 {
00154
00155 return ( _renderer != 0 ) ;
00156
00157 }
00158
00159
00160
00161 Renderer & Scheduler::getRenderer() const
00162 {
00163
00164 if ( _renderer != 0 )
00165 return * _renderer ;
00166 else
00167 throw SchedulingException(
00168 "Scheduler::getRenderer: no renderer available." ) ;
00169
00170 }
00171
00172
00173
00174 void Scheduler::setRenderer( Rendering::Renderer & newRenderer )
00175 {
00176
00177 if ( _renderer != 0 )
00178 delete _renderer ;
00179
00180
00181 _renderer = & newRenderer ;
00182
00183 }
00184
00185
00186
00187 void Scheduler::setScreenshotMode( bool on, const string & frameFilenamePrefix,
00188 Hertz frameFrequency )
00189 {
00190
00191 LogPlug::trace( "Scheduler::setScreenshotMode: mode set to "
00192 + Ceylan::toString( on ) + "." ) ;
00193
00194 _screenshotMode = on ;
00195 _frameFilenamePrefix = frameFilenamePrefix ;
00196
00197 }
00198
00199
00200
00201 void Scheduler::setTimeSliceDuration( Microsecond engineTickDuration )
00202 {
00203
00204 _engineTickDuration = engineTickDuration ;
00205
00206
00207 setSimulationFrequency( _desiredSimulationFrequency ) ;
00208 setRenderingFrequency( _desiredRenderingFrequency ) ;
00209 setScreenshotFrequency( _desiredScreenshotFrequency ) ;
00210 setInputPollingFrequency( _desiredInputFrequency ) ;
00211
00212 _secondToEngineTick = 1000000 / _engineTickDuration ;
00213
00214 OSDL_SCHEDULE_LOG(
00215 "Multiplicative factor to convert seconds into engine ticks: "
00216 + Ceylan::toString( _secondToEngineTick ) ) ;
00217
00218 }
00219
00220
00221
00222 Microsecond Scheduler::getTimeSliceDuration() const
00223 {
00224
00225 return _engineTickDuration ;
00226
00227 }
00228
00229
00230
00231 void Scheduler::setSimulationFrequency( Hertz frequency )
00232 {
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243 if ( frequency * _engineTickDuration > 1E6 )
00244 throw SchedulingException( "Scheduler::setSimulationFrequency: "
00245 " requested simulation frequency ("
00246 + Ceylan::toString( frequency )
00247 + " Hz) is too high for engine tick,"
00248 " which lasts for "
00249 + Ceylan::toString( _engineTickDuration )
00250 + " microseconds." ) ;
00251
00252 _desiredSimulationFrequency = frequency ;
00253 _simulationPeriod = static_cast<Period>(
00254 1E6 / ( frequency * _engineTickDuration ) ) ;
00255
00256 #if OSDL_DEBUG
00257
00258 send( "Scheduler::setSimulationFrequency: for a requested "
00259 "simulation frequency of " + Ceylan::toString( frequency )
00260 + " Hz, the simulation period corresponds to "
00261 + Ceylan::toString( _simulationPeriod ) + " engine ticks." ) ;
00262
00263 #endif // OSDL_DEBUG
00264
00265 }
00266
00267
00268
00269 Period Scheduler::getSimulationTickCount() const
00270 {
00271
00272 return _simulationPeriod ;
00273
00274 }
00275
00276
00277
00278 void Scheduler::setRenderingFrequency( Hertz frequency )
00279 {
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291 if ( frequency * _engineTickDuration > 1E6 )
00292 throw SchedulingException( "Scheduler::setRenderingFrequency: "
00293 "requested rendering frequency ("
00294 + Ceylan::toString( frequency )
00295 + " Hz) is too high for engine tick,"
00296 " which lasts for "
00297 + Ceylan::toString( _engineTickDuration )
00298 + " microseconds." ) ;
00299
00300 _desiredRenderingFrequency = frequency ;
00301 _renderingPeriod = static_cast<Period>(
00302 1E6 / ( frequency * _engineTickDuration ) ) ;
00303
00304 #if OSDL_DEBUG
00305
00306 send( "Scheduler::setRenderingFrequency: for a requested "
00307 "rendering frequency of " + Ceylan::toString( frequency )
00308 + " Hz, the rendering period corresponds to "
00309 + Ceylan::toString( _renderingPeriod ) + " engine ticks." ) ;
00310
00311 #endif // OSDL_DEBUG
00312
00313 }
00314
00315
00316
00317 Period Scheduler::getRenderingTickCount() const
00318 {
00319
00320 return _renderingPeriod ;
00321
00322 }
00323
00324
00325
00326 void Scheduler::setScreenshotFrequency( Hertz frequency )
00327 {
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343 if ( frequency * _engineTickDuration > 1E6 )
00344 throw SchedulingException( "Scheduler::setScreenshotFrequency: "
00345 " requested screenshot frequency ("
00346 + Ceylan::toString( frequency )
00347 + " Hz) is too high for engine tick,"
00348 " which lasts for " + Ceylan::toString( _engineTickDuration )
00349 + " microseconds." ) ;
00350
00351 _desiredScreenshotFrequency = frequency ;
00352 _screenshotPeriod = static_cast<Period>(
00353 1E6 / ( frequency * _engineTickDuration ) ) ;
00354
00355 #if OSDL_DEBUG
00356
00357 send( "Scheduler::setScreenshotFrequency: for a requested "
00358 "screenshot frequency of " + Ceylan::toString( frequency )
00359 + " Hz, the screenshot period corresponds to "
00360 + Ceylan::toString( _screenshotPeriod ) + " engine ticks." ) ;
00361
00362 #endif // OSDL_DEBUG
00363
00364 }
00365
00366
00367
00368 Period Scheduler::getScreenshotTickCount() const
00369 {
00370
00371 return _screenshotPeriod ;
00372
00373 }
00374
00375
00376
00377 void Scheduler::setInputPollingFrequency( Hertz frequency )
00378 {
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390 if ( frequency * _engineTickDuration > 1E6 )
00391 throw SchedulingException(
00392 "Scheduler::setInputPollingFrequency: requested input "
00393 "frequency (" + Ceylan::toString( frequency )
00394 + " Hz) is too high for engine tick,"
00395 " which lasts for "
00396 + Ceylan::toString( _engineTickDuration ) + " microseconds." ) ;
00397
00398 _desiredInputFrequency = frequency ;
00399 _inputPeriod = static_cast<Period>(
00400 1E6 / ( frequency * _engineTickDuration ) ) ;
00401
00402 #if OSDL_DEBUG
00403
00404 send( "Scheduler::setInputPollingFrequency: for a requested "
00405 "input frequency of " + Ceylan::toString( frequency )
00406 + " Hz, the input period corresponds to "
00407 + Ceylan::toString( _inputPeriod ) + " engine ticks." ) ;
00408
00409 #endif // OSDL_DEBUG
00410
00411 }
00412
00413
00414
00415 void Scheduler::setIdleCallback(
00416 Ceylan::System::Callback idleCallback,
00417 void * callbackData,
00418 Ceylan::System::Microsecond callbackExpectedMaxDuration )
00419 {
00420
00421 _idleCallback = idleCallback ;
00422 _idleCallbackData = callbackData ;
00423
00424 if ( callbackExpectedMaxDuration != 0 )
00425 _idleCallbackMaxDuration = callbackExpectedMaxDuration ;
00426 else
00427 _idleCallbackMaxDuration = EventsModule::EvaluateCallbackduration(
00428 idleCallback, callbackData ) ;
00429
00430 }
00431
00432
00433
00434 Period Scheduler::getInputPollingTickCount() const
00435 {
00436
00437 return _inputPeriod ;
00438
00439 }
00440
00441
00442
00443 EngineTick Scheduler::getCurrentEngineTick() const
00444 {
00445
00446 return _currentEngineTick ;
00447
00448 }
00449
00450
00451
00452 SimulationTick Scheduler::getCurrentSimulationTick() const
00453 {
00454
00455 return _currentSimulationTick ;
00456
00457 }
00458
00459
00460
00461 RenderingTick Scheduler::getCurrentRenderingTick() const
00462 {
00463
00464 return _currentRenderingTick ;
00465
00466 }
00467
00468
00469
00470 InputTick Scheduler::getCurrentInputTick() const
00471 {
00472
00473 return _currentInputTick ;
00474
00475 }
00476
00477
00478
00479
00480 Events::SimulationTick Scheduler::getNumberOfSimulationTicksFor(
00481 Ceylan::System::Millisecond duration ) const
00482 {
00483
00484 return static_cast<Events::SimulationTick>(
00485 Ceylan::Maths::Round( duration * 1000.f /
00486 ( _simulationPeriod * _engineTickDuration ) ) ) ;
00487
00488 }
00489
00490
00491
00492 Events::RenderingTick Scheduler::getNumberOfRenderingTicksFor(
00493 Ceylan::System::Millisecond duration ) const
00494 {
00495
00496 return static_cast<Events::RenderingTick>(
00497 Ceylan::Maths::Round( duration * 1000.f /
00498 ( _renderingPeriod * _engineTickDuration ) ) ) ;
00499
00500 }
00501
00502
00503
00504 Events::InputTick Scheduler::getNumberOfInputTicksFor(
00505 Ceylan::System::Millisecond duration ) const
00506 {
00507
00508 return static_cast<Events::InputTick>(
00509 Ceylan::Maths::Round( duration * 1000.f /
00510 ( _inputPeriod * _engineTickDuration ) ) ) ;
00511
00512
00513 }
00514
00515
00516
00517
00518 void Scheduler::registerPeriodicalObject(
00519 PeriodicalActiveObject & objectToRegister )
00520 {
00521
00522
00523 PeriodicSlot & slot = returnPeriodicSlotFor(
00524 objectToRegister.getPeriod() ) ;
00525
00526
00527 Events::Period subslot = slot.add( objectToRegister ) ;
00528
00529 objectToRegister.onRegistering( subslot ) ;
00530
00531
00532 if ( _isRunning )
00533 {
00534
00535
00536 objectToRegister.setBirthTick( _currentSimulationTick ) ;
00537
00538 }
00539 else
00540 {
00541
00542
00543 _initialRegisteredObjects.push_back( & objectToRegister ) ;
00544
00545 }
00546
00547 }
00548
00549
00550
00551 void Scheduler::unregisterPeriodicalObject(
00552 PeriodicalActiveObject & toUnregister )
00553 {
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578 PeriodicSlot & slot = getPeriodicSlotFor( toUnregister.getPeriod() ) ;
00579
00580 slot.removeFromSubslot( toUnregister ) ;
00581
00582 send( "New scheduler state: " + toString() ) ;
00583
00584 }
00585
00586
00587
00588 void Scheduler::registerProgrammedObject(
00589 ProgrammedActiveObject & objectToRegister )
00590 {
00591
00592
00593
00594 const list<SimulationTick> & programmed =
00595 objectToRegister.getProgrammedActivations() ;
00596
00597 SimulationTick offset = 0 ;
00598
00599 if ( ! objectToRegister.areProgrammedActivationsAbsolute() )
00600 {
00601
00602
00603
00604 if ( _isRunning )
00605 offset = _currentSimulationTick ;
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616 }
00617
00618
00619 for ( list<SimulationTick>::const_iterator it = programmed.begin();
00620 it != programmed.end(); it++ )
00621 {
00622 programTriggerFor( objectToRegister, (*it) + offset ) ;
00623 }
00624
00625 objectToRegister.onRegistering() ;
00626
00627
00628 if ( _isRunning )
00629 {
00630
00631
00632 objectToRegister.setBirthTick( _currentSimulationTick ) ;
00633
00634 }
00635 else
00636 {
00637
00638
00639 _initialRegisteredObjects.push_back( & objectToRegister ) ;
00640
00641 }
00642
00643
00644 }
00645
00646
00647
00648 void Scheduler::unregisterProgrammedObject(
00649 ProgrammedActiveObject & objectToUnregister )
00650 {
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680 SimulationTick offset = 0 ;
00681
00682
00683 if ( ! objectToUnregister.areProgrammedActivationsAbsolute() )
00684 offset = objectToUnregister.getBirthTick() ;
00685
00686 const list<SimulationTick> & programmed =
00687 objectToUnregister.getProgrammedActivations() ;
00688
00689 list<SimulationTick>::const_iterator it = programmed.begin() ;
00690
00691
00692 while ( ( it != programmed.end() )
00693 && ( (*it) + offset < _currentSimulationTick ) )
00694 {
00695
00696
00697
00698
00699
00700
00701 it++ ;
00702
00703 }
00704
00705
00706
00707
00708 while ( it != programmed.end() )
00709 {
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719 map<SimulationTick, ListOfProgrammedActiveObjects>::iterator mapIt =
00720 _programmedActivated.find( *it ) ;
00721
00722 if ( mapIt != _programmedActivated.end() )
00723 {
00724
00725
00726
00727
00728
00729
00730 ListOfProgrammedActiveObjects::iterator listIt =
00731 (*mapIt).second.begin() ;
00732
00733
00734
00735
00736
00737
00738
00739 #if OSDL_DEBUG_SCHEDULER
00740 bool found = false ;
00741 #endif // OSDL_DEBUG_SCHEDULER
00742
00743
00744 while ( listIt != (*mapIt).second.end() )
00745 {
00746
00747
00748 if ( *listIt == & objectToUnregister )
00749 {
00750
00751
00752
00753
00754
00755
00756
00757 *listIt = 0 ;
00758
00759 #if OSDL_DEBUG_SCHEDULER
00760
00761
00762 if ( found )
00763 throw SchedulingException(
00764 "Scheduler::unregisterProgrammedObject: "
00765 "object was registered more than once for tick "
00766 + Ceylan::toString(*it) + "." ) ;
00767 else
00768 found = true ;
00769
00770 #else // OSDL_DEBUG_SCHEDULER
00771
00772
00773 break ;
00774
00775 #endif // OSDL_DEBUG_SCHEDULER
00776 }
00777
00778 listIt++ ;
00779
00780 }
00781
00782
00783
00784 #if OSDL_DEBUG_SCHEDULER
00785
00786 if ( ! found )
00787
00788 #else // OSDL_DEBUG_SCHEDULER
00789
00790
00791 if ( listIt == (*mapIt).second.end() )
00792
00793 #endif
00794
00795 throw SchedulingException(
00796 "Scheduler::unregisterProgrammedObject failed when "
00797 "unregistering " + objectToUnregister.toString()
00798 + ": expected to find a programmed activation for tick #"
00799 + Ceylan::toString( *it )
00800 + ", while current scheduler state is: "
00801 + toString() ) ;
00802
00803
00804
00805
00806
00807
00808 }
00809 else
00810 {
00811
00812 throw SchedulingException(
00813 "Scheduler::unregisterProgrammedObject failed when "
00814 "unregistering " + objectToUnregister.toString()
00815 + ": a programmed activation list for tick #"
00816 + Ceylan::toString( *it ) + " should have existed." ) ;
00817 }
00818
00819 it++ ;
00820
00821 }
00822
00823
00824
00825
00826 }
00827
00828
00829
00830 void Scheduler::schedule()
00831 {
00832
00833 try
00834 {
00835
00836 _eventsModule = & OSDL::getExistingCommonModule().getEventsModule() ;
00837
00838 }
00839 catch( OSDL::Exception & e )
00840 {
00841 throw SchedulingException( "Scheduler::schedule: "
00842 "no events module available." ) ;
00843 }
00844
00845
00846 if ( _renderer != 0 )
00847 {
00848 send( "Scheduling now, using renderer: " + _renderer->toString() ) ;
00849 }
00850 else
00851 {
00852
00853 try
00854 {
00855
00856 _videoModule = & OSDL::getExistingCommonModule().getVideoModule() ;
00857
00858 }
00859 catch( OSDL::Exception & e )
00860 {
00861 throw SchedulingException( "Scheduler::schedule: "
00862 "no renderer nor video module available." ) ;
00863 }
00864
00865 send( "Scheduling now, using video module (no renderer)." ) ;
00866
00867 }
00868
00869
00870 if ( _screenshotMode )
00871 scheduleNoDeadline() ;
00872 else
00873 scheduleBestEffort() ;
00874
00875 }
00876
00877
00878
00879 void Scheduler::stop()
00880 {
00881
00882 _stopRequested = true ;
00883
00884 }
00885
00886
00887
00888 void Scheduler::setStopCallback( Ceylan::System::Callback stopCallback,
00889 void * callbackData )
00890 {
00891
00892 _stopCallback = stopCallback ;
00893 _stopCallbackData = callbackData ;
00894
00895 }
00896
00897
00898
00899 const string Scheduler::toString( Ceylan::VerbosityLevels level ) const
00900 {
00901
00902 ostringstream buf ;
00903 buf.precision( 2 ) ;
00904
00905 buf << setiosflags( std::ios::fixed )
00906 << "Scheduler, whose internal frequency is "
00907 << 1E6 / _engineTickDuration
00908 << " Hz (engine tick duration is "
00909 + Ceylan::toString( _engineTickDuration )
00910 + " microseconds). Current engine tick is "
00911 + Ceylan::toString( _currentEngineTick )
00912 + ", current simulation tick is "
00913 + Ceylan::toString( _currentSimulationTick )
00914 + ", current rendering tick is "
00915 + Ceylan::toString( _currentRenderingTick )
00916 + " and current input tick is "
00917 + Ceylan::toString( _currentInputTick )
00918 + ". Screenshot mode is "
00919 + ( _screenshotMode ? "on.": "off." ) ;
00920
00921
00922 if ( _isRunning )
00923 {
00924
00925 Ceylan::System::Second sec ;
00926 Ceylan::System::Microsecond microsec ;
00927 Ceylan::System::getPreciseTime( sec, microsec ) ;
00928
00929
00930
00931
00932
00933
00934 buf << " Scheduler has been running for "
00935 << Ceylan::System::durationToString( _scheduleStartingSecond,
00936 _scheduleStartingMicrosecond, sec, microsec )
00937 << "." ;
00938
00939 }
00940 else
00941 {
00942
00943 buf << " Scheduler is not running." ;
00944
00945 }
00946
00947
00948 if ( _renderer != 0 )
00949 buf << " Using a renderer ("
00950 + _renderer->toString( Ceylan::low ) + ")" ;
00951 else
00952 buf << " No renderer registered, using directly video module" ;
00953
00954 if ( _idleCallback == 0 )
00955 buf << ". Using default atomic sleep idle callback" ;
00956 else
00957 buf << ". Using user-specified idle callback" ;
00958
00959
00960 if ( _idleCallbackData )
00961 {
00962
00963 if ( _idleCallback != 0 )
00964 buf << ", callback data has been set as well." ;
00965 else
00966 buf << ", callback data has been set, "
00967 " whereas it is not used by default idle callback (abnormal)." ;
00968
00969 }
00970
00971
00972 buf << ". The estimated upper-bound for idle callback duration is "
00973 + Ceylan::toString( _idleCallbackMaxDuration )
00974 + " microseconds" ;
00975
00976 buf << ". Minimal measured callback duration was "
00977 + Ceylan::toString( _idleCallbackMinMeasuredDuration )
00978 + " microseconds, maximal was "
00979 + Ceylan::toString( _idleCallbackMaxMeasuredDuration )
00980 + " microseconds" ;
00981
00982
00983 if ( level == Ceylan::low )
00984 return buf.str() ;
00985
00986
00987
00988 buf << ". User-defined simulation frequency is "
00989 + Ceylan::toString( 1E6 / ( _simulationPeriod * _engineTickDuration ),
00990 2 )
00991 + " Hz (a period of "
00992 + Ceylan::toString( _simulationPeriod )
00993 + " engine ticks), rendering frequency is "
00994 + Ceylan::toString( 1E6 / ( _renderingPeriod * _engineTickDuration ),
00995 2 )
00996 + " Hz (a period of "
00997 + Ceylan::toString( _renderingPeriod )
00998 + " engine ticks), input polling frequency is "
00999 + Ceylan::toString( 1E6 / ( _inputPeriod * _engineTickDuration ),
01000 2 )
01001 + " Hz (a period of "
01002 + Ceylan::toString( _inputPeriod )
01003 + " engine ticks). There are "
01004 + Ceylan::toString(
01005 static_cast<Ceylan::Uint32>( _periodicSlots.size() ) )
01006 + " used periodic slot(s) and "
01007 + Ceylan::toString(
01008 static_cast<Ceylan::Uint32>( _programmedActivated.size() ) )
01009 + " programmed object(s)" ;
01010
01011
01012 if ( level == Ceylan::medium )
01013 return buf.str() ;
01014
01015
01016 if ( ! _periodicSlots.empty() )
01017 {
01018
01019 buf << ". Enumerating scheduling periodic slots: " ;
01020
01021 std::list<string> slots ;
01022
01023 for ( list<PeriodicSlot*>::const_iterator it = _periodicSlots.begin();
01024 it != _periodicSlots.end(); it++ )
01025 slots.push_back( (*it)->toString( level ) ) ;
01026
01027 buf << Ceylan::formatStringList( slots ) ;
01028
01029 }
01030
01031 if ( ! _programmedActivated.empty() )
01032 {
01033
01034 buf << ". Enumerating scheduling programmed slots: " ;
01035
01036 std::list<string> programmed ;
01037
01038 for( map<SimulationTick,
01039 ListOfProgrammedActiveObjects>::const_iterator it
01040 = _programmedActivated.begin();
01041 it != _programmedActivated.end(); it++ )
01042 programmed.push_back( "For simulation tick #"
01043 + Ceylan::toString( (*it).first ) + ", there are "
01044 + Ceylan::toString(
01045 static_cast<Ceylan::Uint32>( (*it).second.size() ) )
01046 + " object(s) programmed (note: some of them may be null "
01047 "pointers corresponding to unregistered objects)" ) ;
01048
01049 buf << Ceylan::formatStringList( programmed ) ;
01050
01051 }
01052
01053
01054 if ( _screenshotMode )
01055 {
01056
01057 buf << ". Current movie frame period for screenshot mode is "
01058 + Ceylan::toString( _screenshotPeriod )
01059 + " engine ticks, which corresponds to a frequency of "
01060 + Ceylan::toString( 1E6 /
01061 ( _screenshotPeriod * _engineTickDuration ), 2 )
01062 + " frames per second" ;
01063
01064
01065 return buf.str() ;
01066 }
01067
01068
01069 if ( _missedSimulationTicks == 0 )
01070 {
01071
01072 buf << ". No simulation tick was missed" ;
01073
01074 }
01075 else
01076 {
01077
01078 Ceylan::System::Second sec ;
01079 Ceylan::System::Microsecond microsec ;
01080 Ceylan::System::getPreciseTime( sec, microsec ) ;
01081
01082
01083 buf << ". " + Ceylan::toString( _missedSimulationTicks )
01084 + " simulation ticks were missed, "
01085 "it sums up to an actual average simulation frequency of "
01086 << 1E6 * ( _currentSimulationTick - _missedSimulationTicks ) /
01087 static_cast<Ceylan::Float64>(
01088 ( sec - _scheduleStartingSecond ) * 1E6
01089 + microsec - _scheduleStartingMicrosecond )
01090 << " Hz" ;
01091
01092 }
01093
01094
01095 if ( _missedRenderingTicks == 0 )
01096 buf << ". No rendering tick was missed" ;
01097 else
01098 {
01099
01100 Ceylan::System::Second sec ;
01101 Ceylan::System::Microsecond microsec ;
01102 Ceylan::System::getPreciseTime( sec, microsec ) ;
01103
01104 buf << ". " + Ceylan::toString( _missedRenderingTicks )
01105 + " rendering ticks were missed, "
01106 "it sums up to an actual average rendering frequency of "
01107 << 1E6 * ( _currentRenderingTick - _missedRenderingTicks ) /
01108 static_cast<Ceylan::Float64>(
01109 ( sec - _scheduleStartingSecond ) * 1E6
01110 + microsec - _scheduleStartingMicrosecond )
01111 << " Hz" ;
01112 }
01113
01114
01115 if ( _missedInputPollingTicks == 0 )
01116 buf << ". No input tick was missed" ;
01117 else
01118 {
01119
01120 Ceylan::System::Second sec ;
01121 Ceylan::System::Microsecond microsec ;
01122 Ceylan::System::getPreciseTime( sec, microsec ) ;
01123
01124 buf << ". " + Ceylan::toString( _missedInputPollingTicks )
01125 + " input ticks were missed, "
01126 "it sums up to an actual average input frequency of "
01127 << 1E6 * ( _currentInputTick - _missedInputPollingTicks ) /
01128 static_cast<Ceylan::Float64>(
01129 ( sec - _scheduleStartingSecond ) * 1E6
01130 + microsec - _scheduleStartingMicrosecond )
01131 << " Hz" ;
01132 }
01133
01134 return buf.str() ;
01135
01136 }
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146 Scheduler & Scheduler::GetExistingScheduler()
01147 {
01148
01149 if ( Scheduler::_internalScheduler == 0 )
01150 throw SchedulingException(
01151 "Scheduler::GetExistingScheduler: no scheduler available." ) ;
01152
01153 return * Scheduler::_internalScheduler ;
01154
01155 }
01156
01157
01158
01159 Scheduler & Scheduler::GetScheduler()
01160 {
01161
01162 if ( Scheduler::_internalScheduler == 0 )
01163 {
01164
01165 LogPlug::debug( "Scheduler::GetScheduler: "
01166 "no scheduler available, creating a new one." ) ;
01167
01168 Scheduler::_internalScheduler = new Scheduler() ;
01169
01170 }
01171 else
01172 {
01173
01174 LogPlug::debug( "Scheduler::GetScheduler: "
01175 "returning already constructed instance of scheduler, "
01176 "no creation." ) ;
01177
01178 }
01179
01180 LogPlug::debug( "Scheduler::GetScheduler: returning Scheduler instance "
01181 + Ceylan::toString( Scheduler::_internalScheduler ) ) ;
01182
01183 return * Scheduler::_internalScheduler ;
01184
01185 }
01186
01187
01188
01189 void Scheduler::DeleteExistingScheduler()
01190 {
01191
01192 if ( Scheduler::_internalScheduler != 0 )
01193 throw SchedulingException( "Scheduler::DeleteExistingScheduler: "
01194 "there was no already existing scheduler." ) ;
01195
01196 #if OSDL_DEBUG_SCHEDULER
01197 LogPlug::debug(
01198 "Scheduler::DeleteExistingScheduler: effective deleting." ) ;
01199 #endif // OSDL_DEBUG_SCHEDULER
01200
01201 delete Scheduler::_internalScheduler ;
01202 Scheduler::_internalScheduler = 0 ;
01203
01204 }
01205
01206
01207
01208 void Scheduler::DeleteScheduler()
01209 {
01210
01211 if ( Scheduler::_internalScheduler != 0 )
01212 {
01213
01214 #if OSDL_DEBUG_SCHEDULER
01215 LogPlug::debug( "Scheduler::DeleteScheduler: effective deleting." ) ;
01216 #endif // OSDL_DEBUG_SCHEDULER
01217
01218 delete Scheduler::_internalScheduler ;
01219 Scheduler::_internalScheduler = 0 ;
01220
01221 }
01222 else
01223 {
01224
01225 #if OSDL_DEBUG_SCHEDULER
01226 LogPlug::debug( "Scheduler::DeleteScheduler: no deleting needed." ) ;
01227 #endif // OSDL_DEBUG_SCHEDULER
01228
01229 }
01230
01231 }
01232
01233
01234
01235 void Scheduler::StopExistingScheduler()
01236 {
01237
01238 if ( Scheduler::_internalScheduler != 0 )
01239 {
01240
01241 #if OSDL_DEBUG_SCHEDULER
01242 LogPlug::debug( "Scheduler::StopExistingScheduler: stopping now." ) ;
01243 #endif // OSDL_DEBUG_SCHEDULER
01244
01245 Scheduler::_internalScheduler->stop() ;
01246
01247 }
01248 else
01249 {
01250
01251 throw SchedulingException( "Scheduler::StopExistingScheduler failed: "
01252 "no scheduler available." ) ;
01253
01254 }
01255
01256 }
01257
01258
01259
01260
01261
01262
01263
01264 Scheduler::Scheduler():
01265 _screenshotMode( false ),
01266 _desiredScreenshotFrequency( DefaultMovieFrameFrequency ),
01267 _screenshotPeriod( 0 ),
01268 _periodicSlots(),
01269 _initialRegisteredObjects(),
01270 _programmedActivated(),
01271 _engineTickDuration( 0 ),
01272 _secondToEngineTick( 0 ),
01273 _currentEngineTick( 0 ),
01274 _currentSimulationTick( 0 ),
01275 _currentRenderingTick( 0 ),
01276 _currentInputTick( 0 ),
01277 _simulationPeriod( 0 ),
01278 _renderingPeriod( 0 ),
01279 _inputPeriod( 0 ),
01280 _desiredSimulationFrequency( DefaultSimulationFrequency ),
01281 _desiredRenderingFrequency( DefaultRenderingFrequency ),
01282 _desiredInputFrequency( DefaultInputFrequency ),
01283 _idleCallback( 0 ),
01284 _idleCallbackData( 0 ),
01285 _idleCallbackMaxDuration( 0 ),
01286 _idleCallbackMinMeasuredDuration( 0 ),
01287 _idleCallbackMaxMeasuredDuration( 0 ),
01288 _idleCallsCount( 0 ),
01289 _isRunning( false ),
01290 _stopRequested( false ),
01291 _stopCallback( 0 ),
01292 _stopCallbackData( 0 ),
01293 _scheduleStartingSecond( 0 ),
01294 _scheduleStartingMicrosecond( 0 ),
01295 _recoveredSimulationTicks( 0 ),
01296 _missedSimulationTicks( 0 ),
01297 _recoveredRenderingTicks( 0 ),
01298 _missedRenderingTicks( 0 ),
01299 _recoveredInputPollingTicks( 0 ),
01300 _missedInputPollingTicks( 0 ),
01301 _scheduleFailureCount( 0 ),
01302 _eventsModule( 0 ),
01303 _renderer( 0 ),
01304 _videoModule( 0 )
01305 {
01306
01307 _subSecondSleepsAvailable = Ceylan::System::areSubSecondSleepsAvailable() ;
01308
01309 if ( ! _subSecondSleepsAvailable )
01310 throw SchedulingException( "Scheduler constructor: "
01311 "scheduler could not be created as no subsecond sleep is "
01312 "available on this platform." ) ;
01313
01314
01315 send( "On scheduler creation, "
01316 "detected operating system scheduling granularity is about "
01317 + Ceylan::toString( getSchedulingGranularity() ) + " microseconds." ) ;
01318
01319
01320 setTimeSliceDuration( DefaultEngineTickDuration ) ;
01321
01322 send( "Scheduler created." ) ;
01323
01324 dropIdentifier() ;
01325
01326 }
01327
01328
01329
01330 Scheduler::~Scheduler() throw()
01331 {
01332
01333 send( "Deleting scheduler." ) ;
01334
01335 for ( list<PeriodicSlot *>::iterator it = _periodicSlots.begin();
01336 it != _periodicSlots.end(); it++ )
01337 delete (*it ) ;
01338
01339 send( "Periodic slots deleted." ) ;
01340
01341
01342 if ( _renderer != 0 )
01343 delete _renderer ;
01344
01345 send( "Scheduler deleted." ) ;
01346
01347 }
01348
01349
01350
01351 void Scheduler::scheduleBestEffort()
01352 {
01353
01354 _isRunning = true ;
01355
01356
01357
01358
01359
01360
01361 if ( _idleCallback == 0 && _idleCallbackMaxDuration == 0 )
01362 {
01363
01364 Ceylan::System::Second testStartingSecond, testStoppingSecond ;
01365
01366 Ceylan::System::Microsecond testStartingMicrosecond,
01367 testStoppingMicrosecond ;
01368
01369 getPreciseTime( testStartingSecond, testStartingMicrosecond ) ;
01370
01371 onIdle() ;
01372
01373 getPreciseTime( testStoppingSecond, testStoppingMicrosecond ) ;
01374
01375 Microsecond testDuration = getDurationBetween(
01376 testStartingSecond, testStartingMicrosecond,
01377 testStoppingSecond, testStoppingMicrosecond ) ;
01378
01379
01380
01381
01382
01383
01384
01385 _idleCallbackMaxDuration = Ceylan::Maths::Max( testDuration,
01386 getSchedulingGranularity() ) ;
01387
01388 OSDL_SCHEDULE_LOG(
01389 "Evaluating the maximum duration of the default idle callback to "
01390 + Ceylan::toString( _idleCallbackMaxDuration )+ " microseconds." ) ;
01391
01392 }
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422 Microsecond baseIdleCallbackDuration = _idleCallbackMaxDuration ;
01423
01424
01425 EngineTick baseIdleCallbackTickCount = static_cast<EngineTick>(
01426 Ceylan::Maths::Ceil(
01427 static_cast<Ceylan::Float32>( baseIdleCallbackDuration ) /
01428 _engineTickDuration ) ) ;
01429
01430 if ( baseIdleCallbackTickCount == 0 )
01431 baseIdleCallbackTickCount = 1 ;
01432
01433
01434
01435
01436 OSDL_SCHEDULE_LOG( "Initial estimated duration for the idle callback is "
01437 + Ceylan::toString( _idleCallbackMaxDuration ) + " microseconds, i.e. "
01438 + Ceylan::toString( baseIdleCallbackTickCount )
01439 + " engine ticks." ) ;
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455 getPreciseTime( _scheduleStartingSecond, _scheduleStartingMicrosecond ) ;
01456
01457 send( "Scheduler starting in soft real-time best effort mode. "
01458 "Scheduler informations: " + toString( Ceylan::low ) ) ;
01459
01460
01461
01462
01463
01464
01465
01466 _idleCallbackMinMeasuredDuration = 10 * _idleCallbackMaxDuration ;
01467
01468
01469
01470
01471
01472
01473
01474
01475 _idleCallbackMaxMeasuredDuration = _idleCallbackMaxDuration ;
01476
01477 Events::EngineTick idleCallbackMaxTickCount =
01478 _idleCallbackMaxMeasuredDuration / _engineTickDuration + 1 ;
01479
01480
01481
01482
01483 _stopRequested = false ;
01484
01485 _recoveredSimulationTicks = 0 ;
01486 _missedSimulationTicks = 0 ;
01487
01488 _recoveredRenderingTicks = 0 ;
01489 _missedRenderingTicks = 0 ;
01490
01491 _recoveredInputPollingTicks = 0 ;
01492 _missedInputPollingTicks = 0 ;
01493
01494
01495 #if OSDL_DEBUG_SCHEDULER
01496
01497
01498
01499
01500
01501
01502
01503
01504 list<SimulationTick> metSimulations ;
01505 list<SimulationTick> recoveredSimulations ;
01506 list<SimulationTick> missedSimulations ;
01507
01508 list<RenderingTick> metRenderings ;
01509 list<RenderingTick> recoveredRenderings ;
01510 list<RenderingTick> missedRenderings ;
01511
01512 list<InputTick> metInputPollings ;
01513 list<InputTick> recoveredInputPollings ;
01514 list<InputTick> missedInputPollings ;
01515
01516 list<Microsecond> forecastIdleCallbackDurationList ;
01517 list<Microsecond> actualIdleCallbackDurationList ;
01518
01519 #endif // OSDL_DEBUG_SCHEDULER
01520
01521
01522
01523
01524
01525
01526
01527
01528
01529 _currentEngineTick = computeEngineTickFromCurrentTime() ;
01530
01531 OSDL_SCHEDULE_LOG( "Simulation period: "
01532 + Ceylan::toString( _simulationPeriod ) + " engine ticks" ) ;
01533
01534 OSDL_SCHEDULE_LOG( "Rendering period: "
01535 + Ceylan::toString( _renderingPeriod ) + " engine ticks" ) ;
01536
01537 OSDL_SCHEDULE_LOG( "Input period: "
01538 + Ceylan::toString( _inputPeriod ) + " engine ticks" ) ;
01539
01540 _currentSimulationTick = _currentEngineTick / _simulationPeriod ;
01541 _currentRenderingTick = _currentEngineTick / _renderingPeriod;
01542 _currentInputTick = _currentEngineTick / _inputPeriod;
01543
01544
01545
01546
01547
01548
01549
01550
01551 setInitialBirthTicks( _currentSimulationTick ) ;
01552
01553 OSDL_SCHEDULE_LOG( "Initial engine tick: "
01554 + Ceylan::toString( _currentEngineTick ) ) ;
01555
01556 OSDL_SCHEDULE_LOG( "Initial simulation tick: "
01557 + Ceylan::toString( _currentSimulationTick ) ) ;
01558
01559 OSDL_SCHEDULE_LOG( "Initial rendering tick: "
01560 + Ceylan::toString( _currentRenderingTick ) ) ;
01561
01562 OSDL_SCHEDULE_LOG( "Initial input tick: "
01563 + Ceylan::toString( _currentInputTick ) ) ;
01564
01565
01566 EngineTick nextSimulationDeadline = _currentEngineTick + _simulationPeriod ;
01567 EngineTick nextRenderingDeadline = _currentEngineTick + _renderingPeriod ;
01568 EngineTick nextInputDeadline = _currentEngineTick + _inputPeriod ;
01569
01570 EngineTick nextDeadline = Ceylan::Maths::Min( nextSimulationDeadline,
01571 nextRenderingDeadline, nextInputDeadline ) ;
01572
01573 OSDL_SCHEDULE_LOG( "Initial deadline: "
01574 + Ceylan::toString( nextDeadline ) ) ;
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606 Delay delayBucket = 0 ;
01607
01608
01609 Delay maxDelayBucket = 0 ;
01610
01611
01612
01613
01614
01615
01616 Delay delayCumulativeBucket = 0 ;
01617
01618
01619
01620
01621
01622
01623
01624 const Delay bucketFillThreshold = 100000 ;
01625
01626
01627
01628
01629
01630
01631
01632 const Ceylan::Float32 bucketLeakingFactor = 0.9f ;
01633
01634 _scheduleFailureCount = 0 ;
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651 const Microsecond simulationToleranceTime = 800000 ;
01652
01653 const EngineTick simulationToleranceTick = static_cast<EngineTick>(
01654 simulationToleranceTime / _engineTickDuration ) ;
01655
01656 send( "Simulation tolerance time is "
01657 + Ceylan::toString( simulationToleranceTime ) + " microseconds, i.e. "
01658 + Ceylan::toString( simulationToleranceTick ) + " engine ticks." ) ;
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681 const EngineTick renderingToleranceTick = _renderingPeriod / 4 ;
01682
01683 send( "Rendering tolerance is "
01684 + Ceylan::toString( renderingToleranceTick )
01685 + " engine ticks, which translates into a tolerance of "
01686 + Ceylan::toString( renderingToleranceTick * _engineTickDuration )
01687 + " microseconds." ) ;
01688
01689
01690
01691
01692
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705
01706
01707
01708
01709
01710 const EngineTick inputToleranceTick = _inputPeriod / 2 ;
01711
01712 send( "Input polling tolerance is "
01713 + Ceylan::toString( inputToleranceTick )
01714 + " engine ticks, which translates into a tolerance of "
01715 + Ceylan::toString( inputToleranceTick * _engineTickDuration )
01716 + " microseconds." ) ;
01717
01718
01719
01720 getPreciseTime( _scheduleStartingSecond, _scheduleStartingMicrosecond ) ;
01721
01722 Ceylan::System::Second idleStartingSecond, idleStoppingSecond ;
01723
01724 Ceylan::System::Microsecond idleStartingMicrosecond,
01725 idleStoppingMicrosecond ;
01726
01727
01728 _idleCallsCount = 0 ;
01729
01730
01731
01732 Events::EngineTick lastIdleTickCount = idleCallbackMaxTickCount ;
01733
01734
01735
01736
01737
01738
01739
01740 Events::EngineTick forecastIdleCallbackTickCount =
01741 idleCallbackMaxTickCount / 2 ;
01742
01743
01744
01745
01746
01747 while ( ! _stopRequested )
01748 {
01749
01750
01751 OSDL_SCHEDULE_LOG( "[ E: " + Ceylan::toString( _currentEngineTick )
01752 + " ; S: " + Ceylan::toString( _currentSimulationTick )
01753 + " ; R: " + Ceylan::toString( _currentRenderingTick )
01754 + " ; I: " + Ceylan::toString( _currentInputTick )
01755 + " ; B: " + Ceylan::toString( delayBucket ) + " ]" ) ;
01756
01757
01758 #if OSDL_DEBUG_SCHEDULER
01759
01760
01761 if ( _currentEngineTick % 1000 == 0 )
01762 send( "[ E: " + Ceylan::toString( _currentEngineTick )
01763 + " ; S: " + Ceylan::toString( _currentSimulationTick )
01764 + " ; R: " + Ceylan::toString( _currentRenderingTick )
01765 + " ; I: " + Ceylan::toString( _currentInputTick )
01766 + " ; B: " + Ceylan::toString( delayBucket )
01767 + " ]" ) ;
01768
01769 #endif // OSDL_DEBUG_SCHEDULER
01770
01771
01772 delayCumulativeBucket += delayBucket ;
01773
01774
01775 delayBucket = static_cast<Delay>( delayBucket * bucketLeakingFactor ) ;
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796 if ( _currentEngineTick == nextSimulationDeadline )
01797 {
01798
01799
01800
01801 #if OSDL_DEBUG_SCHEDULER
01802 metSimulations.push_back( _currentSimulationTick ) ;
01803 #endif // OSDL_DEBUG_SCHEDULER
01804
01805 scheduleSimulation( _currentSimulationTick ) ;
01806
01807 _currentSimulationTick++ ;
01808 nextSimulationDeadline += _simulationPeriod ;
01809
01810 }
01811
01812
01813
01814 if ( _currentEngineTick == nextRenderingDeadline )
01815 {
01816
01817
01818
01819 #if OSDL_DEBUG_SCHEDULER
01820 metRenderings.push_back( _currentRenderingTick ) ;
01821 #endif // OSDL_DEBUG_SCHEDULER
01822
01823 scheduleRendering( _currentRenderingTick ) ;
01824
01825 _currentRenderingTick++ ;
01826 nextRenderingDeadline += _renderingPeriod ;
01827
01828 }
01829
01830
01831
01832 if ( _currentEngineTick == nextInputDeadline )
01833 {
01834
01835
01836
01837 #if OSDL_DEBUG_SCHEDULER
01838 metInputPollings.push_back( _currentInputTick ) ;
01839 #endif // OSDL_DEBUG_SCHEDULER
01840
01841 scheduleInput( _currentInputTick ) ;
01842
01843 _currentInputTick++ ;
01844 nextInputDeadline += _inputPeriod ;
01845
01846 }
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867 _currentEngineTick = computeEngineTickFromCurrentTime() ;
01868
01869
01870
01871
01872
01873
01874
01875
01876 while ( nextSimulationDeadline < _currentEngineTick + 1
01877 && delayBucket < ShutdownBucketLevel )
01878 {
01879
01880
01881
01882 EngineTick missedTicks =
01883 _currentEngineTick + 1 - nextSimulationDeadline ;
01884
01885 if ( missedTicks > simulationToleranceTick )
01886 {
01887
01888
01889
01890
01891
01892
01893
01894
01895
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925
01926
01927
01928
01929
01930
01931
01932 delayBucket += 35 * static_cast<Delay>(
01933 Ceylan::Maths::Sqrt( 2.0f * missedTicks ) ) ;
01934
01935
01936
01937
01938
01939
01940 #if OSDL_DEBUG_SCHEDULER
01941 missedSimulations.push_back( _currentSimulationTick ) ;
01942 #endif // OSDL_DEBUG_SCHEDULER
01943
01944
01945
01946
01947
01948
01949
01950 onSimulationSkipped( _currentSimulationTick ) ;
01951
01952
01953 }
01954 else
01955 {
01956
01957
01958 _recoveredSimulationTicks++ ;
01959
01960
01961 missedTicks-- ;
01962
01963 OSDL_SCHEDULE_LOG( "@@@@@ Simulation deadline #"
01964 + Ceylan::toString( _currentSimulationTick )
01965 + " recovered from "
01966 + Ceylan::toString( missedTicks )
01967 + " engine ticks delay ("
01968 + Ceylan::toString( missedTicks * _engineTickDuration )
01969 + " microseconds)." ) ;
01970
01971
01972 delayBucket += 20 ;
01973
01974 #if OSDL_DEBUG_SCHEDULER
01975 recoveredSimulations.push_back( _currentSimulationTick ) ;
01976 #endif // OSDL_DEBUG_SCHEDULER
01977
01978 scheduleSimulation( _currentSimulationTick ) ;
01979
01980 }
01981
01982 _currentSimulationTick++ ;
01983
01984 nextSimulationDeadline += _simulationPeriod ;
01985
01986 _currentEngineTick = computeEngineTickFromCurrentTime() ;
01987
01988
01989
01990 }
01991
01992
01993
01994
01995
01996
01997
01998
01999
02000
02001
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012 bool renderingCompensated = false ;
02013
02014 while ( nextRenderingDeadline < _currentEngineTick + 1
02015 && delayBucket < ShutdownBucketLevel )
02016 {
02017
02018
02019 EngineTick missedTicks =
02020 _currentEngineTick + 1 - nextRenderingDeadline ;
02021
02022 if ( missedTicks > renderingToleranceTick
02023 || renderingCompensated )
02024 {
02025
02026
02027
02028
02029
02030
02031
02032
02033
02034
02035
02036 delayBucket += 5 * static_cast<Delay>(
02037 Ceylan::Maths::Sqrt( 2.0f * missedTicks ) ) ;
02038
02039
02040 #if OSDL_DEBUG_SCHEDULER
02041 missedRenderings.push_back( _currentRenderingTick ) ;
02042 #endif // OSDL_DEBUG_SCHEDULER
02043
02044 onRenderingSkipped( _currentRenderingTick ) ;
02045
02046 }
02047 else
02048 {
02049
02050
02051 _recoveredRenderingTicks++ ;
02052
02053
02054 missedTicks-- ;
02055
02056 OSDL_SCHEDULE_LOG( "@@@@@ Rendering deadline #"
02057 + Ceylan::toString( _currentRenderingTick )
02058 + " recovered from " + Ceylan::toString( missedTicks )
02059 + " engine ticks delay ("
02060 + Ceylan::toString( missedTicks * _engineTickDuration )
02061 + " microseconds)." ) ;
02062
02063
02064 delayBucket += 10 ;
02065
02066 #if OSDL_DEBUG_SCHEDULER
02067 recoveredRenderings.push_back( _currentRenderingTick ) ;
02068 #endif // OSDL_DEBUG_SCHEDULER
02069
02070 scheduleRendering( _currentRenderingTick ) ;
02071
02072 renderingCompensated = true ;
02073
02074 }
02075
02076 _currentRenderingTick++ ;
02077
02078 nextRenderingDeadline += _renderingPeriod ;
02079
02080 _currentEngineTick = computeEngineTickFromCurrentTime() ;
02081
02082 }
02083
02084
02085
02086
02087
02088
02089
02090
02091
02092
02093
02094
02095
02096
02097 bool inputPollingCompensated = false ;
02098
02099 while ( nextInputDeadline < _currentEngineTick + 1
02100 && delayBucket < ShutdownBucketLevel )
02101 {
02102
02103
02104
02105
02106
02107
02108 EngineTick missedTicks =
02109 _currentEngineTick + 1 - nextInputDeadline ;
02110
02111 if ( missedTicks > inputToleranceTick || inputPollingCompensated )
02112 {
02113
02114
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124 delayBucket += static_cast<Delay>(
02125 Ceylan::Maths::Sqrt( 2.0f * missedTicks ) ) ;
02126
02127 #if OSDL_DEBUG_SCHEDULER
02128 missedInputPollings.push_back( _currentInputTick ) ;
02129 #endif // OSDL_DEBUG_SCHEDULER
02130
02131 onInputSkipped( _currentInputTick ) ;
02132
02133 }
02134 else
02135 {
02136
02137
02138 _recoveredInputPollingTicks++ ;
02139
02140
02141 missedTicks-- ;
02142
02143 OSDL_SCHEDULE_LOG( "@@@@@ Input deadline #"
02144 + Ceylan::toString( _currentInputTick )
02145 + " recovered from " + Ceylan::toString( missedTicks )
02146 + " engine ticks delay ("
02147 + Ceylan::toString( missedTicks * _engineTickDuration )
02148 + " microseconds)." ) ;
02149
02150
02151 delayBucket += 5 ;
02152
02153 #if OSDL_DEBUG_SCHEDULER
02154 recoveredInputPollings.push_back( _currentInputTick ) ;
02155 #endif // OSDL_DEBUG_SCHEDULER
02156
02157 scheduleInput( _currentInputTick ) ;
02158
02159 inputPollingCompensated = true ;
02160
02161 }
02162
02163 _currentInputTick++ ;
02164
02165 nextInputDeadline += _inputPeriod ;
02166
02167 _currentEngineTick = computeEngineTickFromCurrentTime() ;
02168
02169 }
02170
02171 if ( maxDelayBucket < delayBucket )
02172 maxDelayBucket = delayBucket ;
02173
02174
02175
02176 if ( delayBucket > bucketFillThreshold )
02177 onScheduleFailure( delayBucket ) ;
02178
02179
02180
02181
02182
02183
02184 nextDeadline = Ceylan::Maths::Min( nextSimulationDeadline,
02185 nextRenderingDeadline, nextInputDeadline ) ;
02186
02187 _currentEngineTick = computeEngineTickFromCurrentTime() ;
02188
02189 if ( nextDeadline < _currentEngineTick )
02190 {
02191
02192 string message = "We are at engine tick #"
02193 + Ceylan::toString( _currentEngineTick )
02194 + ", already late for next deadline, which is at "
02195 + Ceylan::toString( nextDeadline ) + "." ;
02196
02197 OSDL_SCHEDULE_LOG( message ) ;
02198 LogPlug::warning( message ) ;
02199
02200
02201 continue ;
02202 }
02203 else if ( nextDeadline <= _currentEngineTick + 1 )
02204 {
02205
02206
02207
02208
02209
02210
02211
02212 OSDL_SCHEDULE_LOG(
02213 "No jump could be performed, just continuing." ) ;
02214
02215 }
02216 else
02217 {
02218
02219
02220
02221
02222
02223
02224
02225 EngineTick jumpLength = nextDeadline - _currentEngineTick - 1 ;
02226
02227 OSDL_SCHEDULE_LOG( "Next deadline is "
02228 + Ceylan::toString( jumpLength + 1 )
02229 + " engine tick(s) away, preparing jump." ) ;
02230
02231
02232
02233 while ( jumpLength > 0 )
02234 {
02235
02236 delayBucket = static_cast<Delay>(
02237 delayBucket * bucketLeakingFactor ) ;
02238 delayCumulativeBucket += delayBucket ;
02239 jumpLength-- ;
02240
02241 } ;
02242
02243 }
02244
02245 _currentEngineTick = computeEngineTickFromCurrentTime() ;
02246
02247 OSDL_SCHEDULE_LOG( "We are now at engine tick "
02248 + Ceylan::toString( _currentEngineTick )
02249 + ", just before attempting idle call." ) ;
02250
02251
02252
02253
02254
02255
02256
02257
02258 Microsecond lastIdleDuration ;
02259
02260
02261 while ( _currentEngineTick + forecastIdleCallbackTickCount
02262 < nextDeadline )
02263 {
02264
02265 OSDL_SCHEDULE_LOG( "baseIdleCallbackTickCount = "
02266 + Ceylan::toString( baseIdleCallbackTickCount )
02267 + " engine ticks" ) ;
02268
02269 OSDL_SCHEDULE_LOG( "forecastIdleCallbackTickCount = "
02270 + Ceylan::toString( forecastIdleCallbackTickCount )
02271 + " engine ticks" ) ;
02272
02273 OSDL_SCHEDULE_LOG( "lastIdleTickCount = "
02274 + Ceylan::toString( lastIdleTickCount )
02275 + " engine ticks" ) ;
02276
02277 OSDL_SCHEDULE_LOG( "idleCallbackMaxTickCount = "
02278 + Ceylan::toString( idleCallbackMaxTickCount )
02279 + " engine ticks" ) ;
02280
02281
02282 forecastIdleCallbackTickCount = static_cast<Microsecond>(
02283 ( baseIdleCallbackTickCount + 3 * forecastIdleCallbackTickCount
02284 + lastIdleTickCount + idleCallbackMaxTickCount ) / 6.0f ) ;
02285
02286 OSDL_SCHEDULE_LOG( "Current forecasted idle callback duration is "
02287 + Ceylan::toString( forecastIdleCallbackTickCount
02288 * _engineTickDuration ) + " microseconds, i.e. "
02289 + Ceylan::toString( forecastIdleCallbackTickCount )
02290 + " engine ticks." ) ;
02291
02292 #if OSDL_DEBUG_SCHEDULER
02293 forecastIdleCallbackDurationList.push_back(
02294 forecastIdleCallbackTickCount * _engineTickDuration ) ;
02295 #endif // OSDL_DEBUG_SCHEDULER
02296
02297 getPreciseTime( idleStartingSecond, idleStartingMicrosecond ) ;
02298
02299
02300
02301
02302
02303
02304 onIdle() ;
02305
02306 getPreciseTime( idleStoppingSecond, idleStoppingMicrosecond ) ;
02307
02308 lastIdleDuration = getDurationBetween(
02309 idleStartingSecond, idleStartingMicrosecond,
02310 idleStoppingSecond, idleStoppingMicrosecond ) ;
02311
02312 #if OSDL_DEBUG_SCHEDULER
02313 actualIdleCallbackDurationList.push_back( lastIdleDuration ) ;
02314 #endif // OSDL_DEBUG_SCHEDULER
02315
02316 lastIdleTickCount = lastIdleDuration / _engineTickDuration ;
02317
02318 OSDL_SCHEDULE_LOG( Ceylan::toString( lastIdleDuration )
02319 + " microseconds were spent in the idle call, "
02320 "corresponding to " + Ceylan::toString( lastIdleTickCount )
02321 + " engine ticks." ) ;
02322
02323
02324
02325
02326 _idleCallbackMinMeasuredDuration = Ceylan::Maths::Min(
02327 _idleCallbackMinMeasuredDuration, lastIdleDuration ) ;
02328
02329 _idleCallbackMaxMeasuredDuration = Ceylan::Maths::Max(
02330 _idleCallbackMaxMeasuredDuration, lastIdleDuration ) ;
02331
02332 _currentEngineTick = computeEngineTickFromCurrentTime() ;
02333
02334 OSDL_SCHEDULE_LOG( "We are now at engine tick "
02335 + Ceylan::toString( _currentEngineTick ) ) ;
02336
02337 }
02338
02339 OSDL_SCHEDULE_LOG( "We are now at engine tick "
02340 + Ceylan::toString( _currentEngineTick )
02341 + ", just after having attempted idle calls." ) ;
02342
02343
02344
02345 while ( _currentEngineTick < nextDeadline )
02346 {
02347
02348 OSDL_SCHEDULE_LOG( "Busy waiting at engine tick "
02349 + Ceylan::toString( _currentEngineTick ) ) ;
02350
02351 _currentEngineTick = computeEngineTickFromCurrentTime() ;
02352
02353 }
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363 }
02364
02365
02366 Second scheduleStoppingSecond ;
02367 Microsecond scheduleStoppingMicrosecond ;
02368
02369 getPreciseTime( scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
02370
02371
02372 Ceylan::Float64 totalRuntimeFactor ;
02373
02374
02375 if ( scheduleStoppingSecond - _scheduleStartingSecond <
02376 MaximumDurationWithMicrosecondAccuracy )
02377 {
02378
02379 totalRuntimeFactor = 1E6 / getDurationBetween(
02380 _scheduleStartingSecond, _scheduleStartingMicrosecond,
02381 scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
02382
02383 }
02384 else
02385 {
02386
02387
02388 totalRuntimeFactor = 1.0f /
02389 ( scheduleStoppingSecond - _scheduleStartingSecond ) ;
02390
02391 }
02392
02393
02394 string table =
02395 "<table border=1>"
02396 " <tr>"
02397 " <th>Tick types</th>"
02398 " <th>Requested frequency</th>"
02399 " <th>Agreed frequency</th>"
02400 " <th>Measured frequency</th>"
02401 " <th>Tick duration (microsec)</th>"
02402 " <th>Tick duration (engine ticks)</th>"
02403 " <th>Stopped at tick</th>"
02404 " <th>Recovered count</th>"
02405 " <th>Recovered percentage</th>"
02406 " <th>Missed count</th>"
02407 " <th>Missed percentage</th>"
02408 " </tr>" ;
02409
02410
02411 Ceylan::Uint8 precision = 2 ;
02412
02413
02414 table +=
02415 " <tr>"
02416 " <td>Engine tick</td>"
02417 " <td>"
02418 + Ceylan::toString( 1E6 / DefaultEngineTickDuration, precision )
02419 + " Hz</td>"
02420 " <td>"
02421 + Ceylan::toString( 1E6 / DefaultEngineTickDuration, precision )
02422 + " Hz</td>"
02423 " <td>"
02424 + Ceylan::toString( _currentEngineTick * totalRuntimeFactor,
02425 precision )
02426 + " Hz</td>"
02427 " <td>" + Ceylan::toString( _engineTickDuration ) + "</td>"
02428 " <td>1</td>"
02429 " <td>" + Ceylan::toString( _currentEngineTick ) + "</td>"
02430 " <td>N/A</td>"
02431 " <td>N/A</td>"
02432 " <td>N/A</td>"
02433 " <td>N/A</td>"
02434 " </tr>" ;
02435
02436
02437
02438 table +=
02439 " <tr>"
02440 " <td>Simulation tick</td>"
02441 " <td>"
02442 + Ceylan::toString( _desiredSimulationFrequency )
02443 + " Hz</td>"
02444 " <td>"
02445 + Ceylan::toString( 1E6 / ( _simulationPeriod * _engineTickDuration ),
02446 precision ) + " Hz</td>"
02447 " <td>"
02448 + Ceylan::toString(
02449 ( _currentSimulationTick - _missedSimulationTicks ) *
02450 totalRuntimeFactor, precision ) + " Hz</td>"
02451 " <td>" + Ceylan::toString(
02452 _simulationPeriod * _engineTickDuration ) + "</td>"
02453 " <td>" + Ceylan::toString( _simulationPeriod ) + "</td>"
02454 " <td>" + Ceylan::toString( _currentSimulationTick ) + "</td>"
02455 " <td>" + Ceylan::toString( _recoveredSimulationTicks ) + "</td>"
02456 " <td>" + Ceylan::toString(
02457 100.0f * _recoveredSimulationTicks / _currentSimulationTick,
02458 precision )
02459 + "%</td>"
02460 " <td>" + Ceylan::toString( _missedSimulationTicks ) + "</td>"
02461 " <td>" + Ceylan::toString(
02462 100.0f * _missedSimulationTicks / _currentSimulationTick,
02463 precision )
02464 + "%</td>"
02465 " </tr>" ;
02466
02467
02468
02469 table +=
02470 " <tr>"
02471 " <td>Rendering tick</td>"
02472 " <td>"
02473 + Ceylan::toString( _desiredRenderingFrequency )
02474 + " Hz</td>"
02475 " <td>"
02476 + Ceylan::toString( 1E6 / ( _renderingPeriod * _engineTickDuration ),
02477 precision ) + " Hz</td>"
02478 " <td>"
02479 + Ceylan::toString(
02480 ( _currentRenderingTick - _missedRenderingTicks ) *
02481 totalRuntimeFactor, precision ) + " Hz</td>"
02482 " <td>" + Ceylan::toString(
02483 _renderingPeriod * _engineTickDuration ) + "</td>"
02484 " <td>" + Ceylan::toString( _renderingPeriod ) + "</td>"
02485 " <td>" + Ceylan::toString( _currentRenderingTick ) + "</td>"
02486 " <td>" + Ceylan::toString( _recoveredRenderingTicks ) + "</td>"
02487 " <td>" + Ceylan::toString(
02488 100.0f * _recoveredRenderingTicks / _currentRenderingTick,
02489 precision )
02490 + "%</td>"
02491 " <td>" + Ceylan::toString( _missedRenderingTicks ) + "</td>"
02492 " <td>" + Ceylan::toString(
02493 100.0f * _missedRenderingTicks / _currentRenderingTick, precision )
02494 + "%</td>"
02495 " </tr>" ;
02496
02497
02498 table +=
02499 " <tr>"
02500 " <td>Input polling tick</td>"
02501 " <td>"
02502 + Ceylan::toString( _desiredInputFrequency )
02503 + " Hz</td>"
02504 " <td>"
02505 + Ceylan::toString( 1E6 / ( _inputPeriod * _engineTickDuration ),
02506 precision ) + " Hz</td>"
02507 " <td>"
02508 + Ceylan::toString(
02509 ( _currentInputTick - _missedInputPollingTicks ) *
02510 totalRuntimeFactor, precision ) + " Hz</td>"
02511 " <td>" + Ceylan::toString(
02512 _inputPeriod * _engineTickDuration ) + "</td>"
02513 " <td>" + Ceylan::toString( _inputPeriod ) + "</td>"
02514 " <td>" + Ceylan::toString( _currentInputTick ) + "</td>"
02515 " <td>" + Ceylan::toString( _recoveredInputPollingTicks )
02516 + "</td>"
02517 " <td>" + Ceylan::toString(
02518 100.0f * _recoveredInputPollingTicks / _currentInputTick,
02519 precision )
02520 + "%</td>"
02521 " <td>" + Ceylan::toString( _missedInputPollingTicks ) + "</td>"
02522 " <td>" + Ceylan::toString(
02523 100.0f * _missedInputPollingTicks / _currentInputTick, precision )
02524 + "%</td>"
02525 " </tr>" ;
02526
02527 table += "</table>" ;
02528
02529 if ( Ceylan::TextDisplayable::GetOutputFormat() ==
02530 Ceylan::TextDisplayable::html )
02531 send( table ) ;
02532
02533 list<string> summary ;
02534
02535 string durationComment = "The scheduler ran for "
02536 + Ceylan::Timestamp::DescribeDuration(
02537 scheduleStoppingSecond - _scheduleStartingSecond ) ;
02538
02539
02540 if ( scheduleStoppingSecond - _scheduleStartingSecond <
02541 MaximumDurationWithMicrosecondAccuracy )
02542 durationComment += ", more precisely "
02543 + Ceylan::toString( getDurationBetween(
02544 _scheduleStartingSecond, _scheduleStartingMicrosecond,
02545 scheduleStoppingSecond, scheduleStoppingMicrosecond ) )
02546 + " microseconds." ;
02547 else
02548 durationComment += "." ;
02549
02550 summary.push_back( durationComment ) ;
02551
02552 summary.push_back( Ceylan::toString( _idleCallsCount )
02553 + " idle calls have been made." ) ;
02554
02555 summary.push_back( "Each idle call was expected to last for "
02556 + Ceylan::toString( _idleCallbackMaxDuration ) + " microseconds, i.e. "
02557 + Ceylan::toString( baseIdleCallbackTickCount ) + " engine ticks." ) ;
02558
02559 summary.push_back( "Average bucket level has been "
02560 + Ceylan::toString(
02561 ( 1.0f * delayCumulativeBucket ) / _currentEngineTick,
02562 2 )
02563 + ", maximum bucket level has been "
02564 + Ceylan::toString( maxDelayBucket )
02565 + ", bucket fill threshold is "
02566 + Ceylan::toString( bucketFillThreshold )
02567 + ", this threshold has been reached "
02568 + Ceylan::toString( _scheduleFailureCount )
02569 + " times, shutdown bucket level is "
02570 + Ceylan::toString( ShutdownBucketLevel) + "." ) ;
02571
02572 #if OSDL_DEBUG_SCHEDULER
02573
02574 send( "Displaying list of successive forecast idle callback durations: "
02575 + Ceylan::toString( forecastIdleCallbackDurationList ) ) ;
02576
02577 send( "Displaying list of successive actual idle callback durations: "
02578 + Ceylan::toString( actualIdleCallbackDurationList ) ) ;
02579
02580 const string logFilename = "idle-calls.dat" ;
02581
02582 Ceylan::System::File * idleComparisonFile = & File::Create( logFilename ) ;
02583
02584 idleComparisonFile->write(
02585 "# This file records the forecasted and measured durations\n"
02586 "# of idle calls.\n"
02587 "# One may use gnuplot to analyze the result,\n"
02588 "# see test/engine/testOSDLScheduler.cc.\n\n" ) ;
02589
02590 LogPlug::trace( "Recording forecasted and actual durations in the '"
02591 + logFilename + "' file." ) ;
02592
02593 Ceylan::Uint32 count = 0 ;
02594 Ceylan::Uint32 finalCount = forecastIdleCallbackDurationList.size() ;
02595
02596 while ( count != finalCount )
02597 {
02598
02599 idleComparisonFile->write( Ceylan::toString( count ) + " \t "
02600 + Ceylan::toString( forecastIdleCallbackDurationList.front() )
02601 + " \t "
02602 + Ceylan::toString( actualIdleCallbackDurationList.front() )
02603 + " \n" ) ;
02604
02605 forecastIdleCallbackDurationList.pop_front() ;
02606 actualIdleCallbackDurationList.pop_front() ;
02607
02608 count++ ;
02609
02610 }
02611
02612 idleComparisonFile->close() ;
02613 delete idleComparisonFile ;
02614
02615 #endif // OSDL_DEBUG_SCHEDULER
02616
02617
02618 send( "Scheduler stopping, run summary is: "
02619 + Ceylan::formatStringList( summary ) ) ;
02620
02621 send( "Full scheduler infos: " + toString( Ceylan::high ) ) ;
02622
02623 _scheduleStartingSecond = 0 ;
02624 _scheduleStartingMicrosecond = 0 ;
02625
02626
02627 #if OSDL_DEBUG_SCHEDULER
02628
02629
02630
02631
02632
02633
02634
02635 bool beVerbose = true ;
02636 bool beVeryVerbose = true ;
02637
02638
02639
02640
02641
02642 SimulationTick totalSimulationTicks = static_cast<SimulationTick>(
02643 metSimulations.size() + recoveredSimulations.size()
02644 + missedSimulations.size() ) ;
02645
02646 send( "Total simulation ticks: "
02647 + Ceylan::toString(
02648 static_cast<Ceylan::Uint32>( totalSimulationTicks ) )
02649 + "." ) ;
02650
02651 send( "Directly met simulation ticks: "
02652 + Ceylan::toString(
02653 static_cast<Ceylan::Uint32>( metSimulations.size() ) )
02654 + " (" + Ceylan::toString( 100.0f * metSimulations.size()
02655 / totalSimulationTicks, 2 ) + "%)." ) ;
02656
02657 send( "Recovered (indirectly met) simulation ticks: "
02658 + Ceylan::toString(
02659 static_cast<Ceylan::Uint32>( recoveredSimulations.size() ) )
02660 + " (" + Ceylan::toString( 100.0f * recoveredSimulations.size()
02661 / totalSimulationTicks, 2 ) + "%)." ) ;
02662
02663 if ( recoveredSimulations.size() != _recoveredSimulationTicks )
02664 LogPlug::error( "Inconsistency in recovered simulation count." ) ;
02665
02666
02667 send( "Missed simulation ticks: "
02668 + Ceylan::toString(
02669 static_cast<Ceylan::Uint32>( missedSimulations.size() ) )
02670 + " (" + Ceylan::toString( 100.0f * missedSimulations.size()
02671 / totalSimulationTicks, 2 ) + "%)." ) ;
02672
02673 if ( missedSimulations.size() != _missedSimulationTicks )
02674 LogPlug::error( "Inconsistency in missed simulation count." ) ;
02675
02676 if ( _currentSimulationTick - _recoveredSimulationTicks
02677 - _missedSimulationTicks != metSimulations.size() )
02678 LogPlug::error( "Inconsistency in overall simulation count." ) ;
02679
02680
02681 string res ;
02682
02683
02684 if ( beVerbose )
02685 {
02686
02687 if ( beVeryVerbose )
02688 {
02689
02690 for ( list<SimulationTick>::const_iterator it
02691 = metSimulations.begin(); it != metSimulations.end(); it++ )
02692 res += Ceylan::toString( *it ) + " - " ;
02693
02694 send( "Met simulation ticks: " + res ) ;
02695 res.clear() ;
02696
02697 }
02698
02699
02700 for ( list<SimulationTick>::const_iterator it
02701 = recoveredSimulations.begin();
02702 it != recoveredSimulations.end(); it++ )
02703 res += Ceylan::toString( *it ) + " - " ;
02704
02705 send( "Recovered simulation ticks: " + res ) ;
02706 res.clear() ;
02707
02708 for ( list<SimulationTick>::const_iterator it =
02709 missedSimulations.begin(); it != missedSimulations.end(); it++ )
02710 res += Ceylan::toString( *it ) + " - " ;
02711
02712 if ( res.empty() )
02713 send( "No simulation tick was missed." ) ;
02714 else
02715 send( "Missed simulation ticks: " + res ) ;
02716
02717 res.clear() ;
02718
02719 }
02720
02721
02722
02723
02724
02725
02726
02727 SimulationTick newSimulationTick ;
02728
02729 bool * simulationTicks = new bool[ _currentSimulationTick ] ;
02730
02731 for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
02732 simulationTicks[i] = false ;
02733
02734 for ( list<SimulationTick>::const_iterator it = metSimulations.begin();
02735 it != metSimulations.end(); it++ )
02736 {
02737
02738 newSimulationTick = (*it) ;
02739
02740 if ( simulationTicks[ newSimulationTick ] == false )
02741 simulationTicks[ newSimulationTick ] = true ;
02742 else
02743 LogPlug::error( "Met simulation tick #"
02744 + Ceylan::toString( newSimulationTick )
02745 + " should not have been scheduled more than once." ) ;
02746
02747 }
02748
02749
02750 for ( list<SimulationTick>::const_iterator it =
02751 recoveredSimulations.begin(); it != recoveredSimulations.end(); it++ )
02752 {
02753
02754 newSimulationTick = (*it) ;
02755
02756 if ( simulationTicks[ newSimulationTick ] == false )
02757 simulationTicks[ newSimulationTick ] = true ;
02758 else
02759 LogPlug::error( "Recovered simulation tick #"
02760 + Ceylan::toString( newSimulationTick )
02761 + " should not have been scheduled more than once." ) ;
02762
02763 }
02764
02765
02766 for ( list<SimulationTick>::const_iterator it = missedSimulations.begin();
02767 it != missedSimulations.end(); it++ )
02768 {
02769
02770 newSimulationTick = (*it) ;
02771
02772 if ( simulationTicks[ newSimulationTick ] == false )
02773 simulationTicks[ newSimulationTick ] = true ;
02774 else
02775 LogPlug::error( "Missed simulation tick #"
02776 + Ceylan::toString( newSimulationTick )
02777 + " should not have been skipped, "
02778 "since had already been taken into account." ) ;
02779
02780 }
02781
02782 for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
02783 if ( simulationTicks[ i ] == false )
02784 LogPlug::error( "Simulation tick #" + Ceylan::toString( i )
02785 + " has never been scheduled." ) ;
02786
02787 delete simulationTicks ;
02788
02789
02790
02791
02792
02793 RenderingTick totalRenderingTicks = static_cast<RenderingTick>(
02794 metRenderings.size() + recoveredRenderings.size()
02795 + missedRenderings.size() ) ;
02796
02797 send( "Total rendering ticks: "
02798 + Ceylan::toString( totalRenderingTicks ) + "." ) ;
02799
02800 send( "Directly met rendering ticks: "
02801 + Ceylan::toString(
02802 static_cast<Ceylan::Uint32>( metRenderings.size() ) )
02803 + " (" + Ceylan::toString( 100.0f * metRenderings.size()
02804 / totalRenderingTicks, 2 ) + "%)." ) ;
02805
02806 send( "Recovered (indirectly met) rendering ticks: "
02807 + Ceylan::toString(
02808 static_cast<Ceylan::Uint32>( recoveredRenderings.size() ) )
02809 + " (" + Ceylan::toString( 100.0f * recoveredRenderings.size()
02810 / totalRenderingTicks, 2 ) + "%)." ) ;
02811
02812 if ( recoveredRenderings.size() != _recoveredRenderingTicks )
02813 LogPlug::error( "Inconsistency in recovered rendering count." ) ;
02814
02815
02816 send( "Missed rendering ticks: "
02817 + Ceylan::toString(
02818 static_cast<Ceylan::Uint32>( missedRenderings.size() ) )
02819 + " (" + Ceylan::toString( 100.0f * missedRenderings.size()
02820 / totalRenderingTicks, 2 ) + "%)." ) ;
02821
02822 if ( missedRenderings.size() != _missedRenderingTicks )
02823 LogPlug::error( "Inconsistency in missed rendering count." ) ;
02824
02825 if ( _currentRenderingTick - _recoveredRenderingTicks
02826 - _missedRenderingTicks != metRenderings.size() )
02827 LogPlug::error( "Inconsistency in overall rendering count." ) ;
02828
02829
02830
02831 if ( beVerbose )
02832 {
02833
02834 if ( beVeryVerbose )
02835 {
02836
02837 for ( list<RenderingTick>::const_iterator it
02838 = metRenderings.begin(); it != metRenderings.end(); it++ )
02839 res += Ceylan::toString( *it ) + " - " ;
02840
02841 send( "Met rendering ticks: " + res ) ;
02842 res.clear() ;
02843
02844 }
02845
02846 for ( list<RenderingTick>::const_iterator it
02847 = recoveredRenderings.begin();
02848 it != recoveredRenderings.end(); it++ )
02849 res += Ceylan::toString( *it ) + " - " ;
02850
02851 send( "Recovered rendering ticks: " + res ) ;
02852 res.clear() ;
02853
02854 for ( list<RenderingTick>::const_iterator it
02855 = missedRenderings.begin(); it != missedRenderings.end(); it++ )
02856 res += Ceylan::toString( *it ) + " - " ;
02857
02858 if ( res.empty() )
02859 send( "No rendering tick was missed." ) ;
02860 else
02861 send( "Missed rendering ticks: " + res ) ;
02862
02863 res.clear() ;
02864
02865 }
02866
02867
02868
02869
02870
02871
02872
02873
02874 RenderingTick newRenderingTick ;
02875
02876 bool * renderingTicks = new bool[ _currentRenderingTick ] ;
02877
02878 for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
02879 renderingTicks[i] = false ;
02880
02881 for ( list<RenderingTick>::const_iterator it = metRenderings.begin();
02882 it != metRenderings.end(); it++ )
02883 {
02884
02885 newRenderingTick = (*it) ;
02886 if ( renderingTicks[ newRenderingTick ] == false )
02887 renderingTicks[ newRenderingTick ] = true ;
02888 else
02889 LogPlug::error( "Met rendering tick #"
02890 + Ceylan::toString( newRenderingTick )
02891 + " should not have been scheduled more than once." ) ;
02892
02893 }
02894
02895 for ( list<RenderingTick>::const_iterator it =
02896 recoveredRenderings.begin(); it != recoveredRenderings.end(); it++ )
02897 {
02898
02899 newRenderingTick = (*it) ;
02900 if ( renderingTicks[ newRenderingTick ] == false )
02901 renderingTicks[ newRenderingTick ] = true ;
02902 else
02903 LogPlug::error( "Recovered rendering tick #"
02904 + Ceylan::toString( newRenderingTick )
02905 + " should not have been scheduled more than once." ) ;
02906
02907 }
02908
02909
02910 for ( list<RenderingTick>::const_iterator it = missedRenderings.begin();
02911 it != missedRenderings.end(); it++ )
02912 {
02913 newRenderingTick = (*it) ;
02914 if ( renderingTicks[ newRenderingTick ] == false )
02915 renderingTicks[ newRenderingTick ] = true ;
02916 else
02917 LogPlug::error( "Missed rendering tick #"
02918 + Ceylan::toString( newRenderingTick )
02919 + " should not have been been skipped, "
02920 "since had already been taken into account." ) ;
02921
02922 }
02923
02924 for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
02925 if ( renderingTicks[ i ] == false )
02926 LogPlug::error( "Rendering tick #" + Ceylan::toString( i )
02927 + " has never been scheduled." ) ;
02928
02929 delete renderingTicks ;
02930
02931
02932
02933
02934
02935
02936 InputTick totalInputTicks = static_cast<InputTick>(
02937 metInputPollings.size() + recoveredInputPollings.size()
02938 + missedInputPollings.size() ) ;
02939
02940 send( "Total input ticks: "
02941 + Ceylan::toString( totalInputTicks ) + "." ) ;
02942
02943 send( "Directly met input ticks: "
02944 + Ceylan::toString(
02945 static_cast<Ceylan::Uint32>( metInputPollings.size() ) )
02946 + " (" + Ceylan::toString( 100.0f * metInputPollings.size()
02947 / totalInputTicks, 2 ) + "%)." ) ;
02948
02949 send( "Recovered (indirectly met) input ticks: "
02950 + Ceylan::toString(
02951 static_cast<Ceylan::Uint32>( recoveredInputPollings.size() ) )
02952 + " (" + Ceylan::toString( 100.0f * recoveredInputPollings.size()
02953 / totalInputTicks, 2 ) + "%)." ) ;
02954
02955 if ( recoveredInputPollings.size() != _recoveredInputPollingTicks )
02956 LogPlug::error( "Inconsistency in recovered input polling count." ) ;
02957
02958
02959 send( "Missed input ticks: "
02960 + Ceylan::toString(
02961 static_cast<Ceylan::Uint32>( missedInputPollings.size() ) )
02962 + " (" + Ceylan::toString( 100.0f * missedInputPollings.size()
02963 / totalInputTicks, 2 ) + "%)." ) ;
02964
02965 if ( missedInputPollings.size() != _missedInputPollingTicks )
02966 LogPlug::error( "Inconsistency in missed input polling count." ) ;
02967
02968 if ( _currentInputTick - _recoveredInputPollingTicks
02969 - _missedInputPollingTicks != metInputPollings.size() )
02970 LogPlug::error( "Inconsistency in overall input polling count." ) ;
02971
02972
02973 if ( beVerbose )
02974 {
02975
02976 if ( beVeryVerbose )
02977 {
02978
02979 for ( list<InputTick>::const_iterator it
02980 = metInputPollings.begin();
02981 it != metInputPollings.end(); it++ )
02982 res += Ceylan::toString( *it ) + " - " ;
02983
02984 send( "Met input ticks: " + res ) ;
02985 res.clear() ;
02986
02987 }
02988
02989 for ( list<InputTick>::const_iterator it
02990 = recoveredInputPollings.begin();
02991 it != recoveredInputPollings.end(); it++ )
02992 res += Ceylan::toString( *it ) + " - " ;
02993
02994 send( "Recovered input ticks: " + res ) ;
02995 res.clear() ;
02996
02997 for ( list<InputTick>::const_iterator it
02998 = missedInputPollings.begin();
02999 it != missedInputPollings.end(); it++ )
03000 res += Ceylan::toString( *it ) + " - " ;
03001
03002 if ( res.empty() )
03003 send( "No input tick was missed." ) ;
03004 else
03005 send( "Missed input ticks: " + res ) ;
03006
03007 res.clear() ;
03008
03009 }
03010
03011
03012
03013
03014
03015
03016
03017
03018
03019 InputTick newInputTick ;
03020
03021 bool * inputTicks = new bool[ _currentInputTick ] ;
03022
03023 for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
03024 inputTicks[i] = false ;
03025
03026 for ( list<InputTick>::const_iterator it = metInputPollings.begin();
03027 it != metInputPollings.end(); it++ )
03028 {
03029
03030 newInputTick = (*it) ;
03031 if ( inputTicks[ newInputTick ] == false )
03032 inputTicks[ newInputTick ] = true ;
03033 else
03034 LogPlug::error( "Met input tick #"
03035 + Ceylan::toString( newInputTick )
03036 + " should not have been scheduled more than once." ) ;
03037
03038 }
03039
03040 for ( list<InputTick>::const_iterator it = recoveredInputPollings.begin();
03041 it != recoveredInputPollings.end(); it++ )
03042 {
03043
03044 newInputTick = (*it) ;
03045 if ( inputTicks[ newInputTick ] == false )
03046 inputTicks[ newInputTick ] = true ;
03047 else
03048 LogPlug::error( "Recovered input tick #"
03049 + Ceylan::toString( newInputTick )
03050 + " should not have been scheduled more than once." ) ;
03051
03052 }
03053
03054
03055 for ( list<InputTick>::const_iterator it = missedInputPollings.begin();
03056 it != missedInputPollings.end(); it++ )
03057 {
03058 newInputTick = (*it) ;
03059 if ( inputTicks[ newInputTick ] == false )
03060 inputTicks[ newInputTick ] = true ;
03061 else
03062 LogPlug::error( "Missed input tick #"
03063 + Ceylan::toString( newInputTick )
03064 + " should not have been been skipped, "
03065 "since had already been taken into account." ) ;
03066
03067 }
03068
03069 for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
03070 if ( inputTicks[ i ] == false )
03071 LogPlug::error( "Input tick #" + Ceylan::toString( i )
03072 + " has never been scheduled." ) ;
03073
03074 delete inputTicks ;
03075
03076 #endif // OSDL_DEBUG_SCHEDULER
03077
03078 _isRunning = false ;
03079
03080 if ( _stopCallback != 0 )
03081 {
03082
03083
03084
03085
03086 (*_stopCallback)( _stopCallbackData ) ;
03087
03088 }
03089 else
03090 {
03091
03092
03093
03094
03095
03096
03097
03098 }
03099
03100 }
03101
03102
03103
03104
03105
03106
03107 void Scheduler::scheduleNoDeadline( bool pollInputs )
03108 {
03109
03110
03111
03112 _isRunning= true ;
03113 _stopRequested = false ;
03114
03115 #if OSDL_DEBUG_SCHEDULER
03116
03117
03118
03119
03120
03121
03122
03123
03124 list<SimulationTick> metSimulations ;
03125 list<RenderingTick> metRenderings ;
03126 list<InputTick> metInputPollings ;
03127
03128 #endif // OSDL_DEBUG_SCHEDULER
03129
03130
03131
03132 getPreciseTime( _scheduleStartingSecond, _scheduleStartingMicrosecond ) ;
03133
03134
03135 _currentEngineTick = 0 ;
03136
03137 _currentSimulationTick = 0 ;
03138 _currentRenderingTick = 0 ;
03139 _currentInputTick = 0 ;
03140
03141
03142
03143
03144
03145
03146
03147
03148 setInitialBirthTicks( _currentSimulationTick ) ;
03149
03150 OSDL_SCHEDULE_LOG( "Simulation period: "
03151 + Ceylan::toString( _simulationPeriod ) + " engine ticks" ) ;
03152
03153 OSDL_SCHEDULE_LOG( "Rendering period: "
03154 + Ceylan::toString( _renderingPeriod ) + " engine ticks" ) ;
03155
03156 OSDL_SCHEDULE_LOG( "Input period: "
03157 + Ceylan::toString( _inputPeriod ) + " engine ticks" ) ;
03158
03159 _currentSimulationTick = _currentEngineTick / _simulationPeriod ;
03160 _currentRenderingTick = _currentEngineTick / _renderingPeriod;
03161 _currentInputTick = _currentEngineTick / _inputPeriod;
03162
03163
03164 OSDL_SCHEDULE_LOG( "Initial engine tick: "
03165 + Ceylan::toString( _currentEngineTick ) ) ;
03166
03167 OSDL_SCHEDULE_LOG( "Initial simulation tick: "
03168 + Ceylan::toString( _currentSimulationTick ) ) ;
03169
03170 OSDL_SCHEDULE_LOG( "Initial rendering tick: "
03171 + Ceylan::toString( _currentRenderingTick ) ) ;
03172
03173 OSDL_SCHEDULE_LOG( "Initial input tick: "
03174 + Ceylan::toString( _currentInputTick ) ) ;
03175
03176
03177
03178
03179
03180 EngineTick nextSimulationDeadline = _currentEngineTick + _simulationPeriod ;
03181 EngineTick nextRenderingDeadline = _currentEngineTick + _renderingPeriod ;
03182 EngineTick nextInputDeadline = _currentEngineTick + _inputPeriod ;
03183
03184 EngineTick countBeforeSleep = 0 ;
03185
03186
03187
03188
03189
03190 while ( ! _stopRequested )
03191 {
03192
03193 OSDL_SCHEDULE_LOG( "[ E: " + Ceylan::toString( _currentEngineTick )
03194 + " ; S: " + Ceylan::toString( _currentSimulationTick )
03195 + " ; R: " + Ceylan::toString( _currentRenderingTick )
03196 + " ; I: " + Ceylan::toString( _currentInputTick ) + " ]" ) ;
03197
03198 countBeforeSleep++ ;
03199
03200
03201
03202
03203
03204
03205
03206
03207
03208
03209
03210
03211
03212
03213
03214
03215 if ( _currentEngineTick == nextSimulationDeadline )
03216 {
03217
03218
03219
03220 #if OSDL_DEBUG_SCHEDULER
03221 metSimulations.push_back( _currentSimulationTick ) ;
03222 #endif // OSDL_DEBUG_SCHEDULER
03223
03224 scheduleSimulation( _currentSimulationTick ) ;
03225
03226 _currentSimulationTick++ ;
03227 nextSimulationDeadline += _simulationPeriod ;
03228
03229 }
03230
03231 if ( _currentEngineTick == nextRenderingDeadline )
03232 {
03233
03234
03235
03236 #if OSDL_DEBUG_SCHEDULER
03237 metRenderings.push_back( _currentRenderingTick ) ;
03238 #endif // OSDL_DEBUG_SCHEDULER
03239
03240 scheduleRendering( _currentRenderingTick ) ;
03241
03242 _currentRenderingTick++ ;
03243 nextRenderingDeadline += _renderingPeriod ;
03244 }
03245
03246 if ( pollInputs && _currentEngineTick == nextInputDeadline )
03247 {
03248
03249
03250
03251 #if OSDL_DEBUG_SCHEDULER
03252 metInputPollings.push_back( _currentInputTick ) ;
03253 #endif // OSDL_DEBUG_SCHEDULER
03254
03255 scheduleInput( _currentInputTick ) ;
03256
03257 _currentInputTick++ ;
03258 nextInputDeadline += _inputPeriod ;
03259 }
03260
03261 _currentEngineTick++ ;
03262
03263
03264 if ( _subSecondSleepsAvailable && countBeforeSleep == 500 )
03265 {
03266 countBeforeSleep = 0 ;
03267 atomicSleep() ;
03268 }
03269
03270
03271
03272
03273
03274
03275
03276 }
03277
03278 Second scheduleStoppingSecond ;
03279 Microsecond scheduleStoppingMicrosecond ;
03280
03281 getPreciseTime( scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
03282
03283
03284
03285 Ceylan::Float64 totalRuntimeFactor ;
03286
03287
03288 if ( scheduleStoppingSecond - _scheduleStartingSecond <
03289 MaximumDurationWithMicrosecondAccuracy )
03290 {
03291
03292 totalRuntimeFactor = 1E6 / getDurationBetween(
03293 _scheduleStartingSecond, _scheduleStartingMicrosecond,
03294 scheduleStoppingSecond, scheduleStoppingMicrosecond ) ;
03295
03296 }
03297 else
03298 {
03299
03300
03301 totalRuntimeFactor = 1.0f /
03302 ( scheduleStoppingSecond - _scheduleStartingSecond ) ;
03303
03304 }
03305
03306
03307 send( "Scheduler stopping. Scheduler infos: "
03308 + toString( Ceylan::high ) ) ;
03309
03310
03311 ostringstream buf ;
03312
03313 buf.precision( 4 ) ;
03314
03315 buf << "Actual average engine frequency was "
03316 << Ceylan::toString( totalRuntimeFactor * _currentEngineTick,
03317 2 )
03318 << " Hz, average simulation frequency was "
03319 << Ceylan::toString( totalRuntimeFactor * _currentSimulationTick,
03320 2 )
03321 << " Hz, average rendering frequency was "
03322 << Ceylan::toString( totalRuntimeFactor * _currentRenderingTick,
03323 2 )
03324 << " Hz, average input frequency was "
03325 << Ceylan::toString( totalRuntimeFactor * _currentInputTick,
03326 2 )
03327 << " Hz." ;
03328
03329 send( buf.str() ) ;
03330
03331 #if OSDL_DEBUG_SCHEDULER
03332
03333
03334
03335
03336
03337
03338
03339
03340 send( "Total simulation ticks: "
03341 + Ceylan::toString(
03342 static_cast<Ceylan::Uint32>( metSimulations.size() ) )
03343 + "." ) ;
03344
03345
03346
03347
03348
03349
03350 SimulationTick newSimulationTick ;
03351
03352 bool * simulationTicks = new bool[ _currentSimulationTick ] ;
03353
03354 for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
03355 simulationTicks[i] = false ;
03356
03357 for ( list<SimulationTick>::const_iterator it = metSimulations.begin();
03358 it != metSimulations.end(); it++ )
03359 {
03360
03361 newSimulationTick = (*it) ;
03362 if ( simulationTicks[ newSimulationTick ] == false )
03363 simulationTicks[ newSimulationTick ] = true ;
03364 else
03365 LogPlug::error( "Simulation tick #"
03366 + Ceylan::toString( newSimulationTick )
03367 + " should not have been scheduled more than once." ) ;
03368
03369 }
03370
03371 for ( Events::SimulationTick i = 0; i < _currentSimulationTick; i++ )
03372 if ( simulationTicks[ i ] == false )
03373 LogPlug::error( "Simulation tick #" + Ceylan::toString( i )
03374 + " has never been scheduled." ) ;
03375
03376 delete simulationTicks ;
03377
03378
03379
03380
03381 send( "Total rendering ticks: "
03382 + Ceylan::toString(
03383 static_cast<Ceylan::Uint32>( metRenderings.size() ) ) + "." ) ;
03384
03385
03386
03387
03388
03389
03390 RenderingTick newRenderingTick ;
03391
03392 bool * renderingTicks = new bool[ _currentRenderingTick ] ;
03393
03394 for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
03395 renderingTicks[i] = false ;
03396
03397 for ( list<RenderingTick>::const_iterator it = metRenderings.begin();
03398 it != metRenderings.end(); it++ )
03399 {
03400
03401 newRenderingTick = (*it) ;
03402 if ( renderingTicks[ newRenderingTick ] == false )
03403 renderingTicks[ newRenderingTick ] = true ;
03404 else
03405 LogPlug::error( "Rendering tick #"
03406 + Ceylan::toString( newRenderingTick )
03407 + " should not have been scheduled more than once." ) ;
03408
03409 }
03410
03411 for ( Events::RenderingTick i = 0; i < _currentRenderingTick; i++ )
03412 if ( renderingTicks[ i ] == false )
03413 LogPlug::error( "Rendering tick #" + Ceylan::toString( i )
03414 + " has never been scheduled." ) ;
03415
03416 delete renderingTicks ;
03417
03418
03419
03420 send( "Total input polling ticks: "
03421 + Ceylan::toString(
03422 static_cast<Ceylan::Uint32>( metInputPollings.size() ) )
03423 + "." ) ;
03424
03425
03426
03427
03428
03429 if ( pollInputs )
03430 {
03431
03432 InputTick newInputTick ;
03433
03434 bool * inputTicks = new bool[ _currentInputTick ] ;
03435
03436 for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
03437 inputTicks[i] = false ;
03438
03439 for ( list<InputTick>::const_iterator it = metInputPollings.begin();
03440 it != metInputPollings.end(); it++ )
03441 {
03442
03443 newInputTick = (*it) ;
03444 if ( inputTicks[ newInputTick ] == false )
03445 inputTicks[ newInputTick ] = true ;
03446 else
03447 LogPlug::error( "Input tick #"
03448 + Ceylan::toString( newInputTick )
03449 + " should not have been scheduled more than once." ) ;
03450
03451 }
03452
03453 for ( Events::InputTick i = 0; i < _currentInputTick; i++ )
03454 if ( inputTicks[ i ] == false )
03455 LogPlug::error( "Input tick #" + Ceylan::toString( i )
03456 + " has never been scheduled." ) ;
03457
03458 delete inputTicks ;
03459
03460 }
03461
03462 #endif // OSDL_DEBUG_SCHEDULER
03463
03464 _isRunning = false ;
03465
03466 if ( _stopCallback != 0 )
03467 {
03468
03469
03470 (*_stopCallback)( _stopCallbackData ) ;
03471
03472 }
03473
03474 }
03475
03476
03477
03478 EngineTick Scheduler::computeEngineTickFromCurrentTime()
03479 {
03480
03481 Second currentSecond ;
03482 Microsecond currentMicrosecond ;
03483
03484 getPreciseTime( currentSecond, currentMicrosecond ) ;
03485
03486
03487
03488
03489
03490
03491
03492
03493
03494
03495
03496
03497
03498
03499
03500
03501
03502
03503
03504 if ( currentMicrosecond < _scheduleStartingMicrosecond )
03505 {
03506
03507
03508 #if OSDL_DEBUG_SCHEDULER
03509 if ( currentSecond == 0 )
03510 {
03511
03512 LogPlug::fatal( "Scheduler::computeEngineTickFromCurrentTime: "
03513 "abnormal clock skew." ) ;
03514
03515 _stopRequested = true ;
03516
03517 }
03518 #endif // OSDL_DEBUG_SCHEDULER
03519
03520 currentSecond-- ;
03521 currentMicrosecond += 1000000 ;
03522
03523 }
03524
03525 return static_cast<EngineTick>(
03526 ( currentSecond - _scheduleStartingSecond ) * _secondToEngineTick
03527 + ( currentMicrosecond - _scheduleStartingMicrosecond )
03528 / _engineTickDuration ) ;
03529
03530 }
03531
03532
03533
03534 void Scheduler::scheduleSimulation( SimulationTick current )
03535 {
03536
03537
03538
03539 OSDL_SCHEDULE_LOG( "--- simulating! " ) ;
03540
03541
03542 scheduleProgrammedObjects( current ) ;
03543
03544
03545 schedulePeriodicObjects( current ) ;
03546
03547 }
03548
03549
03550
03551 void Scheduler::scheduleProgrammedObjects(
03552 SimulationTick currentSimulationTick )
03553 {
03554
03555 map<SimulationTick, ListOfProgrammedActiveObjects>::iterator it
03556 = _programmedActivated.find( currentSimulationTick ) ;
03557
03558 if ( it != _programmedActivated.end() )
03559 {
03560
03561 scheduleProgrammedObjectList( currentSimulationTick, (*it).second ) ;
03562
03563
03564 _programmedActivated.erase( it ) ;
03565
03566 }
03567
03568
03569
03570 }
03571
03572
03573
03574 void Scheduler::schedulePeriodicObjects( SimulationTick current )
03575 {
03576
03577
03578
03579
03580
03581
03582
03583
03584 list<PeriodicSlot*>::iterator it = _periodicSlots.begin() ;
03585
03586 while ( it != _periodicSlots.end() )
03587 {
03588
03589
03590
03591
03592
03593
03594 if ( ! (*it)->onNextTick( current ) )
03595 {
03596
03597
03598
03599 send( " Scheduler::schedulePeriodicObjects for tick "
03600 + Ceylan::toString( current) + ": removing slot "
03601 + (*it)->toString() ) ;
03602
03603 delete *it ;
03604 it = _periodicSlots.erase( it ) ;
03605
03606 }
03607 else
03608 {
03609 it++ ;
03610
03611 }
03612 }
03613
03614
03615
03616
03617
03618
03619
03620 }
03621
03622
03623
03624 void Scheduler::scheduleRendering( RenderingTick current )
03625 {
03626
03627 OSDL_SCHEDULE_LOG( "--- rendering!" ) ;
03628
03629 if ( _renderer != 0 )
03630 _renderer->render( current ) ;
03631 else
03632 {
03633
03634 #if OSDL_DEBUG
03635 if ( _videoModule == 0 )
03636 Ceylan::emergencyShutdown( "Scheduler::scheduleRendering: "
03637 "no video module available." ) ;
03638 #endif // OSDL_DEBUG
03639 _videoModule->redraw() ;
03640
03641 }
03642
03643 }
03644
03645
03646
03647 void Scheduler::scheduleInput( InputTick current )
03648 {
03649
03650 OSDL_SCHEDULE_LOG( "--- input polling!" ) ;
03651
03652 #if OSDL_DEBUG
03653 if ( _eventsModule == 0 )
03654 Ceylan::emergencyShutdown( "Scheduler::scheduleRendering: "
03655 "no events module available." ) ;
03656 #endif // OSDL_DEBUG
03657
03658 _eventsModule->updateInputState() ;
03659
03660 }
03661
03662
03663
03664 void Scheduler::scheduleProgrammedObjectList(
03665 Events::RenderingTick currentSimulationTick,
03666 ListOfProgrammedActiveObjects & objectList )
03667 {
03668
03669 for ( ListOfProgrammedActiveObjects::iterator it = objectList.begin();
03670 it != objectList.end(); it++ )
03671 {
03672
03673
03674 if ( *it != 0 )
03675 (*it)->onActivation( currentSimulationTick ) ;
03676 }
03677
03678 }
03679
03680
03681
03682 void Scheduler::onSimulationSkipped( SimulationTick skipped )
03683 {
03684
03685 _missedSimulationTicks++ ;
03686
03687
03688
03689
03690
03691
03692
03693
03694
03695
03696
03697
03698
03699
03700 for ( list<PeriodicSlot*>::iterator it = _periodicSlots.begin();
03701 it != _periodicSlots.end(); it++ )
03702 {
03703 (*it)->onSimulationSkipped( skipped ) ;
03704 }
03705
03706
03707
03708 map<SimulationTick, ListOfProgrammedActiveObjects>::iterator it
03709 = _programmedActivated.find( skipped ) ;
03710
03711 if ( it != _programmedActivated.end() )
03712 {
03713
03714 for ( ListOfProgrammedActiveObjects::iterator itObjects =
03715 (*it).second.begin() ;
03716 itObjects != (*it).second.end(); itObjects++ )
03717 {
03718
03719
03720
03721
03722
03723 if ( *itObjects != 0 )
03724 (*itObjects)->onSkip( skipped ) ;
03725
03726 }
03727
03728 }
03729
03730 #if OSDL_DEBUG_SCHEDULER
03731 LogPlug::warning( "Simulation tick " + Ceylan::toString( skipped )
03732 + " had to be skipped." ) ;
03733 #endif // OSDL_DEBUG_SCHEDULER
03734
03735 }
03736
03737
03738
03739 void Scheduler::onRenderingSkipped( RenderingTick skipped )
03740 {
03741
03742 _missedRenderingTicks++ ;
03743
03744 if ( _renderer != 0 )
03745 _renderer->onRenderingSkipped( skipped ) ;
03746
03747 #if OSDL_DEBUG_SCHEDULER
03748 LogPlug::warning( "Rendering tick " + Ceylan::toString( skipped )
03749 + " had to be skipped." ) ;
03750 #endif // OSDL_DEBUG_SCHEDULER
03751
03752 }
03753
03754
03755
03756 void Scheduler::onInputSkipped( InputTick skipped )
03757 {
03758
03759 _missedInputPollingTicks++ ;
03760
03761
03762
03763 }
03764
03765
03766
03767 void Scheduler::onIdle()
03768 {
03769
03770 OSDL_SCHEDULE_LOG( "--- idle callback called!" ) ;
03771
03772 _idleCallsCount++ ;
03773
03774 if ( _idleCallback != 0 )
03775 {
03776
03777
03778 (*_idleCallback)( _idleCallbackData ) ;
03779
03780 }
03781 else
03782 {
03783
03784
03785
03786
03787
03788
03789 if ( _subSecondSleepsAvailable )
03790 {
03791
03792 Ceylan::System::atomicSleep() ;
03793
03794 }
03795 else
03796 {
03797
03798
03799 if ( _idleCallsCount % 100 == 1 )
03800 LogPlug::warning(
03801 "No idle call performed, no atomic call available." ) ;
03802
03803 }
03804 }
03805
03806 }
03807
03808
03809
03810 void Scheduler::onScheduleFailure( Delay currentBucket )
03811 {
03812
03813 _scheduleFailureCount++ ;
03814
03815
03816
03817 if ( currentBucket < ShutdownBucketLevel )
03818 {
03819
03820 string message = "Non-fatal schedule failure for engine tick #"
03821 + Ceylan::toString( _currentEngineTick )
03822 + ", delay bucket reached level "
03823 + Ceylan::toString( currentBucket )
03824 + ", this computer does not seem able to satisfy "
03825 "the requested load." ;
03826
03827
03828 OSDL_SCHEDULE_LOG( "!!!!! " + message ) ;
03829
03830 LogPlug::error( message) ;
03831
03832 }
03833 else
03834 {
03835
03836 OSDL_SCHEDULE_LOG( "[ E: " + Ceylan::toString( _currentEngineTick )
03837 + " ; S: " + Ceylan::toString( _currentSimulationTick )
03838 + " ; R: " + Ceylan::toString( _currentRenderingTick )
03839 + " ; I: " + Ceylan::toString( _currentInputTick )
03840 + " ; B: " + Ceylan::toString( currentBucket ) + " ]" ) ;
03841
03842 string message = "Fatal schedule failure for engine tick #"
03843 + Ceylan::toString( _currentEngineTick )
03844 + ", delay bucket reached level "
03845 + Ceylan::toString( currentBucket )
03846 + ", this computer cannot take in charge the requested load, "
03847 "stopping the scheduler now." ;
03848
03849
03850 OSDL_SCHEDULE_LOG( "XXXXX " + message ) ;
03851
03852 LogPlug::fatal( message) ;
03853
03854 stop() ;
03855
03856 throw SchedulingException( message ) ;
03857
03858 }
03859
03860 }
03861
03862
03863
03864 void Scheduler::programTriggerFor( ProgrammedActiveObject & objectToProgram,
03865 SimulationTick targetTick )
03866 {
03867
03868 map<SimulationTick, ListOfProgrammedActiveObjects>::iterator it
03869 = _programmedActivated.find( targetTick ) ;
03870
03871 if ( it != _programmedActivated.end() )
03872 {
03873
03874
03875 (*it).second.push_back( & objectToProgram ) ;
03876
03877 }
03878 else
03879 {
03880
03881
03882
03883 ListOfProgrammedActiveObjects newList ;
03884
03885 newList.push_back( & objectToProgram ) ;
03886 _programmedActivated[ targetTick ] = newList ;
03887
03888 }
03889
03890 }
03891
03892
03893
03894 PeriodicSlot & Scheduler::getPeriodicSlotFor( Events::Period period )
03895 {
03896
03897 for ( list<PeriodicSlot*>::iterator it = _periodicSlots.begin();
03898 it != _periodicSlots.end(); it++ )
03899 {
03900
03901 if ( (*it)->getPeriod() == period )
03902 return * (*it) ;
03903
03904 if ( (*it)->getPeriod() > period )
03905 throw SchedulingException( "Scheduler::getPeriodicSlotFor: "
03906 "no slot found for period " + Ceylan::toString( period ) ) ;
03907
03908 }
03909
03910
03911 throw SchedulingException( "Scheduler::getPeriodicSlotFor: "
03912 "no slot found for period " + Ceylan::toString( period ) ) ;
03913
03914 }
03915
03916
03917
03918 PeriodicSlot & Scheduler::returnPeriodicSlotFor( Events::Period period )
03919 {
03920
03921 for ( list<PeriodicSlot*>::iterator it = _periodicSlots.begin();
03922 it != _periodicSlots.end(); it++ )
03923 {
03924
03925 if ( (*it)->getPeriod() == period )
03926 return * (*it) ;
03927
03928 if ( (*it)->getPeriod() > period )
03929 {
03930
03931
03932
03933
03934
03935
03936 PeriodicSlot * newSlot = new PeriodicSlot( period ) ;
03937 _periodicSlots.insert( it, newSlot ) ;
03938 return * newSlot ;
03939
03940 }
03941
03942 }
03943
03944
03945
03946
03947
03948
03949 PeriodicSlot * newSlot = new PeriodicSlot( period ) ;
03950 _periodicSlots.push_back( newSlot ) ;
03951
03952 return * newSlot ;
03953
03954 }
03955
03956
03957
03958 void Scheduler::setInitialBirthTicks(
03959 Events::SimulationTick birthSimulationTick )
03960 {
03961
03962
03963 for ( list<ActiveObject *>::iterator it = _initialRegisteredObjects.begin();
03964 it != _initialRegisteredObjects.end(); it++ )
03965 (*it)->setBirthTick( birthSimulationTick ) ;
03966
03967 _initialRegisteredObjects.clear() ;
03968
03969 }
03970
03971
03972
03973 std::string Scheduler::describeProgrammedTicks() const
03974 {
03975
03976 Ceylan::Uint32 tickCount = static_cast<Ceylan::Uint32>(
03977 _programmedActivated.size() ) ;
03978
03979 if ( tickCount == 0 )
03980 return "There is no programmed tick" ;
03981 else
03982 return "There are " + Ceylan::toString(tickCount)
03983 + " programmed tick(s)" ;
03984
03985 }
03986