summaryrefslogtreecommitdiff
path: root/pjlib
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-04-09 10:37:09 +0000
committerBenny Prijono <bennylp@teluu.com>2006-04-09 10:37:09 +0000
commit7eb85e8308835214354d55acbf470a8912fdacc7 (patch)
treec4dd040223787ba09e45ca7e41ce24d376986a76 /pjlib
parenta729289991a830653883592cdf8bb5b7413afa33 (diff)
Added experimental code to handle QueryPerformanceCounter leaping bug on Win32
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@396 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib')
-rw-r--r--pjlib/src/pj/os_timestamp_win32.c170
1 files changed, 170 insertions, 0 deletions
diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c
index 7cd391e3..63f624ca 100644
--- a/pjlib/src/pj/os_timestamp_win32.c
+++ b/pjlib/src/pj/os_timestamp_win32.c
@@ -17,9 +17,23 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/os.h>
+#include <pj/assert.h>
#include <pj/errno.h>
+#include <pj/log.h>
#include <windows.h>
+#define THIS_FILE "os_timestamp_win32.c"
+
+
+#if 1
+# define TRACE_(x) PJ_LOG(3,x)
+#else
+# define TRACE_(x) ;
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+
#if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \
defined(PJ_M_I386) && PJ_M_I386 != 0 && \
defined(PJ_HAS_PENTIUM) && PJ_HAS_PENTIUM!=0 && \
@@ -94,9 +108,164 @@ PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
return PJ_SUCCESS;
}
+/////////////////////////////////////////////////////////////////////////////
+
+#elif defined(PJ_TIMESTAMP_WIN32_USE_SAFE_QPC) && \
+ PJ_TIMESTAMP_WIN32_USE_SAFE_QPC!=0
+
+/* Use safe QueryPerformanceCounter.
+ * This implementation has some protection against bug in KB Q274323:
+ * Performance counter value may unexpectedly leap forward
+ * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
+ *
+ * THIS SHOULD NOT BE USED YET AS IT DOESN'T HANDLE SYSTEM TIME
+ * CHANGE.
+ */
+
+static pj_timestamp g_ts_freq;
+static pj_timestamp g_ts_base;
+static pj_int64_t g_time_base;
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+ enum { MAX_RETRY = 10 };
+ unsigned i;
+
+
+ /* pj_get_timestamp_freq() must have been called before.
+ * This is done when application called pj_init().
+ */
+ pj_assert(g_ts_freq.u64 != 0);
+
+ /* Retry QueryPerformanceCounter() until we're sure that the
+ * value returned makes sense.
+ */
+ i = 0;
+ do {
+ LARGE_INTEGER val;
+ pj_int64_t counter64, time64, diff;
+ pj_time_val time_now;
+
+ /* Retrieve the counter */
+ if (!QueryPerformanceCounter(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ /* Regardless of the goodness of the value, we should put
+ * the counter here, because normally application wouldn't
+ * check the error result of this function.
+ */
+ ts->u64 = val.QuadPart;
+
+ /* Retrieve time */
+ pj_gettimeofday(&time_now);
+
+ /* Get the counter elapsed time in miliseconds */
+ counter64 = (val.QuadPart - g_ts_base.u64) * 1000 / g_ts_freq.u64;
+
+ /* Get the time elapsed in miliseconds.
+ * We don't want to use PJ_TIME_VAL_MSEC() since it's using
+ * 32bit calculation, which limits the maximum elapsed time
+ * to around 49 days only.
+ */
+ time64 = time_now.sec;
+ time64 = time64 * 1000 + time_now.msec;
+ //time64 = GetTickCount();
+
+ /* It's good if the difference between two clocks are within
+ * some compile time constant (default: 20ms, which to allow
+ * context switch happen between QueryPerformanceCounter and
+ * pj_gettimeofday()).
+ */
+ diff = (time64 - g_time_base) - counter64;
+ if (diff >= -20 && diff <= 20) {
+ /* It's good */
+ return PJ_SUCCESS;
+ }
+
+ ++i;
+
+ } while (i < MAX_RETRY);
+
+ TRACE_((THIS_FILE, "QueryPerformanceCounter returned bad value"));
+ return PJ_ETIMEDOUT;
+}
+
+static pj_status_t init_performance_counter(void)
+{
+ LARGE_INTEGER val;
+ pj_time_val time_base;
+ pj_status_t status;
+
+ /* Get the frequency */
+ if (!QueryPerformanceFrequency(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ g_ts_freq.u64 = val.QuadPart;
+
+ /* Get the base timestamp */
+ if (!QueryPerformanceCounter(&val))
+ return PJ_RETURN_OS_ERROR(GetLastError());
+
+ g_ts_base.u64 = val.QuadPart;
+
+
+ /* Get the base time */
+ status = pj_gettimeofday(&time_base);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Convert time base to 64bit value in msec */
+ g_time_base = time_base.sec;
+ g_time_base = g_time_base * 1000 + time_base.msec;
+ //g_time_base = GetTickCount();
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+ if (g_ts_freq.u64 == 0) {
+ enum { MAX_REPEAT = 10 };
+ unsigned i;
+ pj_status_t status;
+
+ /* Make unellegant compiler happy */
+ status = 0;
+
+ /* Repeat initializing performance counter until we're sure
+ * the base timing is correct. It is possible that the system
+ * returns bad counter during this initialization!
+ */
+ for (i=0; i<MAX_REPEAT; ++i) {
+
+ pj_timestamp dummy;
+
+ /* Init base time */
+ status = init_performance_counter();
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Try the base time */
+ status = pj_get_timestamp(&dummy);
+ if (status == PJ_SUCCESS)
+ break;
+ }
+
+ if (status != PJ_SUCCESS)
+ return status;
+ }
+
+ freq->u64 = g_ts_freq.u64;
+ return PJ_SUCCESS;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
#else
+
/*
* Use QueryPerformanceCounter and QueryPerformanceFrequency.
+ * This should be the default implementation to be used on Windows.
*/
PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
{
@@ -120,5 +289,6 @@ PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
return PJ_SUCCESS;
}
+
#endif /* PJ_TIMESTAMP_USE_RDTSC */