From 160bd14bb0ac72bb6e2d3dfae65893279132235c Mon Sep 17 00:00:00 2001 From: janwas Date: Wed, 16 May 2007 15:56:47 +0000 Subject: [PATCH] robustify CPU freq detection and timer code, hopefully solve lockup issue cpu: cache CPU info to prevent calling freq measurement code several times config: mention e.g. fopen_s instead of only the secure string functions ia32: exception-safe scheduler setting; no longer use absolute max priority (risky, could hang machine if loop contains a bug) wtime: add note on TSC safety, slight improvements timer: try and prevent timer from returning the same value This was SVN commit r5075. --- source/lib/config.h | 2 +- source/lib/sysdep/cpu.cpp | 193 ++++++++++++++----------- source/lib/sysdep/cpu.h | 28 ++-- source/lib/sysdep/ia32/ia32.cpp | 168 +++++++++++---------- source/lib/sysdep/win/wposix/wtime.cpp | 129 ++++++++++++----- source/lib/sysdep/win/wsnd.cpp | 2 +- source/lib/timer.cpp | 2 +- 7 files changed, 308 insertions(+), 216 deletions(-) diff --git a/source/lib/config.h b/source/lib/config.h index f24192457c..edf59ab203 100644 --- a/source/lib/config.h +++ b/source/lib/config.h @@ -396,7 +396,7 @@ # define HAVE_STL_HASH 0 #endif -// safe string functions: strcpy_s et al. +// safe CRT functions: strcpy_s, fopen_s, etc. // these are always available to users: if not provided by the CRT, we // implement them ourselves. this flag is only used to skip our impl. #if MSC_VERSION >= 1400 diff --git a/source/lib/sysdep/cpu.cpp b/source/lib/sysdep/cpu.cpp index 921d18a80f..31a776f776 100644 --- a/source/lib/sysdep/cpu.cpp +++ b/source/lib/sysdep/cpu.cpp @@ -34,108 +34,56 @@ AT_STARTUP(\ ) -static ModuleInitState module_init_state = MODULE_BEFORE_INIT; - - //----------------------------------------------------------------------------- #pragma region Accessor functions -// prevent other modules from changing the underlying data. +// insulate caller from the system-specific modules and cache results. +// note: the providers sometimes need to store the results anyway, so we +// don't need to do caching in those cases. +// these are set once during cpu_Init since they're usually all used and +// we thus avoid needing if(already_called) return old_result. +// initially set to 'impossible' values to catch uses before cpu_Init. -bool cpu_IsModuleInitialized() -{ - return module_init_state == MODULE_INITIALIZED; -} +static ModuleInitState module_init_state = MODULE_BEFORE_INIT; +static double clock_frequency = -1.0; +static bool is_throttling_possible = true; +static size_t page_size = 1; +static size_t memory_total_mib = 1; -const char* cpu_IdentifierString() + +static void DetectClockFrequency() { #if CPU_IA32 - return ia32_IdentifierString(); -#endif -} - -double cpu_ClockFrequency() -{ -#if CPU_IA32 - return ia32_ClockFrequency(); // authoritative, precise -#endif -} - -uint cpu_NumPackages() -{ -#if CPU_IA32 - return ia32_NumPackages(); -#endif -} - -uint cpu_CoresPerPackage() -{ -#if CPU_IA32 - return ia32_CoresPerPackage(); -#endif -} - -uint cpu_LogicalPerCore() -{ -#if CPU_IA32 - return ia32_LogicalPerCore(); + clock_frequency = ia32_ClockFrequency(); // authoritative, precise #endif } -bool cpu_IsThrottlingPossible() +static void DetectIfThrottlingPossible() { #if CPU_IA32 if(ia32_IsThrottlingPossible() == 1) - return true; + { + is_throttling_possible = true; + return; + } #endif + #if OS_WIN if(wcpu_IsThrottlingPossible() == 1) - return true; -#endif - return false; -} - -#pragma endregion -//----------------------------------------------------------------------------- -// memory - -static size_t cpu_page_size = 0; -// determined during cpu_Init; cleaned up and given in MiB -static size_t cpu_memory_total_mib = 0; - -// System V derived (GNU/Linux, Solaris) -#if defined(_SC_AVPHYS_PAGES) - -static int SysconfFromMemType(CpuMemoryIndicators mem_type) -{ - switch(mem_type) { - case CPU_MEM_TOTAL: - return _SC_PHYS_PAGES; - case CPU_MEM_AVAILABLE: - return _SC_AVPHYS_PAGES; + is_throttling_possible = true; + return; } - UNREACHABLE; -} - #endif -size_t cpu_MemorySize(CpuMemoryIndicators mem_type) -{ - // quasi-POSIX -#if defined(_SC_AVPHYS_PAGES) - const int sc_name = SysconfFromMemType(mem_type); - const size_t memory_size = sysconf(sc_name) * cpu_page_size; - return memory_size; - // BSD / Mac OS X -#else - return bsd_MemorySize(mem_type); -#endif + is_throttling_possible = false; } -static size_t DetermineMemoryTotalMiB() +static void DetectMemory() { + page_size = (size_t)sysconf(_SC_PAGESIZE); + size_t memory_total = cpu_MemorySize(CPU_MEM_TOTAL); // account for inaccurate reporting by rounding up (see wposix sysconf) @@ -147,16 +95,59 @@ static size_t DetermineMemoryTotalMiB() else memory_total = memory_total_pow2; - const size_t memory_total_mib = memory_total / MiB; - return memory_total_mib; + memory_total_mib = memory_total / MiB; +} + + +bool cpu_IsModuleInitialized() +{ + return module_init_state == MODULE_INITIALIZED; +} + +double cpu_ClockFrequency() +{ + return clock_frequency; +} + +bool cpu_IsThrottlingPossible() +{ + return is_throttling_possible; } size_t cpu_MemoryTotalMiB() { - return cpu_memory_total_mib; + return memory_total_mib; } +const char* cpu_IdentifierString() +{ +#if CPU_IA32 + return ia32_IdentifierString(); // cached +#endif +} +uint cpu_NumPackages() +{ +#if CPU_IA32 + return ia32_NumPackages(); // cached +#endif +} + +uint cpu_CoresPerPackage() +{ +#if CPU_IA32 + return ia32_CoresPerPackage(); // cached +#endif +} + +uint cpu_LogicalPerCore() +{ +#if CPU_IA32 + return ia32_LogicalPerCore(); // cached +#endif +} + +#pragma endregion //----------------------------------------------------------------------------- #if CPU_IA32 @@ -195,16 +186,16 @@ void cpu_Init() InitAndConfigureIA32(); #endif - // memory - cpu_page_size = (size_t)sysconf(_SC_PAGESIZE); - cpu_memory_total_mib = DetermineMemoryTotalMiB(); + DetectMemory(); + DetectIfThrottlingPossible(); + DetectClockFrequency(); // must be set before wtime_reset_impl since it queries this flag via // cpu_IsModuleInitialized. module_init_state = MODULE_INITIALIZED; // HACK: on Windows, the HRT makes its final implementation choice - // in the first calibrate call where cpu info is available. + // in the first calibrate call where CPU info is available. // call wtime_reset_impl here to have that happen now so app code isn't // surprised by a timer change, although the HRT does try to // keep the timer continuous. @@ -215,6 +206,7 @@ void cpu_Init() //----------------------------------------------------------------------------- +// stateless routines bool cpu_CAS(uintptr_t* location, uintptr_t expected, uintptr_t new_value) { @@ -287,3 +279,34 @@ i64 cpu_i64FromDouble(double d) return (i64)d; #endif } + + +// System V derived (GNU/Linux, Solaris) +#if defined(_SC_AVPHYS_PAGES) + +static int SysconfFromMemType(CpuMemoryIndicators mem_type) +{ + switch(mem_type) + { + case CPU_MEM_TOTAL: + return _SC_PHYS_PAGES; + case CPU_MEM_AVAILABLE: + return _SC_AVPHYS_PAGES; + } + UNREACHABLE; +} + +#endif + +size_t cpu_MemorySize(CpuMemoryIndicators mem_type) +{ + // quasi-POSIX +#if defined(_SC_AVPHYS_PAGES) + const int sc_name = SysconfFromMemType(mem_type); + const size_t memory_size = sysconf(sc_name) * page_size; + return memory_size; + // BSD / Mac OS X +#else + return bsd_MemorySize(mem_type); +#endif +} \ No newline at end of file diff --git a/source/lib/sysdep/cpu.h b/source/lib/sysdep/cpu.h index fa9167e41c..08d406ab1b 100644 --- a/source/lib/sysdep/cpu.h +++ b/source/lib/sysdep/cpu.h @@ -28,26 +28,12 @@ namespace ERR extern void cpu_Init(void); extern bool cpu_IsModuleInitialized(); - - extern const char* cpu_IdentifierString(); extern double cpu_ClockFrequency(); +extern bool cpu_IsThrottlingPossible(); extern uint cpu_NumPackages(); // i.e. sockets extern uint cpu_CoresPerPackage(); extern uint cpu_LogicalPerCore(); -extern bool cpu_IsThrottlingPossible(); - - -// -// memory -// - -enum CpuMemoryIndicators -{ - CPU_MEM_TOTAL, CPU_MEM_AVAILABLE -}; - -extern size_t cpu_MemorySize(CpuMemoryIndicators mem_type); // faster than cpu_MemorySize (caches total size determined during init), // returns #Mebibytes (cleaned up to account e.g. for nonpaged pool) @@ -55,7 +41,7 @@ extern size_t cpu_MemoryTotalMiB(); // -// misc +// misc (stateless) // // atomic "compare and swap". compare the machine word at against @@ -75,10 +61,18 @@ extern bool cpu_CAS(uintptr_t* location, uintptr_t expected, uintptr_t new_value **/ extern void cpu_AtomicAdd(intptr_t* location, intptr_t increment); +extern void cpu_Serialize(); + // enforce strong memory ordering. extern void cpu_MemoryFence(); -extern void cpu_Serialize(); + +enum CpuMemoryIndicators +{ + CPU_MEM_TOTAL, CPU_MEM_AVAILABLE +}; + +extern size_t cpu_MemorySize(CpuMemoryIndicators mem_type); // drop-in replacement for libc memcpy(). only requires CPU support for diff --git a/source/lib/sysdep/ia32/ia32.cpp b/source/lib/sysdep/ia32/ia32.cpp index 2836804687..655f6df0db 100644 --- a/source/lib/sysdep/ia32/ia32.cpp +++ b/source/lib/sysdep/ia32/ia32.cpp @@ -208,7 +208,11 @@ public: const char* ia32_IdentifierString() { // 3 calls x 4 registers x 4 bytes = 48 - static char identifier_string[48+1] = ""; + static char identifier_string[48+1] = {'\0'}; + + // not first call, return previous result + if(identifier_string[0] != '\0') + return identifier_string; // get processor signature u32 regs[4]; @@ -309,92 +313,106 @@ int ia32_IsThrottlingPossible() } +// set scheduling priority and restore when going out of scope. +class ScopedSetPriority +{ + int m_old_policy; + sched_param m_old_param; + +public: + ScopedSetPriority(int new_priority) + { + // get current scheduling policy and priority + pthread_getschedparam(pthread_self(), &m_old_policy, &m_old_param); + + // set new priority + sched_param new_param = {0}; + new_param.sched_priority = new_priority; + pthread_setschedparam(pthread_self(), SCHED_FIFO, &new_param); + } + + ~ScopedSetPriority() + { + // restore previous policy and priority. + pthread_setschedparam(pthread_self(), m_old_policy, &m_old_param); + } +}; + double ia32_ClockFrequency() { - double clock_frequency = 0.0; + // if the TSC isn't available, there's really no good way to count the + // actual CPU clocks per known time interval, so bail. + // note: loop iterations ("bogomips") are not a reliable measure due + // to differing IPC and compiler optimizations. + if(!ia32_cap(IA32_CAP_TSC)) + return -1.0; // impossible value - // set max priority, to reduce interference while measuring. - int old_policy; static sched_param old_param; // (static => 0-init) - pthread_getschedparam(pthread_self(), &old_policy, &old_param); - static sched_param max_param; - max_param.sched_priority = sched_get_priority_max(SCHED_FIFO); - pthread_setschedparam(pthread_self(), SCHED_FIFO, &max_param); + // increase priority to reduce interference while measuring. + const int priority = sched_get_priority_max(SCHED_FIFO)-1; + ScopedSetPriority ssp(priority); - // make sure the TSC is available, because we're going to - // measure actual CPU clocks per known time interval. - // counting loop iterations ("bogomips") is unreliable. - if(ia32_cap(IA32_CAP_TSC)) + // note: no need to "warm up" cpuid - it will already have been + // called several times by the time this code is reached. + // (background: it's used in ia32_rdtsc() to serialize instruction flow; + // the first call is documented to be slower on Intel CPUs) + + int num_samples = 16; + // if clock is low-res, do less samples so it doesn't take too long. + // balance measuring time (~ 10 ms) and accuracy (< 1 0/00 error - + // ok for using the TSC as a time reference) + if(timer_res() >= 1e-3) + num_samples = 8; + std::vector samples(num_samples); + + for(int i = 0; i < num_samples; i++) { - // note: no need to "warm up" cpuid - it will already have been - // called several times by the time this code is reached. - // (background: it's used in ia32_rdtsc() to serialize instruction flow; - // the first call is documented to be slower on Intel CPUs) + double dt; + i64 dc; // i64 because VC6 can't convert u64 -> double, + // and we don't need all 64 bits. - int num_samples = 16; - // if clock is low-res, do less samples so it doesn't take too long. - // balance measuring time (~ 10 ms) and accuracy (< 1 0/00 error - - // ok for using the TSC as a time reference) - if(timer_res() >= 1e-3) - num_samples = 8; - std::vector samples(num_samples); - - int i; - for(i = 0; i < num_samples; i++) + // count # of clocks in max{1 tick, 1 ms}: + // .. wait for start of tick. + const double t0 = get_time(); + u64 c1; double t1; + do { - double dt; - i64 dc; - // i64 because VC6 can't convert u64 -> double, - // and we don't need all 64 bits. - - // count # of clocks in max{1 tick, 1 ms}: - // .. wait for start of tick. - const double t0 = get_time(); - u64 c1; double t1; - do - { - // note: get_time effectively has a long delay (up to 5 us) - // before returning the time. we call it before ia32_rdtsc to - // minimize the delay between actually sampling time / TSC, - // thus decreasing the chance for interference. - // (if unavoidable background activity, e.g. interrupts, - // delays the second reading, inaccuracy is introduced). - t1 = get_time(); - c1 = ia32_rdtsc(); - } - while(t1 == t0); - // .. wait until start of next tick and at least 1 ms elapsed. - do - { - const double t2 = get_time(); - const u64 c2 = ia32_rdtsc(); - dc = (i64)(c2 - c1); - dt = t2 - t1; - } - while(dt < 1e-3); - - // .. freq = (delta_clocks) / (delta_seconds); - // ia32_rdtsc/timer overhead is negligible. - const double freq = dc / dt; - samples[i] = freq; + // note: get_time effectively has a long delay (up to 5 us) + // before returning the time. we call it before ia32_rdtsc to + // minimize the delay between actually sampling time / TSC, + // thus decreasing the chance for interference. + // (if unavoidable background activity, e.g. interrupts, + // delays the second reading, inaccuracy is introduced). + t1 = get_time(); + c1 = ia32_rdtsc(); } + while(t1 == t0); + // .. wait until start of next tick and at least 1 ms elapsed. + do + { + const double t2 = get_time(); + const u64 c2 = ia32_rdtsc(); + dc = (i64)(c2 - c1); + dt = t2 - t1; + } + while(dt < 1e-3); - std::sort(samples.begin(), samples.end()); - - // median filter (remove upper and lower 25% and average the rest). - // note: don't just take the lowest value! it could conceivably be - // too low, if background processing delays reading c1 (see above). - double sum = 0.0; - const int lo = num_samples/4, hi = 3*num_samples/4; - for(i = lo; i < hi; i++) - sum += samples[i]; - clock_frequency = sum / (hi-lo); - + // .. freq = (delta_clocks) / (delta_seconds); + // ia32_rdtsc/timer overhead is negligible. + const double freq = dc / dt; + samples[i] = freq; } - // else: TSC not available, can't measure; cpu_freq remains unchanged. - // restore previous policy and priority. - pthread_setschedparam(pthread_self(), old_policy, &old_param); + std::sort(samples.begin(), samples.end()); + // median filter (remove upper and lower 25% and average the rest). + // note: don't just take the lowest value! it could conceivably be + // too low, if background processing delays reading c1 (see above). + double sum = 0.0; + const int lo = num_samples/4, hi = 3*num_samples/4; + for(int i = lo; i < hi; i++) + sum += samples[i]; + + const double clock_frequency = sum / (hi-lo); return clock_frequency; } diff --git a/source/lib/sysdep/win/wposix/wtime.cpp b/source/lib/sysdep/win/wposix/wtime.cpp index 7c56d9d9aa..4853e4a4ce 100644 --- a/source/lib/sysdep/win/wposix/wtime.cpp +++ b/source/lib/sysdep/win/wposix/wtime.cpp @@ -65,7 +65,7 @@ AT_STARTUP(\ // (default values for HRT_NONE impl) // initial measurement of the time source's tick rate. not necessarily -// correct (e.g. when using TSC; cpu_ClockFrequency isn't exact). +// correct (e.g. when using TSC: cpu_ClockFrequency isn't exact). static double hrt_nominal_freq = -1.0; // actual resolution of the time source (may differ from hrt_nominal_freq @@ -130,9 +130,9 @@ enum HRTOverride HRT_FORCE }; +// HRTImpl enums as index +// HACK: no init needed - static data is zeroed (= HRT_DEFAULT) static HRTOverride overrides[HRT_NUM_IMPLS]; - // HRTImpl enums as index - // HACK: no init needed - static data is zeroed (= HRT_DEFAULT) cassert((int)HRT_DEFAULT == 0); @@ -152,6 +152,14 @@ static inline void unlock(void) } +static bool IsSimilarMagnitude(double d1, double d2, const double relative_error_tolerance = 0.05) +{ + const double relative_error = fabs(d1/d2 - 1.0); + if(relative_error > relative_error_tolerance) + return false; + return true; +} + // decide upon a HRT implementation, checking if we can work around // each timer's issues on this platform, but allow user override // in case there are unforeseen problems with one of them. @@ -166,6 +174,9 @@ static LibError choose_impl() if(overrides[impl] == HRT_FORCE)\ safe = true; + // used several times below, so latch it for convenience. + const double cpu_freq = cpu_IsModuleInitialized()? cpu_ClockFrequency() : 0.0; + #if CPU_IA32 && !defined(NO_TSC) // CPU Timestamp Counter (incremented every clock) // ns resolution, moderate precision (poor clock crystal?) @@ -187,7 +198,62 @@ static LibError choose_impl() // will do this as well (if not to save power, for heat reasons). // frequency changes are too often and drastic to correct, // and we don't want to mess with the system power settings => unsafe. - if(cpu_IsModuleInitialized() && cpu_ClockFrequency() > 0.0 && ia32_cap(IA32_CAP_TSC)) + + +/* +AMD has defined a CPUID feature bit that +software can test to determine if the TSC is +invariant. Issuing a CPUID instruction with an %eax register +value of 0x8000_0007, on a processor whose base family is +0xF, returns "Advanced Power Management Information" in the +%eax, %ebx, %ecx, and %edx registers. Bit 8 of the return +%edx is the "TscInvariant" feature flag which is set when +TSC is P-state, C-state, and STPCLK-throttling invariant; it +is clear otherwise. +*/ + +/* +if (CPUID.base_family < 0xf) { +// TSC drift doesn't exist on 7th Gen or less +// However, OS still needs to consider effects +// of P-state changes on TSC +return TRUE; + +} else if (CPUID.AdvPowerMgmtInfo.TscInvariant) { +// Invariant TSC on 8th Gen or newer, use it +// (assume all cores have invariant TSC) +return TRUE; + +} else if ((number_processors == 1)&&(number_cores == 1)){ +// OK to use TSC on uni-processor-uni-core +// However, OS still needs to consider effects +// of P-state changes on TSC +return TRUE; + +} else if ( (number_processors == 1) && +(CPUID.effective_family == 0x0f) && +!C1_ramp_8gen ){ +// Use TSC on 8th Gen uni-proc with C1_ramp off +// However, OS still needs to consider effects +// of P-state changes on TSC +return TRUE; + +} else { +return FALSE; +} + +} + +C1_ramp_8gen() { +// Check if C1-Clock ramping enabled in PMM7.CpuLowPwrEnh +// On 8th-Generation cores only. Assume BIOS has setup +// all Northbridges equivalently. + +return (1 & read_pci_byte(bus=0,dev=0x18,fcn=3,offset=0x87)); +} +*/ + + if(cpu_freq > 0.0 && ia32_cap(IA32_CAP_TSC)) { safe = (cpu_CoresPerPackage() == 1 && cpu_NumPackages() == 1 && cpu_IsThrottlingPossible() == 0); SAFETY_OVERRIDE(HRT_TSC); @@ -213,17 +279,15 @@ static LibError choose_impl() // 2) "System clock problem can inflate benchmark scores": // incorrect value if not polled every 4.5 seconds? solved // by calibration thread, which reads timer every second anyway. - // - TSC on MP HAL - see TSC above. + // - TSC on MP HAL, sometimes with 1/3 of CPU freq. // cache freq because QPF is fairly slow. - static i64 qpc_freq = -1; - - // first call - check if QPC is supported - if(qpc_freq == -1) + static i64 qpc_freq = -1; // set to 0 if unsupported + if(qpc_freq == -1) // first call { - LARGE_INTEGER i; - BOOL qpc_ok = QueryPerformanceFrequency(&i); - qpc_freq = qpc_ok? i.QuadPart : 0; + LARGE_INTEGER freq; + BOOL qpc_ok = QueryPerformanceFrequency(&freq); + qpc_freq = qpc_ok? freq.QuadPart : 0; } // QPC is available @@ -241,11 +305,14 @@ static LibError choose_impl() safe = false; else { - // compare QPC freq to CPU clock freq - can't rule out HPET, - // because its frequency isn't known (it's at least 10 MHz). - double freq_dist = fabs(cpu_ClockFrequency()/qpc_freq - 1.0); - safe = freq_dist > 0.05; - // safe if freqs not within 5% (i.e. it doesn't use TSC) + safe = true; + // compare QPC freq to CPU clock freq. note: we can't + // single out the HPET (as with PIT and PMT above) because + // its frequency is variable and at least 10 MHz. + if(IsSimilarMagnitude(qpc_freq, cpu_freq)) + safe = false; + if(IsSimilarMagnitude(qpc_freq, cpu_freq/3)) // QPC sometimes uses RDTSC/3 + safe = false; } } @@ -318,12 +385,8 @@ static i64 ticks_lk() // add further timers here. - case HRT_NUM_IMPLS: default: debug_warn("invalid impl"); - //-fallthrough - - case HRT_NONE: return 0; } // switch(impl) } @@ -348,7 +411,7 @@ static double time_lk() -// this module is dependent upon detect (supplies system information needed to +// this module is dependent upon cpu.cpp (supplies information needed to // choose a HRT), which in turn uses our timer to detect the CPU clock // when running on Windows (clock(), the only cross platform HRT available on // Windows, isn't good enough - only 10..15 ms resolution). @@ -390,6 +453,8 @@ static LibError reset_impl_lk() hrt_cal_ticks = ticks_lk(); } +debug_printf("HRT impl=%d nominal_freq=%f cur_freq=%f\n", hrt_impl, hrt_nominal_freq, hrt_cur_freq); + return INFO::OK; } @@ -408,9 +473,8 @@ unlock(); // return seconds since init. static double hrt_time() { - double t; lock(); - t = time_lk(); + const double t = time_lk(); unlock(); return t; } @@ -423,7 +487,7 @@ static double hrt_delta_s(i64 start, i64 end) { // paranoia: reading double may not be atomic. lock(); - double freq = hrt_cur_freq; + const double freq = hrt_cur_freq; unlock(); debug_assert(freq != -1.0 && "hrt_delta_s: hrt_cur_freq not set"); @@ -434,8 +498,6 @@ unlock(); // return current timer implementation and its nominal (rated) frequency. // nominal_freq is never 0. // implementation only changes after hrt_override_impl. -// -// may be called before first hrt_ticks / hrt_time, so do init here also. static void hrt_query_impl(HRTImpl& impl, double& nominal_freq, double& res) { lock(); @@ -472,12 +534,9 @@ unlock(); } -////////////////////////////////////////////////////////////////////////////// -// +//----------------------------------------------------------------------------- // calibration -// -////////////////////////////////////////////////////////////////////////////// - +//----------------------------------------------------------------------------- // 'safe' timer, used to measure HRT freq in calibrate() static const long safe_timer_freq = 1000; @@ -629,11 +688,9 @@ static LibError hrt_shutdown() } -////////////////////////////////////////////////////////////////////////////// -// +//----------------------------------------------------------------------------- // wtime wrapper: emulates POSIX functions -// -////////////////////////////////////////////////////////////////////////////// +//----------------------------------------------------------------------------- // NT system time and FILETIME are hectonanoseconds since Jan. 1, 1601 UTC. // SYSTEMTIME is a struct containing month, year, etc. diff --git a/source/lib/sysdep/win/wsnd.cpp b/source/lib/sysdep/win/wsnd.cpp index 0be382f0cf..35418f0971 100644 --- a/source/lib/sysdep/win/wsnd.cpp +++ b/source/lib/sysdep/win/wsnd.cpp @@ -129,7 +129,7 @@ static char ds_drv_path[MAX_PATH+1]; // store sound card name and path to DirectSound driver. // called for each DirectSound driver, but aborts after first valid driver. static BOOL CALLBACK ds_enum(void* UNUSED(guid), const char* description, - const char* module, void* UNUSED(ctx)) + const char* module, void* UNUSED(ctx)) { // skip first (dummy) entry, where description == "Primary Sound Driver". if(module[0] == '\0') diff --git a/source/lib/timer.cpp b/source/lib/timer.cpp index 5179e94e04..1b79dca381 100644 --- a/source/lib/timer.cpp +++ b/source/lib/timer.cpp @@ -58,7 +58,7 @@ double get_time() // make sure time is monotonic (never goes backwards) static double t_last = 0.0; if(t < t_last) - t = t_last; + t = t_last+DBL_EPSILON; t_last = t; return t;