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 "CeylanSystem.h"
00028
00029
00030 #include "CeylanLogPlug.h"
00031 #include "CeylanOperators.h"
00032 #include "CeylanMathsBasic.h"
00033 #include "CeylanEnvironmentVariables.h"
00034 #include "CeylanFile.h"
00035
00036
00037 #ifdef CEYLAN_USES_CONFIG_H
00038 #include "CeylanConfig.h"
00039 #endif // CEYLAN_USES_CONFIG_H
00040
00041
00042
00043 #if CEYLAN_ARCH_NINTENDO_DS
00044
00045 #include "CeylanConfigForNintendoDS.h"
00046 #include "CeylanFIFO.h"
00047
00048 #endif // CEYLAN_ARCH_NINTENDO_DS
00049
00050
00051
00052 #if CEYLAN_ARCH_WINDOWS
00053 #include "CeylanNetwork.h"
00054 #endif // CEYLAN_ARCH_WINDOWS
00055
00056
00057
00058
00059 extern "C"
00060 {
00061
00062 #if CEYLAN_ARCH_NINTENDO_DS
00063
00064 #include "fat.h"
00065
00066 #include <fcntl.h>
00067 #include <unistd.h>
00068
00069 #endif // CEYLAN_ARCH_NINTENDO_DS
00070
00071
00072 #ifdef CEYLAN_USES_UNISTD_H
00073 #include <unistd.h>
00074 #endif // CEYLAN_USES_UNISTD_H
00075
00076 #ifdef CEYLAN_USES_STRING_H
00077 #include <string.h>
00078 #endif // CEYLAN_USES_STRING_H
00079
00080 #ifdef CEYLAN_USES_SYS_TIME_H
00081 #include <sys/time.h>
00082 #endif // CEYLAN_USES_SYS_TYME_H
00083
00084
00085 #ifdef CEYLAN_USES_SYS_SELECT_H
00086 #include <sys/select.h>
00087 #endif // CEYLAN_USES_SYS_SELECT_H
00088
00089 #ifdef CEYLAN_USES_WINDOWS_H
00090 #include <windows.h>
00091 #endif // CEYLAN_USES_WINDOWS_H
00092
00093 #ifdef CEYLAN_USES_SYS_TYPES_H
00094 #include <sys/types.h>
00095 #endif // CEYLAN_USES_SYS_TYPES_H
00096
00097 #ifdef CEYLAN_USES_SYS_TIMEB_H
00098 #include <sys/timeb.h>
00099 #endif // CEYLAN_USES_SYS_TIMEB_H
00100
00101 #ifdef CEYLAN_USES_WINSOCK2_H
00102 #include <winsock2.h>
00103 #endif // CEYLAN_USES_WINSOCK2_H
00104
00105 }
00106
00107
00108 #define CEYLAN_CHECK_FOR_FREEZE 0
00109
00110 #if CEYLAN_CHECK_FOR_FREEZE
00111 #include <iostream>
00112 #endif // CEYLAN_CHECK_FOR_FREEZE
00113
00114
00115 #include <cstdlib>
00116 #include <cerrno>
00117 #include <iostream>
00118
00119
00120
00121
00122 using std::string ;
00123 using std::map ;
00124
00125
00126 using namespace Ceylan::Log ;
00127 using namespace Ceylan::System ;
00128
00129
00130
00131 #if CEYLAN_ARCH_NINTENDO_DS
00132
00134 extern const Size Ceylan::System::CacheLineSize = 32 ;
00135
00136
00138 map<Ceylan::Byte *,Ceylan::Byte *> Ceylan::System::CacheProtectedMap ;
00139
00140 #endif // CEYLAN_ARCH_NINTENDO_DS
00141
00142
00143
00144 extern const Ceylan::System::InterruptMask
00145 Ceylan::System::AllInterruptsDisabled = 0 ;
00146
00147
00148
00149 Ceylan::System::SystemException::SystemException( const string & message ) :
00150 Ceylan::Exception( message )
00151 {
00152
00153 }
00154
00155
00156
00157
00158 Ceylan::System::SystemException::~SystemException() throw()
00159 {
00160
00161 }
00162
00163
00164
00165 Ceylan::System::IOException::IOException( const string & message ) :
00166 SystemException( message )
00167 {
00168
00169 }
00170
00171
00172
00173
00174 Ceylan::System::IOException::~IOException() throw()
00175 {
00176
00177 }
00178
00179
00180
00181 const Second Ceylan::System::MaximumDurationWithMicrosecondAccuracy = 4100 ;
00182
00183
00184 const Ceylan::Uint32 OneMillion = 1000000 ;
00185
00186
00187
00188
00189 ErrorCode Ceylan::System::getError()
00190 {
00191
00192 return errno ;
00193
00194 }
00195
00196
00197
00198
00199 string Ceylan::System::explainError( ErrorCode errorID )
00200 {
00201
00202 return string( ::strerror( errorID ) ) ;
00203
00204 }
00205
00206
00207
00208
00209 string Ceylan::System::explainError()
00210 {
00211
00212 #ifdef CEYLAN_USES_STRERROR
00213
00214 return string( ::strerror( errno ) ) ;
00215
00216 #else // CEYLAN_USES_STRERROR
00217
00218 return "Ceylan::System::explainError not available on this platform "
00219 "(no ::strerror function found)" ;
00220
00221 #endif // CEYLAN_USES_STRERROR
00222
00223 }
00224
00225
00226
00227 string Ceylan::System::getShellName()
00228 {
00229
00230 return Ceylan::System::getEnvironmentVariable( "SHELL" ) ;
00231
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244 void Ceylan::System::InitializeInterrupts( bool force )
00245 {
00246
00247 #if CEYLAN_ARCH_NINTENDO_DS
00248
00249
00250 #ifdef CEYLAN_RUNS_ON_ARM7
00251
00252 throw SystemException(
00253 "Ceylan::System::InitializeInterrupts: only available on the ARM9." ) ;
00254
00255 #elif defined(CEYLAN_RUNS_ON_ARM9)
00256
00257 static bool initialized = false ;
00258
00259 if ( ( ! initialized ) || force )
00260 {
00261
00262
00263 irqInit() ;
00264
00265
00266
00267
00268
00269
00270 irqEnable( IRQ_VBLANK ) ;
00271
00272
00273 swiWaitForVBlank() ;
00274
00275 initialized = true ;
00276
00277
00278
00279 }
00280
00281 #endif // CEYLAN_RUNS_ON_ARM7
00282
00283
00284 #else // CEYLAN_ARCH_NINTENDO_DS
00285
00286 LogPlug::warning( "Ceylan::System::InitializeInterrupts "
00287 "should not be called on this platform." ) ;
00288
00289 #endif // CEYLAN_ARCH_NINTENDO_DS
00290
00291 }
00292
00293
00294
00295 InterruptMask Ceylan::System::SetEnabledInterrupts( InterruptMask newMask )
00296 {
00297
00298 #if CEYLAN_ARCH_NINTENDO_DS
00299
00300
00301
00302 InterruptMask previousMask = REG_IME ;
00303
00304 REG_IME = newMask ;
00305
00306 return previousMask ;
00307
00308 #else // CEYLAN_ARCH_NINTENDO_DS
00309
00310 LogPlug::warning( "Ceylan::System::SetEnabledInterrupts "
00311 "should not be called on this platform." ) ;
00312
00313
00314 return 0 ;
00315
00316 #endif // CEYLAN_ARCH_NINTENDO_DS
00317
00318 }
00319
00320
00321
00322 void Ceylan::System::InitializeIPC()
00323 {
00324
00325 #if CEYLAN_ARCH_NINTENDO_DS
00326
00327
00328 #ifdef CEYLAN_RUNS_ON_ARM7
00329
00330 throw SystemException(
00331 "Ceylan::System::InitializeIPC: only available on the ARM9." ) ;
00332
00333 #elif defined(CEYLAN_RUNS_ON_ARM9)
00334
00335
00336 FIFO * fifo = new FIFO() ;
00337
00338 fifo->activate() ;
00339
00340 #endif // CEYLAN_RUNS_ON_ARM7
00341
00342
00343 #else // CEYLAN_ARCH_NINTENDO_DS
00344
00345 LogPlug::warning( "Ceylan::System::InitializeIPC "
00346 "should not be called on this platform." ) ;
00347
00348 #endif // CEYLAN_ARCH_NINTENDO_DS
00349
00350 }
00351
00352
00353
00354 Ceylan::Byte * Ceylan::System::CacheProtectedNew( Size numberOfBytes )
00355 {
00356
00357 #if CEYLAN_ARCH_NINTENDO_DS
00358
00359 #ifdef CEYLAN_RUNS_ON_ARM9
00360
00361
00362
00363 static bool inUse = false ;
00364
00365 while ( inUse )
00366 ;
00367
00368 inUse = true ;
00369
00370 int savedIME = REG_IME ;
00371
00372 REG_IME = AllInterruptsDisabled ;
00373
00374
00375
00376
00377
00378
00379
00380
00381 Ceylan::Byte * biggerBuffer = new Ceylan::Byte[
00382 numberOfBytes + CacheLineSize - 1 ] ;
00383
00384 if ( biggerBuffer == 0 )
00385 {
00386
00387 REG_IME = savedIME ;
00388 inUse = false ;
00389
00390 throw SystemException( "Ceylan::System::CacheProtectedNew failed: "
00391 "null pointer returned, not enough memory?" ) ;
00392
00393 }
00394
00395
00396
00397
00398
00399
00400 Ceylan::Byte * boundaryAlignedBuffer = biggerBuffer
00401 + ( CacheLineSize - reinterpret_cast<Ceylan::Uint32>( biggerBuffer ) )
00402 % CacheLineSize ;
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420 Ceylan::System::CacheProtectedMap[ boundaryAlignedBuffer ] = biggerBuffer ;
00421
00422
00423 REG_IME = savedIME ;
00424
00425 inUse = false ;
00426
00427 return boundaryAlignedBuffer ;
00428
00429
00430 #else // CEYLAN_RUNS_ON_ARM9
00431
00432 throw SystemException( "Ceylan::System::CacheProtectedNew failed: "
00433 "not available on the ARM7." ) ;
00434
00435 #endif // CEYLAN_RUNS_ON_ARM9
00436
00437 #else // CEYLAN_ARCH_NINTENDO_DS
00438
00439 throw SystemException( "Ceylan::System::CacheProtectedNew failed: "
00440 "not available on this platform." ) ;
00441
00442 #endif // CEYLAN_ARCH_NINTENDO_DS
00443
00444 }
00445
00446
00447
00448 void Ceylan::System::CacheProtectedDelete( Ceylan::Byte * cacheProtectedBuffer )
00449 {
00450
00451 #if CEYLAN_ARCH_NINTENDO_DS
00452
00453 #ifdef CEYLAN_RUNS_ON_ARM9
00454
00455
00456 static bool inUse = false ;
00457
00458 while ( inUse )
00459 ;
00460
00461 inUse = true ;
00462
00463 int savedIME = REG_IME ;
00464
00465 REG_IME = AllInterruptsDisabled ;
00466
00467
00468
00469 map<Ceylan::Byte *,Ceylan::Byte *>::iterator it =
00470 CacheProtectedMap.find( cacheProtectedBuffer ) ;
00471
00472 if ( it == CacheProtectedMap.end() )
00473 {
00474
00475 REG_IME = savedIME ;
00476 inUse = false ;
00477
00478 throw SystemException( "Ceylan::System::CacheProtectedDelete failed: "
00479 "pointer " + Ceylan::toString( cacheProtectedBuffer )
00480 + " not found." ) ;
00481
00482 }
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 delete [] (*it).second ;
00493
00494 REG_IME = savedIME ;
00495 inUse = false ;
00496
00497 #else // CEYLAN_RUNS_ON_ARM9
00498
00499 throw SystemException( "CacheProtectedDelete failed: "
00500 "not available on the ARM7." ) ;
00501
00502 #endif // CEYLAN_RUNS_ON_ARM9
00503
00504 #else // CEYLAN_ARCH_NINTENDO_DS
00505
00506 throw SystemException( "CacheProtectedDelete failed: "
00507 "not available on this platform." ) ;
00508
00509 #endif // CEYLAN_ARCH_NINTENDO_DS
00510
00511 }
00512
00513
00514
00515 bool Ceylan::System::HasAvailableData( FileDescriptor fd )
00516 {
00517
00518 #if CEYLAN_ARCH_NINTENDO_DS
00519
00520 LogPlug::error( "Ceylan::System::HasAvailableData: "
00521 "not supported on the Nintendo DS platform." ) ;
00522
00523 return false ;
00524
00525 #else // CEYLAN_ARCH_NINTENDO_DS
00526
00527 #if CEYLAN_USES_FILE_DESCRIPTORS
00528
00529 struct timeval tv ;
00530 tv.tv_sec = 0 ;
00531 tv.tv_usec = 0 ;
00532
00533
00534 fd_set set ;
00535 FD_ZERO( & set ) ;
00536 FD_SET( fd, & set ) ;
00537
00538 Ceylan::Sint32 n = ::select( fd + 1, & set, 0, 0, & tv ) ;
00539
00540 if ( n > 0 )
00541 return FD_ISSET( fd, & set ) ;
00542
00543 if ( n == -1 )
00544 LogPlug::error( "Ceylan::System::HasAvailableData failed: "
00545 + System::explainError() ) ;
00546
00547 return false ;
00548
00549 #else // CEYLAN_USES_FILE_DESCRIPTORS
00550
00551 LogPlug::error( "Ceylan::System::HasAvailableData: "
00552 "file descriptor feature not available" ) ;
00553
00554 return false ;
00555
00556 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00557
00558 #endif // CEYLAN_ARCH_NINTENDO_DS
00559
00560 }
00561
00562
00563
00564 Size Ceylan::System::FDRead( FileDescriptor fd, char * dataBuffer,
00565 Size toReadBytesNumber )
00566 {
00567
00568 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00569 LogPlug::trace( "Ceylan::System::FDRead: will try to read "
00570 + Ceylan::toString(
00571 static_cast<Ceylan::Uint32>( toReadBytesNumber ) ) + " byte(s)." ) ;
00572 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00573
00574
00575 SignedSize totalReadBytesNumber = 0 ;
00576
00577 #if CEYLAN_ARCH_WINDOWS
00578
00579
00580
00581 #if CEYLAN_USES_NETWORK
00582
00583 char * pos = dataBuffer ;
00584
00585 SignedSize readBytesNumber ;
00586
00587
00588 while ( toReadBytesNumber &&
00589 ( readBytesNumber = ::recv( fd, pos,
00590 static_cast<int>( toReadBytesNumber ),
00591 0 ) ) != 0 )
00592 {
00593
00594 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00595 LogPlug::trace( "Ceylan::System::FDRead: recv returned "
00596 + Ceylan::toString( readBytesNumber ) + "." ) ;
00597 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00598
00599
00600 if ( readBytesNumber == SOCKET_ERROR )
00601 {
00602
00603
00604 if ( Network::getSocketError() == WSAEWOULDBLOCK )
00605 {
00606
00607 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00608 LogPlug::trace( "Ceylan::System::FDRead: "
00609 "operation would block." ) ;
00610 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00611
00612 readBytesNumber = 0 ;
00613 break ;
00614 }
00615 else
00616 {
00617
00618 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00619 LogPlug::trace( "Ceylan::System::FDRead: operation failed." ) ;
00620 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00621
00622 throw IOException( "Ceylan::System::FDRead failed: "
00623 + Network::explainSocketError() ) ;
00624 }
00625
00626 }
00627
00628 totalReadBytesNumber += readBytesNumber ;
00629 toReadBytesNumber -= readBytesNumber ;
00630 pos += readBytesNumber ;
00631
00632 }
00633
00634
00635
00636
00637 #else // CEYLAN_USES_NETWORK
00638
00639 throw Features::FeatureNotAvailableException( "Ceylan::System::FDRead: "
00640 "network feature not available" ) ;
00641
00642 #endif // CEYLAN_USES_NETWORK
00643
00644 #else // CEYLAN_ARCH_WINDOWS
00645
00646 #if CEYLAN_USES_FILE_DESCRIPTORS
00647
00648
00649 char * pos = dataBuffer ;
00650
00651 SignedSize readBytesNumber ;
00652
00653 while ( toReadBytesNumber &&
00654 ( readBytesNumber = ::read( fd, pos, toReadBytesNumber ) ) != 0 )
00655 {
00656
00657 if ( readBytesNumber < 0 )
00658 {
00659
00660
00661 if ( System::getError() == EAGAIN )
00662 {
00663 readBytesNumber = 0 ;
00664 break ;
00665 }
00666 else
00667 {
00668 throw IOException( "Ceylan::System::FDRead failed: "
00669 + explainError() ) ;
00670 }
00671
00672 }
00673
00674 totalReadBytesNumber += readBytesNumber ;
00675 toReadBytesNumber -= readBytesNumber ;
00676 pos += readBytesNumber ;
00677
00678 }
00679
00680
00681
00682
00683 #else // CEYLAN_USES_FILE_DESCRIPTORS
00684
00685 throw Features::FeatureNotAvailableException( "Ceylan::System::FDRead: "
00686 "file descriptor feature not available" ) ;
00687
00688 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00689
00690 #endif // CEYLAN_ARCH_WINDOWS
00691
00692
00693 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00694 LogPlug::trace( "Ceylan::System::FDRead: read "
00695 + Ceylan::toString( totalReadBytesNumber ) + " byte(s)." ) ;
00696 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00697
00698 return static_cast<Size>( totalReadBytesNumber ) ;
00699
00700 }
00701
00702
00703
00704 Size Ceylan::System::FDWrite( FileDescriptor fd,
00705 const Ceylan::Byte * dataBuffer, Size toWriteBytesNumber )
00706 {
00707
00708 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00709 LogPlug::trace( "Ceylan::System::FDWrite: will try to write "
00710 + Ceylan::toString(
00711 static_cast<Ceylan::Uint32>( toWriteBytesNumber ) )
00712 + " byte(s)." ) ;
00713 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00714
00715
00716 SignedSize totalWroteBytesNumber = 0 ;
00717
00718 #if CEYLAN_ARCH_WINDOWS
00719
00720
00721
00722 #if CEYLAN_USES_NETWORK
00723
00724 const char * pos = dataBuffer ;
00725
00726 SignedSize wroteBytesNumber ;
00727
00728 while( toWriteBytesNumber
00729 && ( wroteBytesNumber = ::send( fd, pos,
00730 static_cast<int>( toWriteBytesNumber ),
00731 0 ) ) != 0 )
00732 {
00733
00734 if ( wroteBytesNumber < 0 )
00735 {
00736
00737
00738
00739
00740
00741
00742 if ( Network::getSocketError() == WSAEWOULDBLOCK )
00743 {
00744 wroteBytesNumber = 0 ;
00745 break ;
00746
00747 }
00748 else
00749 {
00750
00751 throw IOException( "Ceylan::System::FDWrite failed: "
00752 + Network::explainSocketError() ) ;
00753 }
00754 }
00755
00756 totalWroteBytesNumber += wroteBytesNumber ;
00757 toWriteBytesNumber -= wroteBytesNumber ;
00758 pos += wroteBytesNumber ;
00759
00760 }
00761
00762 #else // CEYLAN_USES_NETWORK
00763
00764 throw Features::FeatureNotAvailableException( "Ceylan::System::FDWrite: "
00765 "network feature not available" ) ;
00766
00767 #endif // CEYLAN_USES_NETWORK
00768
00769 #else // CEYLAN_ARCH_WINDOWS
00770
00771 #if CEYLAN_USES_FILE_DESCRIPTORS
00772
00773
00774 const char * pos = dataBuffer ;
00775
00776 SignedSize wroteBytesNumber ;
00777
00778 while( toWriteBytesNumber
00779 && ( wroteBytesNumber = ::write( fd, pos, toWriteBytesNumber ) ) != 0 )
00780 {
00781
00782 if ( wroteBytesNumber < 0 )
00783 {
00784
00785
00786 if ( System::getError() == EAGAIN )
00787 {
00788 wroteBytesNumber = 0 ;
00789 break ;
00790
00791 }
00792 else
00793 {
00794
00795 throw IOException( "Ceylan::System::FDWrite failed: "
00796 + explainError() ) ;
00797 }
00798 }
00799
00800 totalWroteBytesNumber += wroteBytesNumber ;
00801 toWriteBytesNumber -= wroteBytesNumber ;
00802 pos += wroteBytesNumber ;
00803
00804 }
00805
00806
00807 #else // CEYLAN_USES_FILE_DESCRIPTORS
00808
00809 throw Features::FeatureNotAvailableException( "Ceylan::System::FDWrite: "
00810 "file descriptor feature not available" ) ;
00811
00812 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00813
00814 #endif // CEYLAN_ARCH_WINDOWS
00815
00816 #if CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00817 LogPlug::trace( "Ceylan::System::FDWrite: wrote "
00818 + Ceylan::toString( totalWroteBytesNumber ) + " byte(s)." ) ;
00819 #endif // CEYLAN_DEBUG_LOW_LEVEL_STREAMS
00820
00821 return static_cast<Size>( totalWroteBytesNumber ) ;
00822
00823 }
00824
00825
00826
00827
00828
00829
00830
00831
00832 Second Ceylan::System::getTime()
00833 {
00834
00835 #ifdef CEYLAN_USES_TIME
00836
00837 Ceylan::Sint32 currentTime = static_cast<Ceylan::Sint32>(
00838 ::time( 0 ) ) ;
00839
00840 if ( currentTime == -1 )
00841 throw SystemException( explainError( getError() ) ) ;
00842
00843
00844
00845
00846
00847
00848
00849 return static_cast<Second>( currentTime ) ;
00850
00851 #else // CEYLAN_USES_TIME
00852
00853 throw SystemException( "Ceylan::System::getTime: "
00854 "not available on this platform (no ::time function found)." ) ;
00855
00856 #endif // CEYLAN_USES_TIME
00857
00858 }
00859
00860
00861
00862 string Ceylan::System::timeToString( const time_t & t )
00863 {
00864
00865 #ifdef CEYLAN_USES_CTIME
00866
00867 char * charTime = ::ctime( &t ) ;
00868
00869 if ( charTime == 0 )
00870 throw SystemException( "Ceylan::System::timeToString: "
00871 "unable to convert time to string with ctime." ) ;
00872
00873 string stringTime = charTime ;
00874 string eol = "\n" ;
00875 stringTime.erase( stringTime.size() - eol.size(), eol.size() ) ;
00876 return stringTime ;
00877
00878 #else // CEYLAN_USES_CTIME
00879
00880 throw SystemException( "Ceylan::System::timeToString: "
00881 "not available on this platform (no ::ctime function found)." ) ;
00882
00883 #endif // CEYLAN_USES_CTIME
00884
00885 }
00886
00887
00888
00889 string Ceylan::System::durationToString(
00890 Second startingSecond, Microsecond startingMicrosecond,
00891 Second stoppingSecond, Microsecond stoppingMicrosecond )
00892 {
00893
00894
00895
00896
00897
00898 Second s1 = startingSecond + startingMicrosecond / OneMillion ;
00899 Second s2 = stoppingSecond + stoppingMicrosecond / OneMillion ;
00900
00901 Microsecond r1 = startingMicrosecond % OneMillion ;
00902 Microsecond r2 = stoppingMicrosecond % OneMillion ;
00903
00904
00905
00906 if ( s1 > s2 )
00907 throw SystemException( "Ceylan::System::durationToString: "
00908 "specified duration is negative." ) ;
00909
00910 if ( s1 == s2 )
00911 {
00912 if ( r1 > r2 )
00913 throw SystemException( "Ceylan::System::durationToString: "
00914 "specified duration should not be negative." ) ;
00915 }
00916
00917
00918
00919
00920 if ( r2 >= r1 )
00921 {
00922 return Ceylan::toString( s2 - s1 ) + " second(s) and "
00923 + Ceylan::toString( r2 - r1 ) + " microsecond(s)" ;
00924 }
00925 else
00926 {
00927 return Ceylan::toString( s2 - s1 - 1 ) + " second(s) and "
00928 + Ceylan::toString( OneMillion + r2 - r1 )
00929 + " microsecond(s)" ;
00930 }
00931
00932 }
00933
00934
00935
00936 Microsecond Ceylan::System::getDurationBetween(
00937 Second startingSecond, Microsecond startingMicrosecond,
00938 Second stoppingSecond, Microsecond stoppingMicrosecond )
00939 {
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950 Second s1 = startingSecond + startingMicrosecond / OneMillion ;
00951 Second s2 = stoppingSecond + stoppingMicrosecond / OneMillion ;
00952
00953 Microsecond r1 = startingMicrosecond % OneMillion ;
00954 Microsecond r2 = stoppingMicrosecond % OneMillion ;
00955
00956
00957
00958 if ( s1 > s2 )
00959 throw SystemException( "Ceylan::System::getDurationBetween: "
00960 "specified duration is negative." ) ;
00961
00962 if ( s1 == s2 )
00963 {
00964 if ( r1 > r2 )
00965 throw SystemException( "Ceylan::System::getDurationBetween: "
00966 "specified duration should not be negative." ) ;
00967 }
00968
00969
00970
00971
00972 if ( ( s2 - s1 ) > MaximumDurationWithMicrosecondAccuracy )
00973 throw SystemException( "Ceylan::System::getDurationBetween: "
00974 "specified duration should not exceed "
00975 + Ceylan::toString( MaximumDurationWithMicrosecondAccuracy )
00976 + " seconds." ) ;
00977
00978 return ( ( s2 - s1 ) * OneMillion + r2 -r1 ) ;
00979
00980 }
00981
00982
00983
00984 void Ceylan::System::getPreciseTime( Second & seconds, Microsecond & microsec )
00985 {
00986
00987 #ifdef CEYLAN_USES_GETTIMEOFDAY
00988
00989 timeval currentTime ;
00990
00991 if ( ::gettimeofday( & currentTime,
00992 0 ) != 0 )
00993 throw SystemException( "System::getPreciseTime: "
00994 + Ceylan::System::explainError() ) ;
00995
00996 seconds = static_cast<Second>( currentTime.tv_sec ) ;
00997 microsec = static_cast<Microsecond>( currentTime.tv_usec ) ;
00998
00999 #else // CEYLAN_USES_GETTIMEOFDAY
01000
01001
01002 #ifdef CEYLAN_USES__FTIME_S
01003
01004 struct _timeb timeBuffer ;
01005
01006 if( ::_ftime_s( & timeBuffer ) != 0 )
01007 throw SystemException( "System::getPreciseTime: "
01008 "_ftime_s failed." ) ;
01009
01010 seconds = static_cast<Second>( timeBuffer.time ) ;
01011 microsec = static_cast<Microsecond>( timeBuffer.millitm * 1000 ) ;
01012
01013
01014 #else // CEYLAN_USES__FTIME_S
01015
01016 throw SystemException( "System::getPreciseTime: "
01017 "not available on this platform." ) ;
01018
01019 #endif // CEYLAN_USES__FTIME_S
01020
01021 #endif // CEYLAN_USES_GETTIMEOFDAY
01022
01023 }
01024
01025
01026
01027 Microsecond Ceylan::System::getAccuracyOfPreciseTime( Microsecond * minGap,
01028 Microsecond * maxGap )
01029 {
01030
01031 Ceylan::Uint32 numberOfMeasures = 100 ;
01032
01033
01034
01035
01036
01037
01038 Microsecond minDuration = 4000000000U ;
01039 Microsecond maxDuration = 0 ;
01040
01041 Microsecond currentDuration ;
01042 Microsecond cumulativeDuration = 0 ;
01043
01044 Microsecond lastSecond ;
01045 Microsecond lastMicrosecond ;
01046
01047 Microsecond currentSecond ;
01048 Microsecond currentMicrosecond ;
01049
01050 #if CEYLAN_DEBUG_SYSTEM
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060 Microsecond * durations = new Microsecond[numberOfMeasures] ;
01061
01062 #endif // CEYLAN_DEBUG_SYSTEM
01063
01064 getPreciseTime( lastSecond, lastMicrosecond ) ;
01065
01066 for ( Ceylan::Uint32 i = 0; i < numberOfMeasures; i++ )
01067 {
01068
01069 getPreciseTime( currentSecond, currentMicrosecond ) ;
01070
01071 currentDuration = getDurationBetween( lastSecond, lastMicrosecond,
01072 currentSecond, currentMicrosecond ) ;
01073
01074 if ( currentDuration < minDuration )
01075 minDuration = currentDuration ;
01076 else if ( currentDuration > maxDuration )
01077 maxDuration = currentDuration ;
01078
01079 cumulativeDuration += currentDuration ;
01080
01081 #if CEYLAN_DEBUG_SYSTEM
01082 durations[i] = currentDuration ;
01083 #endif // CEYLAN_DEBUG_SYSTEM
01084
01085 lastSecond = currentSecond ;
01086 lastMicrosecond = currentMicrosecond ;
01087
01088 }
01089
01090 if ( minGap != 0 )
01091 *minGap = minDuration ;
01092
01093 if ( maxGap != 0 )
01094 *maxGap = maxDuration ;
01095
01096 #if CEYLAN_DEBUG_SYSTEM
01097
01098 for ( Ceylan::Uint32 i = 0; i < numberOfMeasures; i++ )
01099 Log::LogPlug::debug( "Duration of getPreciseTime call: "
01100 + Ceylan::toString( durations[i] ) + " microseconds." ) ;
01101
01102 Log::LogPlug::debug( "Real average duration: "
01103 + Ceylan::toString(
01104 static_cast<Ceylan::Float32>(
01105 cumulativeDuration ) / numberOfMeasures ) ) ;
01106
01107 delete durations ;
01108
01109 #endif // CEYLAN_DEBUG_SYSTEM
01110
01111 Microsecond result = static_cast<Microsecond>(
01112 cumulativeDuration / numberOfMeasures ) ;
01113
01114
01115 if ( result == 0 )
01116 return 1 ;
01117 else
01118 return result ;
01119
01120 }
01121
01122
01123
01124 Microsecond Ceylan::System::getPreciseTimeCallDuration()
01125 {
01126
01127 static Microsecond duration = 0 ;
01128
01129
01130 if ( duration != 0 )
01131 return duration ;
01132
01133
01134 Ceylan::Uint16 calls = 1000 ;
01135
01136 Second lastSecond ;
01137 Microsecond lastMicrosecond ;
01138
01139 Second currentSecond ;
01140 Microsecond currentMicrosecond ;
01141
01142 getPreciseTime( lastSecond, lastMicrosecond ) ;
01143
01144
01145 for ( Ceylan::Uint16 i = 0; i < calls ; i++ )
01146 getPreciseTime( currentSecond, currentMicrosecond ) ;
01147
01148 Microsecond totalTime = getDurationBetween( lastSecond, lastMicrosecond,
01149 currentSecond, currentMicrosecond ) ;
01150
01151
01152 duration = totalTime / calls ;
01153
01154 if ( duration == 0 )
01155 duration = 1 ;
01156
01157 return duration ;
01158
01159 }
01160
01161
01162
01163 void Ceylan::System::sleepForSeconds( Second seconds )
01164 {
01165
01166 #if CEYLAN_ARCH_NINTENDO_DS
01167
01168 basicSleep( seconds, 0 ) ;
01169
01170 #else // CEYLAN_ARCH_NINTENDO_DS
01171
01172 #ifdef CEYLAN_USES_SLEEP
01173
01174 Second stillToBeSlept = seconds ;
01175
01176 while ( stillToBeSlept != 0 )
01177 {
01178
01179
01180
01181
01182
01183
01184 stillToBeSlept = ::sleep( stillToBeSlept ) ;
01185
01186 }
01187
01188 #else // CEYLAN_USES_SLEEP
01189
01190 #ifdef CEYLAN_USES_WINDOWS_H
01191
01192
01193 ::Sleep( 1000 * seconds ) ;
01194
01195 #else // CEYLAN_USES_WINDOWS_H
01196
01197 throw SystemException( "Ceylan::sleepForSeconds: "
01198 "not available on this platform (no ::sleep function found)" ) ;
01199
01200 #endif // CEYLAN_USES_WINDOWS_H
01201
01202 #endif // CEYLAN_USES_SLEEP
01203
01204 #endif // CEYLAN_ARCH_NINTENDO_DS
01205
01206 }
01207
01208
01209
01210 bool Ceylan::System::areSubSecondSleepsAvailable()
01211 {
01212
01213 #if CEYLAN_ARCH_NINTENDO_DS
01214
01215
01216 return true ;
01217
01218 #elif CEYLAN_ARCH_WINDOWS == 1
01219
01220 return true ;
01221
01222 #else // CEYLAN_ARCH_WINDOWS
01223
01224
01225 #if CEYLAN_USES_FILE_DESCRIPTORS
01226 return true ;
01227 #else // CEYLAN_USES_FILE_DESCRIPTORS
01228 return false ;
01229 #endif // CEYLAN_USES_FILE_DESCRIPTORS
01230
01231
01232 #endif // CEYLAN_ARCH_NINTENDO_DS
01233
01234 }
01235
01236
01237
01238 void Ceylan::System::basicSleep( Second seconds, Nanosecond nanos )
01239 {
01240
01241 #if CEYLAN_DEBUG_SYSTEM
01242 LogPlug::debug( "Ceylan::System::basicSleep: requested duration is "
01243 + Ceylan::toString( seconds ) + " second(s) and "
01244 + Ceylan::toString( nanos ) + " nanosecond(s)." ) ;
01245 #endif // CEYLAN_DEBUG_SYSTEM
01246
01247
01248 #if CEYLAN_ARCH_NINTENDO_DS
01249
01250 #ifdef CEYLAN_RUNS_ON_ARM9
01251 InitializeInterrupts( false ) ;
01252 #endif // CEYLAN_RUNS_ON_ARM9
01253
01254
01255 Ceylan::Uint32 vblankCount = seconds * 60 + nanos * (6/100000000) ;
01256
01257 while ( vblankCount > 0 )
01258 {
01259 swiWaitForVBlank();
01260 vblankCount-- ;
01261 }
01262
01263 #else // CEYLAN_ARCH_NINTENDO_DS
01264
01265
01266 #if CEYLAN_ARCH_WINDOWS
01267
01268
01269 ::Sleep( seconds * 1000 + nanos / OneMillion ) ;
01270
01271 #else // CEYLAN_ARCH_WINDOWS
01272
01273
01274 #define CEYLAN_USES_NANOSLEEP 0
01275
01276 #if CEYLAN_USES_NANOSLEEP
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291 timespec waiter ;
01292
01293 waiter.tv_sec = static_cast<Second>( seconds ) ;
01294 waiter.tv_nsec = static_cast<long>( nanos ) ;
01295
01296 if ( ::nanosleep( & waiter, 0 ) != 0 )
01297 throw SystemException( "System::basicSleep with nanosleep failed: "
01298 + Ceylan::System::explainError() ) ;
01299
01300 #else // CEYLAN_USES_NANOSLEEP
01301
01302
01303 #if CEYLAN_USES_FILE_DESCRIPTORS
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313 timeval timeout ;
01314 timeout.tv_sec = seconds ;
01315
01316
01317 timeout.tv_usec = static_cast<long>( nanos / 1000 ) ;
01318
01319
01320
01321 if ( ::select( 0, static_cast<fd_set *>( 0 ),
01322 static_cast<fd_set *>( 0 ), static_cast<fd_set *>( 0 ),
01323 & timeout ) < 0 )
01324 throw SystemException( "System::basicSleep with select failed: "
01325 + Ceylan::System::explainError() ) ;
01326
01327 #else // CEYLAN_USES_FILE_DESCRIPTORS
01328
01329 throw SystemException( "System::basicSleep: "
01330 "file descriptor feature not available" ) ;
01331
01332 #endif // CEYLAN_USES_FILE_DESCRIPTORS
01333
01334
01335 #endif // CEYLAN_USES_NANOSLEEP
01336
01337 #endif // CEYLAN_ARCH_WINDOWS
01338
01339 #endif // CEYLAN_ARCH_NINTENDO_DS
01340
01341 #if CEYLAN_DEBUG_SYSTEM
01342 LogPlug::debug( "Ceylan::System::basicSleep: awoken now." ) ;
01343 #endif // CEYLAN_DEBUG_SYSTEM
01344
01345 }
01346
01347
01348
01349 void Ceylan::System::atomicSleep()
01350 {
01351
01352 #if CEYLAN_ARCH_NINTENDO_DS
01353
01354 #ifdef CEYLAN_RUNS_ON_ARM9
01355 InitializeInterrupts( false ) ;
01356 #endif // CEYLAN_RUNS_ON_ARM9
01357
01358
01359
01360
01361
01362
01363
01364 swiWaitForVBlank() ;
01365
01366 #else // CEYLAN_ARCH_NINTENDO_DS
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379 const Ceylan::Float32 marginDecreaseFactor = 0.75f ;
01380
01381 #if CEYLAN_DEBUG_SYSTEM
01382
01383 LogPlug::debug( "Ceylan::System::atomicSleep triggered." ) ;
01384
01385 #endif // CEYLAN_DEBUG_SYSTEM
01386
01387 static Microsecond durationToRequest = static_cast<Microsecond>(
01388 marginDecreaseFactor * getSchedulingGranularity() ) ;
01389
01390 basicSleep( durationToRequest ) ;
01391
01392 #endif // CEYLAN_ARCH_NINTENDO_DS
01393
01394
01395 }
01396
01397
01398
01399 void Ceylan::System::basicSleep( Microsecond micros )
01400 {
01401
01402
01403 basicSleep( micros / OneMillion,
01404 ( micros % OneMillion ) * 1000 ) ;
01405
01406 }
01407
01408
01409
01410 bool Ceylan::System::smartSleep( Second seconds, Microsecond micros )
01411 {
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426 Second targetSecond ;
01427 Microsecond targetMicrosecond ;
01428
01429 getPreciseTime( targetSecond, targetMicrosecond ) ;
01430
01431
01432 targetSecond += seconds ;
01433
01434
01435
01436
01437
01438
01439 targetMicrosecond += micros - getPreciseTimeCallDuration() - 1 ;
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454 const Ceylan::Float32 marginIncreaseFactor = 1.2f ;
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467 const Microsecond usedGranularity = static_cast<Microsecond>(
01468 getSchedulingGranularity() * marginIncreaseFactor ) ;
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479 Ceylan::Uint32 fullTimeSliceCount = ( seconds * OneMillion + micros )
01480 / usedGranularity ;
01481
01482 #if CEYLAN_DEBUG_SYSTEM
01483 LogPlug::debug( "Ceylan::System::smartSleep: will first use "
01484 + Ceylan::toString( fullTimeSliceCount )
01485 + " full time slice(s) for waiting." ) ;
01486 #endif // CEYLAN_DEBUG_SYSTEM
01487
01488
01489
01490
01491
01492
01493
01494
01495
01496 if ( fullTimeSliceCount != 0 )
01497 {
01498
01499 #if CEYLAN_DEBUG_SYSTEM
01500
01501 LogPlug::debug( "Ceylan::System::smartSleep: will ask basicSleep "
01502 + Ceylan::toString( fullTimeSliceCount
01503 * getSchedulingGranularity() )
01504 + " microseconds." ) ;
01505
01506 #endif // CEYLAN_DEBUG_SYSTEM
01507
01508 basicSleep( fullTimeSliceCount * getSchedulingGranularity() ) ;
01509
01510 }
01511
01512 Second currentSecond ;
01513 Microsecond currentMicrosecond ;
01514
01515 getPreciseTime( currentSecond, currentMicrosecond ) ;
01516
01517
01518
01519
01520
01521
01522 Ceylan::SignedLongInteger currentError
01523 = ( currentSecond - targetSecond ) * OneMillion
01524 + currentMicrosecond - targetMicrosecond ;
01525
01526 LogPlug::debug( "Ceylan::System::smartSleep: "
01527 "after having waited the big chunk, we are at "
01528 + Ceylan::toString( -currentError )
01529 + " microseconds before deadline." ) ;
01530
01531 #if CEYLAN_DEBUG_SYSTEM
01532 Ceylan::Uint32 atomicCount = 0 ;
01533 #endif // CEYLAN_DEBUG_SYSTEM
01534
01535
01536
01537 while ( -currentError > static_cast<int>( 2 * usedGranularity ) )
01538 {
01539
01540 #if CEYLAN_DEBUG_SYSTEM
01541 atomicCount++ ;
01542 #endif // CEYLAN_DEBUG_SYSTEM
01543
01544
01545
01546
01547
01548
01549 atomicSleep() ;
01550
01551 getPreciseTime( currentSecond, currentMicrosecond ) ;
01552
01553
01554 currentError = ( currentSecond - targetSecond ) * OneMillion
01555 + currentMicrosecond - targetMicrosecond ;
01556
01557 }
01558
01559 #if CEYLAN_DEBUG_SYSTEM
01560
01561 LogPlug::debug( "Ceylan::System::smartSleep: used "
01562 + Ceylan::toString( atomicCount )
01563 + " atomic waitings." ) ;
01564
01565 #endif // CEYLAN_DEBUG_SYSTEM
01566
01567
01568 if ( currentError > 0 )
01569 {
01570
01571 LogPlug::warning( "Ceylan::System::smartSleep: "
01572 "sleeps waited too much, target time missed of "
01573 + Ceylan::toString( currentError )
01574 + " microseconds, because of non-precomputed "
01575 "or underestimated time slices (their evaluated duration was "
01576 + Ceylan::toString( getSchedulingGranularity() )
01577 + " microseconds)." ) ;
01578 return false ;
01579
01580 }
01581
01582
01583
01584
01585 #if CEYLAN_DEBUG_SYSTEM
01586
01587 Microsecond remaining = static_cast<Microsecond>( -currentError ) ;
01588
01589 LogPlug::debug( "Ceylan::System::smartSleep: remaining time ("
01590 + Ceylan::toString( remaining ) + " microseconds)"
01591 + " will be spent in active waiting." ) ;
01592
01593 #endif // CEYLAN_DEBUG_SYSTEM
01594
01595
01596 bool done = false ;
01597
01598
01599
01600
01601
01602
01603
01604
01605 #if CEYLAN_CHECK_FOR_FREEZE
01606 std::cerr << std::endl << "active waiting started -> " ;
01607 #endif // CEYLAN_CHECK_FOR_FREEZE
01608
01609 Ceylan::Sint32 preciseTimeDuration =
01610 static_cast<Sint32>( getPreciseTimeCallDuration() ) ;
01611
01612 #if CEYLAN_DEBUG_SYSTEM
01613
01614 Ceylan::Uint32 logCount = 0 ;
01615
01616 #endif // CEYLAN_DEBUG_SYSTEM
01617
01618
01619 Ceylan::Sint32 remainingTime ;
01620
01621 while( ! done )
01622 {
01623
01624 getPreciseTime( currentSecond, currentMicrosecond ) ;
01625 remainingTime =
01626 static_cast<Sint32>( targetSecond - currentSecond ) * OneMillion
01627 + targetMicrosecond - currentMicrosecond ;
01628
01629 #if CEYLAN_DEBUG_SYSTEM
01630
01631 logCount++ ;
01632
01633 if ( ( logCount % 50 ) == 0 )
01634 LogPlug::debug( "Ceylan::System::smartSleep: "
01635 "remaining time in active waiting is "
01636 + Ceylan::toString( remainingTime ) + " microseconds." ) ;
01637
01638
01639 #endif // CEYLAN_DEBUG_SYSTEM
01640
01641
01642 #if CEYLAN_ARCH_WINDOWS
01643
01644
01645
01646
01647
01648
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680 if ( remaining < 1.5 * usedGranularity )
01681 {
01682
01683 #if CEYLAN_DEBUG_SYSTEM
01684 LogPlug::debug( "Ceylan::System::smartSleep: remaining time ("
01685 + Ceylan::toString( remaining ) + " microseconds)"
01686 + " smaller than half of a time slice ("
01687 + Ceylan::toString( usedGranularity )
01688 + " microseconds) and we are on Windows, "
01689 "we prefer to finish earlier than later." ) ;
01690 #endif // CEYLAN_DEBUG_SYSTEM
01691
01692 return false ;
01693
01694 }
01695
01696 #endif // CEYLAN_ARCH_WINDOWS
01697
01698
01699
01700
01701
01702
01703 if ( remainingTime < preciseTimeDuration )
01704 done = true ;
01705
01706
01707
01708
01709
01710
01711
01712
01713 }
01714
01715 #if CEYLAN_CHECK_FOR_FREEZE
01716 std::cerr << "finished" ;
01717 #endif // CEYLAN_CHECK_FOR_FREEZE
01718
01719
01720
01721 return true ;
01722
01723 }
01724
01725
01726
01727 bool Ceylan::System::smartSleepUntil( Second second, Microsecond micro )
01728 {
01729
01730 Second currentSecond ;
01731 Microsecond currentMicrosecond ;
01732
01733 getPreciseTime( currentSecond, currentMicrosecond ) ;
01734
01735
01736
01737
01738
01739
01740 currentMicrosecond++ ;
01741
01742 if ( currentSecond > second || currentMicrosecond > micro )
01743 throw SystemException( "Ceylan::System::smartSleepUntil: "
01744 "specified date (" + Ceylan::toString( second )
01745 + " seconds and " + Ceylan::toString( micro )
01746 + " microseconds) is on the past (current date is "
01747 + Ceylan::toString( currentSecond ) + " seconds, "
01748 + Ceylan::toString( currentMicrosecond ) + " microseconds)." ) ;
01749
01750 if ( micro < currentMicrosecond )
01751 {
01752 second-- ;
01753 micro += OneMillion ;
01754 }
01755
01756 return smartSleep( second - currentSecond, micro - currentMicrosecond ) ;
01757
01758 }
01759
01760
01761
01762 Microsecond Ceylan::System::getActualDurationForSleep(
01763 Microsecond requestedMicroseconds, Second requestedSeconds )
01764 {
01765
01766
01767
01768
01769
01770
01771
01772 Second lastSecond ;
01773 Microsecond lastMicrosecond ;
01774
01775 Second currentSecond ;
01776 Microsecond currentMicrosecond ;
01777
01778 getPreciseTime( lastSecond, lastMicrosecond ) ;
01779
01780 Ceylan::System::basicSleep( requestedSeconds ,
01781 requestedMicroseconds * 1000 ) ;
01782
01783 getPreciseTime( currentSecond, currentMicrosecond ) ;
01784
01785
01786
01787
01788
01789
01790
01791
01792 return getDurationBetween( lastSecond, lastMicrosecond,
01793 currentSecond, currentMicrosecond) ;
01794
01795 }
01796
01797
01798
01799 Microsecond Ceylan::System::getSchedulingGranularity()
01800 {
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813 static Microsecond granularity = 0 ;
01814
01815
01816 if ( granularity != 0 )
01817 return granularity ;
01818
01819 #if CEYLAN_DEBUG_SYSTEM
01820
01821 bool logMeasures = true ;
01822
01823
01824
01825
01826
01827
01828
01829 const string logFilename = "granularity.dat" ;
01830
01831 File * logFile = 0 ;
01832
01833 if ( logMeasures )
01834 {
01835
01836 logFile = & File::Create( logFilename ) ;
01837 logFile->write(
01838 "# This file records the requested sleep durations (first column)\n"
01839 "# and the corresponding actual sleep durations (second column).\n"
01840 "# The scheduling granularity can be usually guessed from it.\n"
01841 "# One may use gnuplot to analyze the result,\n"
01842 "# see test/system/testCeylanTime.cc.\n\n" ) ;
01843
01844 LogPlug::trace( "Ceylan::System::getSchedulingGranularity: "
01845 "computing granularity now, and logging the result in the '"
01846 + logFilename + "' file." ) ;
01847
01848 }
01849 else
01850 {
01851
01852 LogPlug::trace( "Ceylan::System::getSchedulingGranularity: "
01853 "computing granularity now (no file logging requested)." ) ;
01854
01855 }
01856
01857 #endif // CEYLAN_DEBUG_SYSTEM
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870 Microsecond lastMeasuredDuration = 0 ;
01871 Microsecond currentMeasuredDuration = 0 ;
01872
01873
01874 const Microsecond durationStep = 250 ;
01875
01882 Microsecond maximumPossibleDuration = 110000 ;
01883
01884
01885
01886
01887
01888
01889
01890
01891
01892
01893 Microsecond currentRequestedDuration = 0 ;
01894
01895 while ( currentRequestedDuration < maximumPossibleDuration )
01896 {
01897
01898
01899 currentRequestedDuration += durationStep ;
01900
01901 lastMeasuredDuration = currentMeasuredDuration ;
01902
01903 currentMeasuredDuration =
01904 getActualDurationForSleep( currentRequestedDuration ) ;
01905
01906 #if CEYLAN_DEBUG_SYSTEM
01907 logFile->write( Ceylan::toString( currentRequestedDuration )
01908 + " \t " + Ceylan::toString( currentMeasuredDuration )
01909 + " \n" ) ;
01910 #endif // CEYLAN_DEBUG_SYSTEM
01911
01912
01913
01914 if ( currentMeasuredDuration == lastMeasuredDuration )
01915 {
01916 if ( currentMeasuredDuration == 0 )
01917 continue ;
01918 else
01919 break ;
01920 }
01921
01922
01923
01924 if ( Ceylan::Maths::Abs( static_cast<Ceylan::Float32>(
01925 currentMeasuredDuration - lastMeasuredDuration ) )
01926 / ( currentMeasuredDuration + lastMeasuredDuration ) < 0.005f )
01927 break ;
01928
01929 #if CEYLAN_DEBUG_SYSTEM
01930 LogPlug::debug( "Previous (" + Ceylan::toString( lastMeasuredDuration )
01931 + ") and current (" + Ceylan::toString( currentMeasuredDuration )
01932 + ") measured durations are not deemed relatively equal, "
01933 " continuing." ) ;
01934 #endif // CEYLAN_DEBUG_SYSTEM
01935
01936 }
01937
01938
01939
01940
01941
01942
01943
01944 Microsecond testDuration = 900 ;
01945
01946
01947
01948 if ( currentRequestedDuration == maximumPossibleDuration )
01949 {
01950
01951 currentMeasuredDuration = getActualDurationForSleep( testDuration ) ;
01952 LogPlug::warning( "Ceylan::System::getSchedulingGranularity: "
01953 "failed to guess actual time slice duration, starting with "
01954 + Ceylan::toString( currentMeasuredDuration )
01955 + " microseconds as an experimental basis." ) ;
01956
01957 }
01958 else
01959 {
01960
01961 #if CEYLAN_DEBUG_SYSTEM
01962
01963 LogPlug::debug( "Relative equality has been found between previous ("
01964 + Ceylan::toString( lastMeasuredDuration )
01965 + ") and current (" + Ceylan::toString( currentMeasuredDuration )
01966 + ") measured durations, research was stopped." ) ;
01967 #endif // CEYLAN_DEBUG_SYSTEM
01968
01969 }
01970
01971
01972
01973
01974
01975
01976
01977 const Ceylan::Uint8 sampleCount = 20 ;
01978
01979
01980 granularity = 0 ;
01981
01982 for ( Ceylan::Uint8 i = 0; i < sampleCount; i++ )
01983 {
01984
01985
01986
01987
01988
01989
01990
01991
01992 granularity += getActualDurationForSleep(
01993 static_cast<Microsecond>( 0.4 * currentMeasuredDuration ) ) ;
01994
01995 }
01996
01997 granularity = static_cast<Microsecond>(
01998 ( granularity * 1.0f ) / sampleCount ) ;
01999
02000
02001
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018 #if CEYLAN_DEBUG_SYSTEM
02019 LogPlug::debug( "Final returned scheduling granularity is "
02020 + Ceylan::toString( granularity ) + " microseconds." ) ;
02021
02022 if ( logFile != 0 )
02023 {
02024 logFile->close() ;
02025 delete logFile ;
02026 logFile = 0 ;
02027 }
02028 #endif // CEYLAN_DEBUG_SYSTEM
02029
02030 return granularity ;
02031
02032 }
02033
02034
02035
02036 bool Ceylan::System::setLegacyStreamSynchronization( bool synchronized )
02037 {
02038
02039
02040
02041
02042
02043
02044
02045
02046
02047
02048
02049 return std::ios::sync_with_stdio( synchronized ) ;
02050
02051 }
02052