Use a monotonic clock in the implementation of Timer.

Ask the embedder for the current time, since it is the embedder who later compares the deadline with current time when deciding when to send a wake up message.

BUG=http://dartbug.com/25055
BUG=http://dartbug.com/25216
R=iposva@google.com, zra@google.com

Review URL: https://codereview.chromium.org/1519563003 .
This commit is contained in:
Ryan Macnak 2015-12-11 12:39:56 -08:00
parent 04e3f8520c
commit 9387fead15
14 changed files with 117 additions and 48 deletions

View file

@ -9,4 +9,5 @@ patch class _IOCrypto {
_setupHooks() {
VMLibraryHooks.eventHandlerSendData = _EventHandler._sendData;
VMLibraryHooks.timerMillisecondClock = _EventHandler._timerMillisecondClock;
}

View file

@ -131,5 +131,12 @@ void FUNCTION_NAME(EventHandler_SendData)(Dart_NativeArguments args) {
event_handler->SendData(id, dart_port, data);
}
void FUNCTION_NAME(EventHandler_TimerMillisecondClock)(
Dart_NativeArguments args) {
int64_t now = TimerUtils::GetCurrentMonotonicMillis();
Dart_SetReturnValue(args, Dart_NewInteger(now));
}
} // namespace bin
} // namespace dart

View file

