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() { _setupHooks() {
VMLibraryHooks.eventHandlerSendData = _EventHandler._sendData; 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); 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 bin
} // namespace dart } // namespace dart

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -71,17 +71,21 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false; return false;
} }
int64_t TimerUtils::GetCurrentTimeMilliseconds() { int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentTimeMicros() / 1000; return GetCurrentMonotonicMicros() / 1000;
} }
int64_t TimerUtils::GetCurrentTimeMicros() { int64_t TimerUtils::GetCurrentMonotonicMicros() {
struct timeval tv; struct timespec ts;
if (gettimeofday(&tv, NULL) < 0) { if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
UNREACHABLE(); UNREACHABLE();
return 0; 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) { void TimerUtils::Sleep(int64_t millis) {

View file

@ -69,17 +69,21 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false; return false;
} }
int64_t TimerUtils::GetCurrentTimeMilliseconds() { int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentTimeMicros() / 1000; return GetCurrentMonotonicMicros() / 1000;
} }
int64_t TimerUtils::GetCurrentTimeMicros() { int64_t TimerUtils::GetCurrentMonotonicMicros() {
struct timeval tv; struct timespec ts;
if (gettimeofday(&tv, NULL) < 0) { if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
UNREACHABLE(); UNREACHABLE();
return 0; 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) { void TimerUtils::Sleep(int64_t millis) {

View file

@ -7,6 +7,9 @@
#include <errno.h> // NOLINT #include <errno.h> // NOLINT
#include <netdb.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 <sys/time.h> // NOLINT
#include <time.h> // NOLINT #include <time.h> // NOLINT
@ -71,17 +74,44 @@ bool ShellUtils::GetUtf8Argv(int argc, char** argv) {
return false; return false;
} }
int64_t TimerUtils::GetCurrentTimeMilliseconds() { int64_t TimerUtils::GetCurrentMonotonicMillis() {
return GetCurrentTimeMicros() / 1000; return GetCurrentMonotonicMicros() / 1000;
} }
int64_t TimerUtils::GetCurrentTimeMicros() { int64_t TimerUtils::GetCurrentMonotonicMicros() {
struct timeval tv; #if TARGET_OS_IOS
if (gettimeofday(&tv, NULL) < 0) { // On iOS mach_absolute_time stops while the device is sleeping. Instead use
UNREACHABLE(); // now - KERN_BOOTTIME to get a time difference that is not impacted by clock
return 0; // 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) { void TimerUtils::Sleep(int64_t millis) {

View file

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

View file

@ -11,10 +11,17 @@ patch List makeFixedListUnmodifiable(List fixedLengthList)
class VMLibraryHooks { class VMLibraryHooks {
// Example: "dart:isolate _Timer._factory" // Example: "dart:isolate _Timer._factory"
static var timerFactory; static var timerFactory;
// Example: "dart:io _EventHandler._sendData" // Example: "dart:io _EventHandler._sendData"
static var eventHandlerSendData; 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. // Implementation of Resource.readAsBytes.
static var resourceReadAsBytes; static var resourceReadAsBytes;
// Implementation of package root/map provision. // Implementation of package root/map provision.
static var getPackageRoot; static var getPackageRoot;
static var getPackageMap; static var getPackageMap;

View file

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