Added rdtsc option for win32 timestamp and added pj_elapsed_msec

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@70 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h
index 2a8d5c0..ec21a52 100644
--- a/pjlib/include/pj/config.h
+++ b/pjlib/include/pj/config.h
@@ -97,6 +97,7 @@
 #   undef PJ_HAS_SEMAPHORE
 #   undef PJ_HAS_EVENT_OBJ
 #   undef PJ_ENABLE_EXTRA_CHECK
+#   undef PJ_EXCEPTION_USE_WIN32_SEH
 #endif
 
 /**
@@ -319,6 +320,27 @@
 #   define PJ_MAX_EXCEPTION_ID      16
 #endif
 
+/**
+ * Should we use Windows Structured Exception Handling (SEH) for the
+ * PJLIB exceptions.
+ *
+ * Default: 0
+ */
+#ifndef PJ_EXCEPTION_USE_WIN32_SEH
+#  define PJ_EXCEPTION_USE_WIN32_SEH 0
+#endif
+
+/**
+ * Should we attempt to use Pentium's rdtsc for high resolution
+ * timestamp.
+ *
+ * Default: 0
+ */
+#ifndef PJ_TIMESTAMP_USE_RDTSC
+#   define PJ_TIMESTAMP_USE_RDTSC   0
+#endif
+
+
 /** @} */
 
 /********************************************************************
@@ -353,11 +375,13 @@
 #ifdef __cplusplus
 #  define PJ_DECL(type)		    type
 #  define PJ_DECL_NO_RETURN(type)   type PJ_NORETURN
+#  define PJ_IDECL_NO_RETURN(type)  PJ_INLINE(type) PJ_NORETURN
 #  define PJ_BEGIN_DECL		    extern "C" {
 #  define PJ_END_DECL		    }
 #else
 #  define PJ_DECL(type)		    extern type
 #  define PJ_DECL_NO_RETURN(type)   PJ_NORETURN type
+#  define PJ_IDECL_NO_RETURN(type)  PJ_NORETURN PJ_INLINE(type)
 #  define PJ_BEGIN_DECL
 #  define PJ_END_DECL
 #endif
diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h
index 7b03c96..2917a2a 100644
--- a/pjlib/include/pj/os.h
+++ b/pjlib/include/pj/os.h
@@ -922,6 +922,24 @@
                                       const pj_timestamp *stop );
 
 /**
+ * Calculate the elapsed time as 32-bit miliseconds.
+ * This function calculates the elapsed time using highest precision
+ * calculation that is available for current platform, considering
+ * whether floating point or 64-bit precision arithmetic is available. 
+ * For maximum portability, application should prefer to use this function
+ * rather than calculating the elapsed time by itself.
+ *
+ * @param start     The starting timestamp.
+ * @param stop      The end timestamp.
+ *
+ * @return	    Elapsed time in milisecond.
+ *
+ * @see pj_elapsed_time(), pj_elapsed_cycle(), pj_elapsed_nanosec()
+ */
+PJ_DECL(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start,
+                                      const pj_timestamp *stop );
+
+/**
  * Calculate the elapsed time in 32-bit microseconds.
  * This function calculates the elapsed time using highest precision
  * calculation that is available for current platform, considering
diff --git a/pjlib/src/pj/os_core_win32.c b/pjlib/src/pj/os_core_win32.c
index 91add77..756b88a 100644
--- a/pjlib/src/pj/os_core_win32.c
+++ b/pjlib/src/pj/os_core_win32.c
@@ -161,8 +161,10 @@
 #if defined(PJ_HAS_HIGH_RES_TIMER) && PJ_HAS_HIGH_RES_TIMER != 0
     {
 	pj_timestamp dummy_ts;
+	if ((rc=pj_get_timestamp_freq(&dummy_ts)) != PJ_SUCCESS) {
+	    return rc;
+	}
 	if ((rc=pj_get_timestamp(&dummy_ts)) != PJ_SUCCESS) {
-	    PJ_LOG(1, ("pj_init", "Unable to initialize timestamp"));
 	    return rc;
 	}
     }
diff --git a/pjlib/src/pj/os_timestamp_common.c b/pjlib/src/pj/os_timestamp_common.c
index 4dc410f..a86c174 100644
--- a/pjlib/src/pj/os_timestamp_common.c
+++ b/pjlib/src/pj/os_timestamp_common.c
@@ -26,9 +26,14 @@
 #define USEC    (1000000UL)
 #define MSEC    (1000)
 
+#define u64tohighprec(u64)	((pj_highprec_t)((pj_int64_t)(u64)))
+
 static pj_highprec_t get_elapsed( const pj_timestamp *start,
                                   const pj_timestamp *stop )
 {
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    return u64tohighprec(stop->u64 - start->u64);
+#else
     pj_highprec_t elapsed_hi, elapsed_lo;
 
     elapsed_hi = stop->u32.hi - start->u32.hi;
@@ -38,6 +43,38 @@
     pj_highprec_mul(elapsed_hi, U32MAX);
 
     return elapsed_hi + elapsed_lo;
+#endif
+}
+
+static pj_highprec_t elapsed_msec( const pj_timestamp *start,
+                                   const pj_timestamp *stop )
+{
+    pj_timestamp ts_freq;
+    pj_highprec_t freq, elapsed;
+
+    if (pj_get_timestamp_freq(&ts_freq) != PJ_SUCCESS)
+        return 0;
+
+    /* Convert frequency timestamp */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    freq = u64tohighprec(ts_freq.u64);
+#else
+    freq = ts_freq.u32.hi;
+    pj_highprec_mul(freq, U32MAX);
+    freq += ts_freq.u32.lo;
+#endif
+
+    /* Avoid division by zero. */
+    if (freq == 0) freq = 1;
+
+    /* Get elapsed time in cycles. */
+    elapsed = get_elapsed(start, stop);
+
+    /* usec = elapsed * MSEC / freq */
+    pj_highprec_mul(elapsed, MSEC);
+    pj_highprec_div(elapsed, freq);
+
+    return elapsed;
 }
 
 static pj_highprec_t elapsed_usec( const pj_timestamp *start,
@@ -50,9 +87,13 @@
         return 0;
 
     /* Convert frequency timestamp */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    freq = u64tohighprec(ts_freq.u64);
+#else
     freq = ts_freq.u32.hi;
     pj_highprec_mul(freq, U32MAX);
     freq += ts_freq.u32.lo;
+#endif
 
     /* Avoid division by zero. */
     if (freq == 0) freq = 1;
@@ -77,9 +118,13 @@
         return 0;
 
     /* Convert frequency timestamp */
+#if defined(PJ_HAS_INT64) && PJ_HAS_INT64!=0
+    freq = u64tohighprec(ts_freq.u64);
+#else
     freq = ts_freq.u32.hi;
     pj_highprec_mul(freq, U32MAX);
     freq += ts_freq.u32.lo;
+#endif
 
     /* Avoid division by zero. */
     if (freq == 0) freq = 1;
@@ -100,10 +145,16 @@
     return (pj_uint32_t)elapsed_usec(start, stop);
 }
 