@ -333,7 +333,7 @@ int64_t EventHandlerImplementation::GetTimeout() {
return kInfinityTimeout;
}
int64_t millis = timeout_queue_.CurrentTimeout() -
TimerUtils::GetCurrentTimeMilliseconds();
TimerUtils::GetCurrentMonotonicMillis();
return (millis < 0) ? 0 : millis;
}
@ -341,7 +341,7 @@ int64_t EventHandlerImplementation::GetTimeout() {
void EventHandlerImplementation::HandleTimeout() {
if (timeout_queue_.HasTimeout()) {
int64_t millis = timeout_queue_.CurrentTimeout() -
TimerUtils::GetCurrentTimeMilliseconds();
TimerUtils::GetCurrentMonotonicMillis();
if (millis <= 0) {
DartUtils::PostNull(timeout_queue_.CurrentPort());
timeout_queue_.RemoveCurrent();

View file

@ -394,7 +394,7 @@ int64_t EventHandlerImplementation::GetTimeout() {
return kInfinityTimeout;
}
int64_t millis = timeout_queue_.CurrentTimeout() -
TimerUtils::GetCurrentTimeMilliseconds();
TimerUtils::GetCurrentMonotonicMillis();
return (millis < 0) ? 0 : millis;
}
@ -402,7 +402,7 @@ int64_t EventHandlerImplementation::GetTimeout() {
void EventHandlerImplementation::HandleTimeout() {
if (timeout_queue_.HasTimeout()) {
int64_t millis = timeout_queue_.CurrentTimeout() -
TimerUtils::GetCurrentTimeMilliseconds();
TimerUtils::GetCurrentMonotonicMillis();
if (millis <= 0) {
DartUtils::PostNull(timeout_queue_.CurrentPort());
timeout_queue_.RemoveCurrent();

View file

@ -9,5 +9,8 @@ patch class _EventHandler {
SendPort sendPort,
int data)
native "EventHandler_SendData";
static int _timerMillisecondClock()
native "EventHandler_TimerMillisecondClock";
}

View file

@ -1352,7 +1352,7 @@ int64_t EventHandlerImplementation::GetTimeout() {
return kInfinityTimeout;
}
int64_t millis = timeout_queue_.CurrentTimeout() -
TimerUtils::GetCurrentTimeMilliseconds();
TimerUtils::GetCurrentMonotonicMillis();
return (millis < 0) ? 0 : millis;
}

View file

@ -31,6 +31,7 @@ namespace bin {
V(Directory_Rename, 2) \
V(Directory_List, 3) \
V(EventHandler_SendData, 3) \
V(EventHandler_TimerMillisecondClock, 0) \
V(File_Open, 2) \
V(File_Exists, 1) \
V(File_GetFD, 1) \

View file

@ -98,8 +98,8 @@ class ShellUtils {
class TimerUtils {
public:
static int64_t GetCurrentTimeMicros();
static int64_t GetCurrentTimeMilliseconds();
static int64_t GetCurrentMonotonicMicros();
static int64_t GetCurrentMonotonicMillis();
static void Sleep(int64_t millis);
};

View file

@ -71,17 +71,21 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false;
}
int64_t TimerUtils::GetCurrentTimeMilliseconds() {
return GetCurrentTimeMicros() / 1000;
int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentMonotonicMicros() / 1000;
}
int64_t TimerUtils::GetCurrentTimeMicros() {
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) {
int64_t TimerUtils::GetCurrentMonotonicMicros() {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
UNREACHABLE();
return 0;
}
return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
// Convert to microseconds.
int64_t result = ts.tv_sec;
result *= kMicrosecondsPerSecond;
result += (ts.tv_nsec / kNanosecondsPerMicrosecond);
return result;
}
void TimerUtils::Sleep(int64_t millis) {

View file

@ -69,17 +69,21 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false;
}
int64_t TimerUtils::GetCurrentTimeMilliseconds() {
return GetCurrentTimeMicros() / 1000;
int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentMonotonicMicros() / 1000;
}
int64_t TimerUtils::GetCurrentTimeMicros() {
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) {
int64_t TimerUtils::GetCurrentMonotonicMicros() {
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
UNREACHABLE();
return 0;
}
return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
// Convert to microseconds.
int64_t result = ts.tv_sec;
result *= kMicrosecondsPerSecond;
result += (ts.tv_nsec / kNanosecondsPerMicrosecond);
return result;
}
void TimerUtils::Sleep(int64_t millis) {

View file

@ -7,6 +7,9 @@
#include <errno.h> // NOLINT
#include <netdb.h> // NOLINT
#include <mach/mach.h> // NOLINT
#include <mach/clock.h> // NOLINT
#include <mach/mach_time.h> // NOLINT
#include <sys/time.h> // NOLINT
#include <time.h> // NOLINT
@ -71,17 +74,44 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false;
}
int64_t TimerUtils::GetCurrentTimeMilliseconds() {
return GetCurrentTimeMicros() / 1000;
int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentMonotonicMicros() / 1000;
}
int64_t TimerUtils::GetCurrentTimeMicros() {
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) {
UNREACHABLE();
return 0;
int64_t TimerUtils::GetCurrentMonotonicMicros() {
#if TARGET_OS_IOS
// On iOS mach_absolute_time stops while the device is sleeping. Instead use
// now - KERN_BOOTTIME to get a time difference that is not impacted by clock
// changes. KERN_BOOTTIME will be updated by the system whenever the system
// clock change.
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
int kr = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &boottime, &size, NULL, 0);
ASSERT(KERN_SUCCESS == kr);
int64_t now = GetCurrentTimeMicros();
int64_t origin = boottime.tv_sec * kMicrosecondsPerSecond;
origin += boottime.tv_usec;
return now - origin;
#else
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0) {
// Zero-initialization of statics guarantees that denom will be 0 before
// calling mach_timebase_info. mach_timebase_info will never set denom to
// 0 as that would be invalid, so the zero-check can be used to determine
// whether mach_timebase_info has already been called. This is
// recommended by Apple's QA1398.
kern_return_t kr = mach_timebase_info(&timebase_info);
ASSERT(KERN_SUCCESS == kr);
}
return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
// timebase_info converts absolute time tick units into nanoseconds. Convert
// to microseconds.
int64_t result = mach_absolute_time() / kNanosecondsPerMicrosecond;
result *= timebase_info.numer;
result /= timebase_info.denom;
return result;
#endif // TARGET_OS_IOS
}
void TimerUtils::Sleep(int64_t millis) {

View file

@ -168,27 +168,39 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return true;
}
int64_t TimerUtils::GetCurrentTimeMilliseconds() {
return GetCurrentTimeMicros() / 1000;
int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentMonotonicMicros() / 1000;
}
int64_t TimerUtils::GetCurrentTimeMicros() {
static const int64_t kTimeEpoc = 116444736000000000LL;
static const int64_t kTimeScaler = 10; // 100 ns to us.
static int64_t qpc_ticks_per_second = 0;
// Although win32 uses 64-bit integers for representing timestamps,
// these are packed into a FILETIME structure. The FILETIME
// structure is just a struct representing a 64-bit integer. The
// TimeStamp union allows access to both a FILETIME and an integer
// representation of the timestamp. The Windows timestamp is in
// 100-nanosecond intervals since January 1, 1601.
union TimeStamp {
FILETIME ft_;
int64_t t_;
};
TimeStamp time;
GetSystemTimeAsFileTime(&time.ft_);
return (time.t_ - kTimeEpoc) / kTimeScaler;
int64_t TimerUtils::GetCurrentMonotonicMicros() {
if (qpc_ticks_per_second == 0) {
// QueryPerformanceCounter not supported, fallback.
return GetCurrentTimeMicros();
}
// Grab performance counter value.
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
int64_t qpc_value = static_cast<int64_t>(now.QuadPart);
// Convert to microseconds.
int64_t seconds = qpc_value / qpc_ticks_per_second;
int64_t leftover_ticks = qpc_value - (seconds * qpc_ticks_per_second);
int64_t result = seconds * kMicrosecondsPerSecond;
result += ((leftover_ticks * kMicrosecondsPerSecond) / qpc_ticks_per_second);
return result;
}
void* OS::AlignedAllocate(intptr_t size, intptr_t alignment) {
const int kMinimumAlignment = 16;
ASSERT(Utils::IsPowerOfTwo(alignment));
ASSERT(alignment >= kMinimumAlignment);
void* p = _aligned_malloc(size, alignment);
if (p == NULL) {
UNREACHABLE();
}
return p;
}
void TimerUtils::Sleep(int64_t millis) {

View file

@ -11,10 +11,17 @@ patch List makeFixedListUnmodifiable(List fixedLengthList)
class VMLibraryHooks {
// Example: "dart:isolate _Timer._factory"
static var timerFactory;
// Example: "dart:io _EventHandler._sendData"
static var eventHandlerSendData;
// A nullary closure that answers the current clock value in milliseconds.
// Example: "dart:io _EventHandler._timerMillisecondClock"
static var timerMillisecondClock;
// Implementation of Resource.readAsBytes.
static var resourceReadAsBytes;
// Implementation of package root/map provision.
static var getPackageRoot;
static var getPackageMap;

View file

@ -172,7 +172,7 @@ class _Timer implements Timer {
// to nearest millisecond, not up, so that time + duration is before
// duration milliseconds from now. Using microsecond timers like
// Stopwatch allows detecting that the timer fires early.
int now = new DateTime.now().millisecondsSinceEpoch;
int now = VMLibraryHooks.timerMillisecondClock();
int wakeupTime = (milliSeconds == 0) ? now : (now + 1 + milliSeconds);
_Timer timer = new _Timer._internal(callback,
@ -232,7 +232,7 @@ class _Timer implements Timer {
if (_milliSeconds > 0) {
_wakeupTime += _milliSeconds;
} else {
_wakeupTime = new DateTime.now().millisecondsSinceEpoch;
_wakeupTime = VMLibraryHooks.timerMillisecondClock();
}
}
@ -340,7 +340,7 @@ class _Timer implements Timer {
} else {
// Collect pending timers from the timer heap which have expired at this
// time.
var currentTime = new DateTime.now().millisecondsSinceEpoch;
var currentTime = VMLibraryHooks.timerMillisecondClock();
var timer;
while (!_heap.isEmpty && (_heap.first._wakeupTime <= currentTime)) {
timer = _heap.removeFirst();