mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-08 03:44:22 -04:00
Implement fast/precise monotonic clocks on Windows
This uses code from libutp, which was released under the MIT license; see evutil_time.c and LICENSE changes.
This commit is contained in:
parent
a2598ec6bd
commit
2c470452fb
25
LICENSE
25
LICENSE
@ -72,3 +72,28 @@ The arc4module is available under the following, sometimes called the
|
|||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
==============================
|
||||||
|
|
||||||
|
The Windows timer code is based on code from libutp, which is
|
||||||
|
distributed under this license, sometimes called the "MIT" license.
|
||||||
|
|
||||||
|
|
||||||
|
Copyright (c) 2010 BitTorrent, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
155
evutil_time.c
155
evutil_time.c
@ -133,7 +133,6 @@ evutil_usleep_(const struct timeval *tv)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function assumes it's called repeatedly with a
|
This function assumes it's called repeatedly with a
|
||||||
not-actually-so-monotonic time source whose outputs are in 'tv'. It
|
not-actually-so-monotonic time source whose outputs are in 'tv'. It
|
||||||
@ -283,57 +282,163 @@ evutil_gettime_monotonic_(struct evutil_monotonic_timer *base,
|
|||||||
|
|
||||||
Windows has QueryPerformanceCounter(), which gives time most high-
|
Windows has QueryPerformanceCounter(), which gives time most high-
|
||||||
resolution time. It's a pity it's not so monotonic in practice; it's
|
resolution time. It's a pity it's not so monotonic in practice; it's
|
||||||
also got some fun bugs, especially with older Windowses, under
|
also got some fun bugs, especially: with older Windowses, under
|
||||||
virtualizations, with funny hardware, on multiprocessor systems, and so
|
virtualizations, with funny hardware, on multiprocessor systems, and so
|
||||||
on. PEP418 [1] has a nice roundup here.
|
on. PEP418 [1] has a nice roundup of the issues here.
|
||||||
|
|
||||||
There's GetTickCount64(), which gives a number of 1-msec ticks since
|
There's GetTickCount64() on Vista and later, which gives a number of 1-msec
|
||||||
startup. The accuracy here might be as bad as 10-20 msec, I hear.
|
ticks since startup. The accuracy here might be as bad as 10-20 msec, I
|
||||||
There's an undocumented function (NtSetTimerResolution) that allegedly
|
hear. There's an undocumented function (NtSetTimerResolution) that
|
||||||
increases the accuracy. Good luck!
|
allegedly increases the accuracy. Good luck!
|
||||||
|
|
||||||
There's also GetTickCount(), which is only 32 bits, but seems to be
|
There's also GetTickCount(), which is only 32 bits, but seems to be
|
||||||
supported on pre-Vista versions of Windows.
|
supported on pre-Vista versions of Windows. Apparently, you can coax
|
||||||
|
another 14 bits out of it, giving you 2231 years before rollover.
|
||||||
|
|
||||||
The less said about timeGetTime() the better.
|
The less said about timeGetTime() the better.
|
||||||
|
|
||||||
"We don't care. We don't have to. We're the Phone Company."
|
"We don't care. We don't have to. We're the Phone Company."
|
||||||
-- Lily Tomlin, SNL
|
-- Lily Tomlin, SNL
|
||||||
|
|
||||||
|
Our strategy, if precise timers are turned off, is to just use the best
|
||||||
|
GetTickCount equivalent available. If we've been asked for precise timing,
|
||||||
|
then we mostly[2] assume that GetTickCount is monotonic, and correct
|
||||||
|
GetPerformanceCounter to approximate it.
|
||||||
|
|
||||||
[1] http://www.python.org/dev/peps/pep-0418
|
[1] http://www.python.org/dev/peps/pep-0418
|
||||||
|
[2] Of course, we feed the Windows stuff into adjust_monotonic_time()
|
||||||
|
anyway, just in case it isn't.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
Parts of our logic in the win32 timer code here are closely based on
|
||||||
|
BitTorrent's libUTP library. That code is subject to the following
|
||||||
|
license:
|
||||||
|
|
||||||
|
Copyright (c) 2010 BitTorrent, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static ev_uint64_t
|
||||||
|
evutil_GetTickCount_(struct evutil_monotonic_timer *base)
|
||||||
|
{
|
||||||
|
if (base->GetTickCount64_fn) {
|
||||||
|
/* Let's just use GetTickCount64 if we can. */
|
||||||
|
return base->GetTickCount64_fn();
|
||||||
|
} else if (base->GetTickCount_fn) {
|
||||||
|
/* Greg Hazel assures me that this works, that BitTorrent has
|
||||||
|
* done it for years, and this it won't turn around and
|
||||||
|
* bite us. He says they found it on some game programmers'
|
||||||
|
* forum some time around 2007.
|
||||||
|
*/
|
||||||
|
ev_uint64_t v = base->GetTickCount_fn();
|
||||||
|
return (DWORD)v | ((v >> 18) & 0xFFFFFFFF00000000);
|
||||||
|
} else {
|
||||||
|
/* Here's the fallback implementation. We have to use
|
||||||
|
* GetTickCount() with its given signature, so we only get
|
||||||
|
* 32 bits worth of milliseconds, which will roll ove every
|
||||||
|
* 49 days or so. */
|
||||||
|
DWORD ticks = GetTickCount();
|
||||||
|
if (ticks < base->last_tick_count) {
|
||||||
|
base->adjust_tick_count += ((ev_uint64_t)1) << 32;
|
||||||
|
}
|
||||||
|
base->last_tick_count = ticks;
|
||||||
|
return ticks + base->adjust_tick_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base,
|
evutil_configure_monotonic_time_(struct evutil_monotonic_timer *base,
|
||||||
int precise)
|
int precise)
|
||||||
{
|
{
|
||||||
|
HANDLE h;
|
||||||
memset(base, 0, sizeof(*base));
|
memset(base, 0, sizeof(*base));
|
||||||
base->last_tick_count = GetTickCount();
|
|
||||||
|
h = evutil_load_windows_system_library_(TEXT("kernel32.dll"));
|
||||||
|
if (h != NULL) {
|
||||||
|
base->GetTickCount64_fn = (ev_GetTickCount_func)GetProcAddress(h, "GetTickCount64");
|
||||||
|
base->GetTickCount_fn = (ev_GetTickCount_func)GetProcAddress(h, "GetTickCount");
|
||||||
|
}
|
||||||
|
|
||||||
|
base->first_tick = base->last_tick_count = evutil_GetTickCount_(base);
|
||||||
|
if (precise) {
|
||||||
|
LARGE_INTEGER freq;
|
||||||
|
if (QueryPerformanceFrequency(&freq)) {
|
||||||
|
LARGE_INTEGER counter;
|
||||||
|
QueryPerformanceCounter(&counter);
|
||||||
|
base->first_counter = counter.QuadPart;
|
||||||
|
base->usec_per_count = 1.0e6 / freq.QuadPart;
|
||||||
|
base->use_performance_counter = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline ev_int64_t
|
||||||
|
abs64(ev_int64_t i)
|
||||||
|
{
|
||||||
|
return i < 0 ? -i : i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
evutil_gettime_monotonic_(struct evutil_monotonic_timer *base,
|
evutil_gettime_monotonic_(struct evutil_monotonic_timer *base,
|
||||||
struct timeval *tp)
|
struct timeval *tp)
|
||||||
{
|
{
|
||||||
/* TODO: Support GetTickCount64. */
|
ev_uint64_t ticks = evutil_GetTickCount_(base);
|
||||||
/* TODO: Support alternate timer backends if the user asked
|
if (base->use_performance_counter) {
|
||||||
* for a high-precision timer. QueryPerformanceCounter is
|
/* Here's a trick we took from BitTorrent's libutp, at Greg
|
||||||
* possibly a good idea, but it is also supposed to have
|
* Hazel's recommendation. We use QueryPerformanceCounter for
|
||||||
* reliability issues under various circumstances. */
|
* our high-resolution timer, but use GetTickCount*() to keep
|
||||||
DWORD ticks = GetTickCount();
|
* it sane, and adjust_monotonic_time() to keep it monotonic.
|
||||||
if (ticks < base->last_tick_count) {
|
*/
|
||||||
/* The 32-bit timer rolled over. Let's assume it only
|
LARGE_INTEGER counter;
|
||||||
* happened once. Add 2**32 msec to adjust_tick_count. */
|
ev_int64_t counter_elapsed, counter_usec_elapsed, ticks_elapsed;
|
||||||
const struct timeval tv_rollover = { 4294967, 296000 };
|
QueryPerformanceCounter(&counter);
|
||||||
evutil_timeradd(&tv_rollover, &base->adjust_tick_count, &base->adjust_tick_count);
|
counter_elapsed = (ev_int64_t)
|
||||||
}
|
(counter.QuadPart - base->first_counter);
|
||||||
base->last_tick_count = ticks;
|
ticks_elapsed = ticks - base->first_tick;
|
||||||
tp->tv_sec = ticks / 1000;
|
/* TODO: This may upset VC6. If you need this to work with
|
||||||
tp->tv_usec = (ticks % 1000) * 1000;
|
* VC6, please supply an appropriate patch. */
|
||||||
evutil_timeradd(tp, &base->adjust_tick_count, tp);
|
counter_usec_elapsed = (ev_int64_t)
|
||||||
|
(counter_elapsed * base->usec_per_count);
|
||||||
|
|
||||||
|
if (abs64(ticks_elapsed*1000 - counter_usec_elapsed) > 1000000) {
|
||||||
|
/* It appears that the QueryPerformanceCounter()
|
||||||
|
* result is more than 1 second away from
|
||||||
|
* GetTickCount() result. Let's adjust it to be as
|
||||||
|
* accurate as we can; adjust_monotnonic_time() below
|
||||||
|
* will keep it monotonic. */
|
||||||
|
counter_usec_elapsed = ticks_elapsed * 1000;
|
||||||
|
base->first_counter = counter.QuadPart - counter_usec_elapsed / base->usec_per_count;
|
||||||
|
}
|
||||||
|
tp->tv_sec = counter_usec_elapsed / 1000000;
|
||||||
|
tp->tv_usec = counter_usec_elapsed % 1000000;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* We're just using GetTickCount(). */
|
||||||
|
tp->tv_sec = ticks / 1000;
|
||||||
|
tp->tv_usec = (ticks % 1000) * 1000;
|
||||||
|
}
|
||||||
adjust_monotonic_time(base, tp);
|
adjust_monotonic_time(base, tp);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -56,6 +56,10 @@ extern "C" {
|
|||||||
long evutil_tv_to_msec_(const struct timeval *tv);
|
long evutil_tv_to_msec_(const struct timeval *tv);
|
||||||
void evutil_usleep_(const struct timeval *tv);
|
void evutil_usleep_(const struct timeval *tv);
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
typedef ULONGLONG (WINAPI *ev_GetTickCount_func)(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
struct evutil_monotonic_timer {
|
struct evutil_monotonic_timer {
|
||||||
|
|
||||||
#ifdef HAVE_MACH_MONOTONIC
|
#ifdef HAVE_MACH_MONOTONIC
|
||||||
@ -67,8 +71,15 @@ struct evutil_monotonic_timer {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef HAVE_WIN32_MONOTONIC
|
#ifdef HAVE_WIN32_MONOTONIC
|
||||||
DWORD last_tick_count;
|
ev_GetTickCount_func GetTickCount64_fn;
|
||||||
struct timeval adjust_tick_count;
|
ev_GetTickCount_func GetTickCount_fn;
|
||||||
|
ev_uint64_t last_tick_count;
|
||||||
|
ev_uint64_t adjust_tick_count;
|
||||||
|
|
||||||
|
ev_uint64_t first_tick;
|
||||||
|
ev_uint64_t first_counter;
|
||||||
|
double usec_per_count;
|
||||||
|
int use_performance_counter;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct timeval adjust_monotonic_clock;
|
struct timeval adjust_monotonic_clock;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user