+PJ_DEF(pj_uint32_t) pj_elapsed_msec( const pj_timestamp *start,
+                                     const pj_timestamp *stop )
+{
+    return (pj_uint32_t)elapsed_msec(start, stop);
+}
+
 PJ_DEF(pj_time_val) pj_elapsed_time( const pj_timestamp *start,
                                      const pj_timestamp *stop )
 {
-    pj_highprec_t elapsed = elapsed_usec(start, stop);
+    pj_highprec_t elapsed = elapsed_msec(start, stop);
     pj_time_val tv_elapsed;
 
     if (PJ_HIGHPREC_VALUE_IS_ZERO(elapsed)) {
@@ -113,12 +164,11 @@
         pj_highprec_t sec, msec;
 
         sec = elapsed;
-        pj_highprec_div(sec, USEC);
+        pj_highprec_div(sec, MSEC);
         tv_elapsed.sec = (long)sec;
 
         msec = elapsed;
-        pj_highprec_mod(msec, USEC);
-        pj_highprec_div(msec, 1000);
+        pj_highprec_mod(msec, MSEC);
         tv_elapsed.msec = (long)msec;
 
         return tv_elapsed;
diff --git a/pjlib/src/pj/os_timestamp_win32.c b/pjlib/src/pj/os_timestamp_win32.c
index eeff6c0..757a300 100644
--- a/pjlib/src/pj/os_timestamp_win32.c
+++ b/pjlib/src/pj/os_timestamp_win32.c
@@ -20,6 +20,75 @@
 #include <pj/errno.h>
 #include <windows.h>
 
+#if defined(PJ_TIMESTAMP_USE_RDTSC) && PJ_TIMESTAMP_USE_RDTSC!=0 && \
+    defined(PJ_M_I386) && PJ_M_I386 != 0 && \
+    defined(_MSC_VER)
+/*
+ * Use rdtsc to get the OS timestamp.
+ */
+static LONG CpuMhz;
+static pj_int64_t CpuHz;
+ 
+static pj_status_t GetCpuHz(void)
+{
+    HKEY key;
+    LONG rc;
+    DWORD size;
+
+    rc = RegOpenKey( HKEY_LOCAL_MACHINE,
+		     "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+		     &key);
+    if (rc != ERROR_SUCCESS)
+	return PJ_RETURN_OS_ERROR(rc);
+
+    size = sizeof(CpuMhz);
+    rc = RegQueryValueEx(key, "~MHz", NULL, NULL, (BYTE*)&CpuMhz, &size);
+    RegCloseKey(key);
+
+    if (rc != ERROR_SUCCESS) {
+	return PJ_RETURN_OS_ERROR(rc);
+    }
+
+    CpuHz = CpuMhz;
+    CpuHz = CpuHz * 1000000;
+
+    return PJ_SUCCESS;
+}
+
+/* __int64 is nicely returned in EDX:EAX */
+__declspec(naked) __int64 rdtsc() 
+{
+    __asm 
+    {
+	RDTSC
+	RET
+    }
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
+{
+    ts->u64 = rdtsc();
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_get_timestamp_freq(pj_timestamp *freq)
+{
+    pj_status_t status;
+
+    if (CpuHz == 0) {
+	status = GetCpuHz();
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    freq->u64 = CpuHz;
+    return PJ_SUCCESS;
+}
+
+#else
+/*
+ * Use QueryPerformanceCounter and QueryPerformanceFrequency.
+ */
 PJ_DEF(pj_status_t) pj_get_timestamp(pj_timestamp *ts)
 {
     LARGE_INTEGER val;
@@ -42,3 +111,5 @@
     return PJ_SUCCESS;
 }
 
+#endif	/* PJ_TIMESTAMP_USE_RDTSC */
+
diff --git a/pjlib/src/pjlib-test/sleep.c b/pjlib/src/pjlib-test/sleep.c
index 5732a16..2c735a3 100644
--- a/pjlib/src/pjlib-test/sleep.c
+++ b/pjlib/src/pjlib-test/sleep.c
@@ -124,9 +124,10 @@
     {
 	pj_time_val t1, t2;
         pj_timestamp start, stop;
-        pj_time_val elapsed;
 	pj_uint32_t msec;
 
+	pj_thread_sleep(0);
+
         /* Mark start of test. */
         rc = pj_get_timestamp(&start);
         if (rc != PJ_SUCCESS) {
@@ -156,10 +157,8 @@
 	    return -75;
 	}
 
-        /* Get elapsed time in time_val */
-        elapsed = pj_elapsed_time(&start, &stop);
-
-	msec = PJ_TIME_VAL_MSEC(elapsed);
+        /* Get elapsed time in msec */
+        msec = pj_elapsed_msec(&start, &stop);
 
 	/* Check if it's within range. */
 	if (msec < DURATION2 * (100-MIS)/100 ||
@@ -169,7 +168,13 @@
 		      "...error: slept for %d ms instead of %d ms "
 		      "(outside %d%% err window)",
 		      msec, DURATION2, MIS));
-	    return -30;
+	    PJ_TIME_VAL_SUB(t2, t1);
+	    PJ_LOG(3,(THIS_FILE, 
+		      "...info: gettimeofday() reported duration is "
+		      "%d msec",
+		      PJ_TIME_VAL_MSEC(t2)));
+
+	    return -76;
 	}
     }