Changeset 396


Ignore:
Timestamp:
Apr 9, 2006 10:37:09 AM (18 years ago)
Author:
bennylp
Message:

Added experimental code to handle QueryPerformanceCounter? leaping bug on Win32

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjlib/src/pj/os_timestamp_win32.c

    r125 r396  
    1818 */ 
    1919#include <pj/os.h> 
     20#include <pj/assert.h> 
    2021#include <pj/errno.h> 
     22#include <pj/log.h> 
    2123#include <windows.h> 
     24 
     25#define THIS_FILE   "os_timestamp_win32.c" 
     26 
     27 
     28#if 1 
     29#   define TRACE_(x)        PJ_LOG(3,x) 
     30#else 
     31#   define TRACE_(x)        ; 
     32#endif 
     33 
     34 
     35///////////////////////////////////////////////////////////////////////////// 
    2236 
    2337#if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \ 
     
    95109} 
    96110 
     111///////////////////////////////////////////////////////////////////////////// 
     112 
     113#elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \ 
     114         PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0 
     115 
     116/* Use safe QueryPerformanceCounter. 
     117 * This implementation has some protection against bug in KB Q274323: 
     118 *   Performance counter value may unexpectedly leap forward 
     119 *   http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323 
     120 * 
     121 * THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME 
     122 * CHANGE. 
     123 */ 
     124 
     125static pj_timestamp g_ts_freq; 
     126static pj_timestamp g_ts_base; 
     127static pj_int64_t   g_time_base; 
     128 
     129PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) 
     130{ 
     131    enum { MAX_RETRY = 10 }; 
     132    unsigned i; 
     133 
     134 
     135    /* pj_get_timestamp_freq() must have been called before. 
     136     * This is done when application called pj_init(). 
     137     */ 
     138    pj_assert(g_ts_freq.u64 != 0); 
     139 
     140    /* Retry QueryPerformanceCounter() until we're sure that the 
     141     * value returned makes sense. 
     142     */ 
     143    i = 0; 
     144    do { 
     145        LARGE_INTEGER val; 
     146        pj_int64_t counter64, time64, diff; 
     147        pj_time_val time_now; 
     148 
     149        /* Retrieve the counter */ 
     150        if (!QueryPerformanceCounter(&val)) 
     151            return PJ_RETURN_OS_ERROR(GetLastError()); 
     152 
     153        /* Regardless of the goodness of the value, we should put 
     154         * the counter here, because normally application wouldn't 
     155         * check the error result of this function. 
     156         */ 
     157        ts->u64 = val.QuadPart; 
     158 
     159        /* Retrieve time */ 
     160        pj_gettimeofday(&time_now); 
     161 
     162        /* Get the counter elapsed time in miliseconds */ 
     163        counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64; 
     164         
     165        /* Get the time elapsed in miliseconds.  
     166         * We don't want to use PJ_TIME_VAL_MSEC() since it's using 
     167         * 32bit calculation, which limits the maximum elapsed time 
     168         * to around 49 days only. 
     169         */ 
     170        time64 = time_now.sec; 
     171        time64 = time64 * 1000 + time_now.msec; 
     172        //time64 = GetTickCount(); 
     173 
     174        /* It's good if the difference between two clocks are within 
     175         * some compile time constant (default: 20ms, which to allow 
     176         * context switch happen between QueryPerformanceCounter and 
     177         * pj_gettimeofday()). 
     178         */ 
     179        diff = (time64 - g_time_base) - counter64; 
     180        if (diff >= -20 && diff <= 20) { 
     181            /* It's good */ 
     182            return PJ_SUCCESS; 
     183        } 
     184 
     185        ++i; 
     186 
     187    } while (i < MAX_RETRY); 
     188 
     189    TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value")); 
     190    return PJ_ETIMEDOUT; 
     191} 
     192 
     193static pj_status_t init_performance_counter(void) 
     194{ 
     195    LARGE_INTEGER val; 
     196    pj_time_val time_base; 
     197    pj_status_t status; 
     198 
     199    /* Get the frequency */ 
     200    if (!QueryPerformanceFrequency(&val)) 
     201        return PJ_RETURN_OS_ERROR(GetLastError()); 
     202 
     203    g_ts_freq.u64 = val.QuadPart; 
     204 
     205    /* Get the base timestamp */ 
     206    if (!QueryPerformanceCounter(&val)) 
     207        return PJ_RETURN_OS_ERROR(GetLastError()); 
     208 
     209    g_ts_base.u64 = val.QuadPart; 
     210 
     211 
     212    /* Get the base time */ 
     213    status = pj_gettimeofday(&time_base); 
     214    if (status != PJ_SUCCESS) 
     215        return status; 
     216 
     217    /* Convert time base to 64bit value in msec */ 
     218    g_time_base = time_base.sec; 
     219    g_time_base  = g_time_base * 1000 + time_base.msec; 
     220    //g_time_base = GetTickCount(); 
     221 
     222    return PJ_SUCCESS; 
     223} 
     224 
     225PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq) 
     226{ 
     227    if (g_ts_freq.u64 == 0) { 
     228        enum { MAX_REPEAT = 10 }; 
     229        unsigned i; 
     230        pj_status_t status; 
     231 
     232        /* Make unellegant compiler happy */ 
     233        status = 0; 
     234 
     235        /* Repeat initializing performance counter until we're sure 
     236         * the base timing is correct. It is possible that the system 
     237         * returns bad counter during this initialization! 
     238         */ 
     239        for (i=0; i<MAX_REPEAT; ++i) { 
     240 
     241            pj_timestamp dummy; 
     242 
     243            /* Init base time */ 
     244            status = init_performance_counter(); 
     245            if (status != PJ_SUCCESS) 
     246                return status; 
     247 
     248            /* Try the base time */ 
     249            status = pj_get_timestamp(&dummy); 
     250            if (status == PJ_SUCCESS) 
     251                break; 
     252        } 
     253 
     254        if (status != PJ_SUCCESS) 
     255            return status; 
     256    } 
     257 
     258    freq->u64 = g_ts_freq.u64; 
     259    return PJ_SUCCESS; 
     260} 
     261 
     262///////////////////////////////////////////////////////////////////////////// 
     263 
    97264#else 
     265 
    98266/* 
    99267 * Use QueryPerformanceCounter and QueryPerformanceFrequency. 
     268 * This should be the default implementation to be used on Windows. 
    100269 */ 
    101270PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts) 
     
    121290} 
    122291 
     292 
    123293#endif  /* PJ_TIMESTAMP_USE_RDTSC */ 
    124294 
Note: See TracChangeset for help on using the changeset viewer.