Hi ruby-core.
here I am again, with a new revision of my monotonic patch. I still
hope that one day it will be integrated :)
= Problem
This is a subtle problem that is mostly problematic on long running
processes. If the computer's system clock is set back for a certain
delta, the scheduled threads will wait for that delta time + their
scheduled time before being activated again. This is due to the fact
that they uses the epoch-based gettimeofday() time source.
= Solution
The general solution is to use a monotonic clock because monotonicity
is the guarantee that you won't go back in time. Those clocks are not
epoch-based but it doesn't matter because thread scheduling is only
using deltas of times.
The implemented solution uses the POSIX clock_gettime function, which
has a CLOCK_MONOTONIC flag. clock_gettime(), on most BSD, should be in
the libc. On linux, it is required to link ruby against librt.so,
which is part of the libc6 package. The Darwin (OS X) kernel does not
implement the clock_gettime function. Since clock_gettime() doesn't
guarantee you that your system provides such clock, I also had to add
an init function to check the clock availability, plus the
gettimeofday() fallback.
= Patch
Index: eval.c
===================================================================
--- eval.c (revision 16252)
+++ eval.c (working copy)
@@ -28,6 +28,12 @@
#define EXIT_FAILURE 1
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#endif
+
#include <stdio.h>
#include "st.h"
@@ -1340,6 +1346,7 @@
void Init_stack _((VALUE*));
void Init_heap _((void));
void Init_ext _((void));
+void Init_clock _((void));
#ifdef HAVE_NATIVETHREAD
static rb_nativethread_t ruby_thid;
@@ -1382,6 +1389,7 @@
rb_origenviron = environ;
#endif
+ Init_clock();
Init_stack((void*)&state);
Init_heap();
PUSH_SCOPE();
@@ -10245,13 +10253,32 @@
curr_thread->safe = level;
}
+static int clock_monotonic = 0;
+
+void
+Init_clock()
+{
+#ifdef CLOCK_MONOTONIC
+ struct timespec tp;
+ clock_monotonic = (clock_gettime(CLOCK_MONOTONIC, &tp) == 0);
+#endif
+}
+
/* Return the current time as a floating-point number */
static double
timeofday()
{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
+ if (clock_monotonic) {
+#ifdef CLOCK_MONOTONIC
+ struct timespec tp;
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+ return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9;
+#endif
+ } else {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
+ }
}
#define STACK(addr) (th->stk_pos<(VALUE*)(addr) &&
(VALUE*)(addr)<th->stk_pos+th->stk_len)
= QA
Q: on what branch is your code based ?
A: I used the ruby_1_8 branch
Q: timeofday() might be used by some code that needs epoch-based time ?
A: I have grep'ed trough the code but might have missed something
Q: is there a similar solution for windows ?
A: I don't know any Windows API that guarantees clock monotonicity.
Q: why didn't you rename timeofday() ?
A: It would hide the purpose of the patch. It is a good idea although.
Q: how do I know if my ruby instance uses the monotonic clock ? (like
I'm running a rails application on a vserver)
A: use "strace" :) . I don't think there is a Scheduled class where I
could publish that information.
on 01.05.2008 19:46
on 01.05.2008 20:27
On Fri, 2 May 2008 02:45:55 +0900, zimbatm <zimbatm@oree.ch> wrote: > Q: timeofday() might be used by some code that needs epoch-based time ? > A: I have grep'ed trough the code but might have missed something So far as I can tell, the places which need epoch-based time (like time.c) use gettimeofday() directly, so there's no problem. > Q: is there a similar solution for windows ? > A: I don't know any Windows API that guarantees clock monotonicity. The Windows API functions for monotonic time are GetTickCount and GetTickCount64. The latter is preferable since the former wraps around its 32-bit count every 50 days or so. -mental
on 01.05.2008 20:33
On Fri, 2 May 2008 03:26:53 +0900, MenTaLguY <mental@rydia.net> wrote: > The Windows API functions for monotonic time are GetTickCount and > GetTickCount64. The latter is preferable since the former wraps around > its 32-bit count every 50 days or so. (GetTickCount64 is specific to Windows Vista, though -- I wouldn't worry about Windows just yet, let's make sure that we have POSIX systems fixed first) -mental
on 01.05.2008 20:35
2008/5/1 MenTaLguY <mental@rydia.net>: > On Fri, 2 May 2008 02:45:55 +0900, zimbatm <zimbatm@oree.ch> wrote: > > Q: timeofday() might be used by some code that needs epoch-based time ? > > A: I have grep'ed trough the code but might have missed something > > So far as I can tell, the places which need epoch-based time (like time.c) > use gettimeofday() directly, so there's no problem. Yes, this is also my belief. Another confirmation is that timeofday() has even moved in thread.c in ruby1.9