Skip to content

Commit 9a585f1

Browse files
[GR-44120] Fix various time-related issues.
PullRequest: graal/13806
2 parents 1cbe03e + 6be77ef commit 9a585f1

File tree

24 files changed

+638
-446
lines changed

24 files changed

+638
-446
lines changed

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixUtils.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@
5151
import com.oracle.svm.core.posix.headers.Errno;
5252
import com.oracle.svm.core.posix.headers.Locale;
5353
import com.oracle.svm.core.posix.headers.Signal;
54+
import com.oracle.svm.core.posix.headers.Time;
5455
import com.oracle.svm.core.posix.headers.Unistd;
5556
import com.oracle.svm.core.posix.headers.Wait;
57+
import com.oracle.svm.core.posix.headers.darwin.DarwinTime;
58+
import com.oracle.svm.core.posix.headers.linux.LinuxTime;
5659
import com.oracle.svm.core.util.VMError;
5760

5861
public class PosixUtils {
@@ -270,4 +273,16 @@ public static Signal.SignalDispatcher installSignalHandler(int signum, Signal.Si
270273
}
271274
return old.sa_handler();
272275
}
276+
277+
// Checkstyle: stop
278+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
279+
public static int clock_gettime(int clock_id, Time.timespec ts) {
280+
if (Platform.includedIn(Platform.DARWIN.class)) {
281+
return DarwinTime.NoTransitions.clock_gettime(clock_id, ts);
282+
} else {
283+
assert Platform.includedIn(Platform.LINUX.class);
284+
return LinuxTime.NoTransitions.clock_gettime(clock_id, ts);
285+
}
286+
}
287+
// Checkstyle: resume
273288
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/Target_java_lang_System_Posix.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,14 @@
2727
import java.io.Console;
2828

2929
import org.graalvm.nativeimage.StackValue;
30-
import org.graalvm.word.WordFactory;
3130

31+
import com.oracle.svm.core.Uninterruptible;
3232
import com.oracle.svm.core.annotate.Alias;
3333
import com.oracle.svm.core.annotate.RecomputeFieldValue;
3434
import com.oracle.svm.core.annotate.Substitute;
3535
import com.oracle.svm.core.annotate.TargetClass;
36-
import com.oracle.svm.core.Uninterruptible;
3736
import com.oracle.svm.core.posix.headers.Time;
38-
import com.oracle.svm.core.posix.headers.Time.timeval;
39-
import com.oracle.svm.core.posix.headers.Time.timezone;
37+
import com.oracle.svm.core.util.TimeUtils;
4038

4139
@TargetClass(java.lang.System.class)
4240
final class Target_java_lang_System_Posix {
@@ -47,9 +45,9 @@ final class Target_java_lang_System_Posix {
4745
@Substitute
4846
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
4947
public static long currentTimeMillis() {
50-
timeval timeval = StackValue.get(timeval.class);
51-
timezone timezone = WordFactory.nullPointer();
52-
Time.NoTransitions.gettimeofday(timeval, timezone);
53-
return timeval.tv_sec() * 1_000L + timeval.tv_usec() / 1_000L;
48+
Time.timespec ts = StackValue.get(Time.timespec.class);
49+
int status = PosixUtils.clock_gettime(Time.CLOCK_REALTIME(), ts);
50+
PosixUtils.checkStatusIs0(status, "System.currentTimeMillis(): clock_gettime(CLOCK_REALTIME) failed.");
51+
return ts.tv_sec() * TimeUtils.millisPerSecond + ts.tv_nsec() / TimeUtils.nanosPerMilli;
5452
}
5553
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSubstitutions.java

Lines changed: 65 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,55 +24,30 @@
2424
*/
2525
package com.oracle.svm.core.posix.darwin;
2626

27-
import static com.oracle.svm.core.posix.headers.darwin.DarwinTime.mach_absolute_time;
28-
import static com.oracle.svm.core.posix.headers.darwin.DarwinTime.mach_timebase_info;
27+
import static com.oracle.svm.core.posix.headers.darwin.DarwinTime.NoTransitions.mach_absolute_time;
28+
import static com.oracle.svm.core.posix.headers.darwin.DarwinTime.NoTransitions.mach_timebase_info;
2929

3030
import org.graalvm.nativeimage.ImageSingletons;
31+
import org.graalvm.nativeimage.Platform;
32+
import org.graalvm.nativeimage.Platforms;
3133
import org.graalvm.nativeimage.StackValue;
32-
import org.graalvm.word.WordFactory;
3334

35+
import com.oracle.svm.core.Uninterruptible;
3436
import com.oracle.svm.core.annotate.Substitute;
3537
import com.oracle.svm.core.annotate.TargetClass;
36-
import com.oracle.svm.core.Uninterruptible;
37-
import com.oracle.svm.core.posix.headers.Time;
38-
import com.oracle.svm.core.posix.headers.Time.timeval;
39-
import com.oracle.svm.core.posix.headers.Time.timezone;
40-
import com.oracle.svm.core.posix.headers.darwin.DarwinTime.MachTimebaseInfo;
4138
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
39+
import com.oracle.svm.core.posix.PosixUtils;
40+
import com.oracle.svm.core.posix.headers.darwin.DarwinTime;
41+
42+
import jdk.internal.misc.Unsafe;
4243

4344
@TargetClass(java.lang.System.class)
4445
final class Target_java_lang_System_Darwin {
4546

4647
@Substitute
47-
@Uninterruptible(reason = "Does basic math after a few simple system calls")
48+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
4849
private static long nanoTime() {
49-
final Util_java_lang_System utilJavaLangSystem = ImageSingletons.lookup(Util_java_lang_System.class);
50-
51-
if (utilJavaLangSystem.fastTime) {
52-
return mach_absolute_time();
53-
}
54-
55-
if (!utilJavaLangSystem.timeBaseValid) {
56-
MachTimebaseInfo timeBaseInfo = StackValue.get(MachTimebaseInfo.class);
57-
if (mach_timebase_info(timeBaseInfo) == 0) {
58-
if (timeBaseInfo.getdenom() == 1 && timeBaseInfo.getnumer() == 1) {
59-
utilJavaLangSystem.fastTime = true;
60-
return mach_absolute_time();
61-
}
62-
utilJavaLangSystem.factor = (double) timeBaseInfo.getnumer() / (double) timeBaseInfo.getdenom();
63-
}
64-
utilJavaLangSystem.timeBaseValid = true;
65-
}
66-
67-
if (utilJavaLangSystem.factor != 0) {
68-
return (long) (mach_absolute_time() * utilJavaLangSystem.factor);
69-
}
70-
71-
/* High precision time is not available, fall back to low precision. */
72-
timeval timeval = StackValue.get(timeval.class);
73-
timezone timezone = WordFactory.nullPointer();
74-
Time.NoTransitions.gettimeofday(timeval, timezone);
75-
return timeval.tv_sec() * 1_000_000_000L + timeval.tv_usec() * 1_000L;
50+
return ImageSingletons.lookup(DarwinTimeUtil.class).nanoTime();
7651
}
7752

7853
@Substitute
@@ -83,13 +58,61 @@ public static String mapLibraryName(String libname) {
8358

8459
/** Additional static-like fields for {@link Target_java_lang_System_Darwin}. */
8560
@AutomaticallyRegisteredImageSingleton
86-
final class Util_java_lang_System {
87-
boolean timeBaseValid = false;
88-
boolean fastTime = false;
89-
double factor = 0.0;
61+
final class DarwinTimeUtil {
62+
private static final Unsafe U = Unsafe.getUnsafe();
63+
private static final long INITIALIZED_OFFSET = U.objectFieldOffset(DarwinTimeUtil.class, "initialized");
64+
private static final long MAX_ABS_TIME_OFFSET = U.objectFieldOffset(DarwinTimeUtil.class, "maxAbsTime");
65+
66+
@SuppressWarnings("unused") //
67+
private volatile boolean initialized;
68+
@SuppressWarnings("unused") //
69+
private volatile long maxAbsTime;
70+
private int numer;
71+
private int denom;
72+
73+
@Platforms(Platform.HOSTED_ONLY.class)
74+
DarwinTimeUtil() {
75+
}
9076

91-
Util_java_lang_System() {
92-
/* Nothing to do. */
77+
/**
78+
* Based on HotSpot JDK 19 (git commit hash: 967a28c3d85fdde6d5eb48aa0edd8f7597772469, JDK tag:
79+
* jdk-19+36).
80+
*/
81+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
82+
long nanoTime() {
83+
if (!U.getBooleanAcquire(this, INITIALIZED_OFFSET)) {
84+
/* Can be called by multiple threads but they should all query the same data. */
85+
DarwinTime.MachTimebaseInfo timeBaseInfo = StackValue.get(DarwinTime.MachTimebaseInfo.class);
86+
int status = mach_timebase_info(timeBaseInfo);
87+
PosixUtils.checkStatusIs0(status, "mach_timebase_info() failed.");
88+
89+
numer = timeBaseInfo.getnumer();
90+
denom = timeBaseInfo.getdenom();
91+
/* Ensure that the stores from above are visible when initialized is set to true. */
92+
U.putBooleanRelease(this, INITIALIZED_OFFSET, true);
93+
}
94+
95+
long tm = mach_absolute_time();
96+
long now = (tm * numer) / denom;
97+
long prev = U.getLongOpaque(this, MAX_ABS_TIME_OFFSET);
98+
if (now <= prev) {
99+
return prev; // same or retrograde time;
100+
}
101+
long obsv = U.compareAndExchangeLong(this, MAX_ABS_TIME_OFFSET, prev, now);
102+
assert obsv >= prev : "invariant to ensure monotonicity";
103+
/*
104+
* If the CAS succeeded then we're done and return "now". If the CAS failed and the observed
105+
* value "obsv" is >= now then we should return "obsv". If the CAS failed and now > obsv >
106+
* prv then some other thread raced this thread and installed a new value, in which case we
107+
* could either (a) retry the entire operation, (b) retry trying to install now or (c) just
108+
* return obsv. We use (c). No loop is required although in some cases we might discard a
109+
* higher "now" value in deference to a slightly lower but freshly installed obsv value.
110+
* That's entirely benign -- it admits no new orderings compared to (a) or (b) -- and
111+
* greatly reduces coherence traffic. We might also condition (c) on the magnitude of the
112+
* delta between obsv and now. Avoiding excessive CAS operations to hot RW locations is
113+
* critical. See https://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
114+
*/
115+
return (prev == obsv) ? now : obsv;
93116
}
94117
}
95118

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Pthread.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050
@CContext(PosixDirectives.class)
5151
@CLibrary("pthread")
5252
public class Pthread {
53-
5453
public interface pthread_t extends OSThreadHandle, OSThreadId {
5554
}
5655

@@ -147,6 +146,9 @@ public interface pthread_key_tPointer extends PointerBase {
147146
@CFunction(transition = Transition.NO_TRANSITION)
148147
public static native int pthread_mutex_init(pthread_mutex_t mutex, pthread_mutexattr_t mutexattr);
149148

149+
@CFunction(transition = Transition.NO_TRANSITION)
150+
public static native int pthread_mutex_destroy(pthread_mutex_t mutex);
151+
150152
@CFunction(value = "pthread_mutex_trylock", transition = Transition.NO_TRANSITION)
151153
public static native int pthread_mutex_trylock_no_transition(pthread_mutex_t mutex);
152154

@@ -162,6 +164,9 @@ public interface pthread_key_tPointer extends PointerBase {
162164
@CFunction(transition = Transition.NO_TRANSITION)
163165
public static native int pthread_cond_init(pthread_cond_t cond, pthread_condattr_t cond_attr);
164166

167+
@CFunction(transition = Transition.NO_TRANSITION)
168+
public static native int pthread_cond_destroy(pthread_cond_t cond);
169+
165170
@CFunction(transition = Transition.NO_TRANSITION)
166171
public static native int pthread_cond_signal(pthread_cond_t cond);
167172

@@ -184,7 +189,7 @@ public interface pthread_key_tPointer extends PointerBase {
184189
public static native int pthread_condattr_init(pthread_condattr_t attr);
185190

186191
@CFunction(transition = Transition.NO_TRANSITION)
187-
public static native int pthread_condattr_setclock(pthread_condattr_t attr, int clock_id);
192+
public static native int pthread_condattr_destroy(pthread_condattr_t attr);
188193

189194
@CFunction
190195
public static native int pthread_kill(pthread_t thread, Signal.SignalEnum sig);
@@ -200,4 +205,5 @@ public interface pthread_key_tPointer extends PointerBase {
200205

201206
@CFunction(transition = Transition.NO_TRANSITION)
202207
public static native VoidPointer pthread_getspecific(pthread_key_t key);
208+
203209
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Time.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.core.posix.headers;
2626

2727
import org.graalvm.nativeimage.c.CContext;
28+
import org.graalvm.nativeimage.c.constant.CConstant;
2829
import org.graalvm.nativeimage.c.constant.CEnum;
2930
import org.graalvm.nativeimage.c.constant.CEnumValue;
3031
import org.graalvm.nativeimage.c.function.CFunction;
@@ -101,6 +102,9 @@ public enum TimerTypeEnum {
101102
public native int getCValue();
102103
}
103104

105+
@CConstant
106+
public static native int CLOCK_REALTIME();
107+
104108
public static class NoTransitions {
105109
/**
106110
* @param which from {@link TimerTypeEnum#getCValue()}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinTime.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.graalvm.word.PointerBase;
3232

3333
import com.oracle.svm.core.posix.headers.PosixDirectives;
34+
import com.oracle.svm.core.posix.headers.Time;
3435

3536
// Checkstyle: stop
3637

@@ -47,9 +48,14 @@ public interface MachTimebaseInfo extends PointerBase {
4748
int getdenom();
4849
}
4950

50-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
51-
public static native long mach_absolute_time();
51+
public static class NoTransitions {
52+
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
53+
public static native int clock_gettime(int clock_id, Time.timespec tp);
5254

53-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
54-
public static native int mach_timebase_info(MachTimebaseInfo timeBaseInfo);
55+
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
56+
public static native long mach_absolute_time();
57+
58+
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
59+
public static native int mach_timebase_info(MachTimebaseInfo timeBaseInfo);
60+
}
5561
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxPthread.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
import org.graalvm.nativeimage.c.function.CFunction.Transition;
3030
import org.graalvm.nativeimage.c.function.CLibrary;
3131
import org.graalvm.nativeimage.c.type.CCharPointer;
32+
import org.graalvm.nativeimage.c.type.CIntPointer;
3233

3334
import com.oracle.svm.core.posix.headers.PosixDirectives;
35+
import com.oracle.svm.core.posix.headers.Pthread;
3436
import com.oracle.svm.core.posix.headers.Pthread.pthread_t;
35-
import org.graalvm.nativeimage.c.type.CIntPointer;
3637

3738
// Checkstyle: stop
3839

@@ -45,4 +46,7 @@ public class LinuxPthread {
4546

4647
@CFunction(transition = Transition.NO_TRANSITION)
4748
public static native int pthread_getcpuclockid(pthread_t pthread, CIntPointer clock_id);
49+
50+
@CFunction(transition = Transition.NO_TRANSITION)
51+
public static native int pthread_condattr_setclock(Pthread.pthread_condattr_t attr, int clock_id);
4852
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxTime.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@
3939
*/
4040
@CContext(PosixDirectives.class)
4141
public class LinuxTime extends Time {
42-
4342
@CConstant
4443
public static native int CLOCK_MONOTONIC();
4544

4645
@CConstant
4746
public static native int CLOCK_THREAD_CPUTIME_ID();
4847

49-
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
50-
@CLibrary("rt")
51-
public static native int clock_gettime(int clock_id, timespec tp);
48+
public static class NoTransitions {
49+
/* We still need to support glibc 2.12, where clock_gettime is located in librt. */
50+
@CFunction(transition = CFunction.Transition.NO_TRANSITION)
51+
@CLibrary("rt")
52+
public static native int clock_gettime(int clock_id, timespec tp);
53+
}
5254
}

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSubstitutions.java

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,37 +24,26 @@
2424
*/
2525
package com.oracle.svm.core.posix.linux;
2626

27-
import static com.oracle.svm.core.posix.headers.linux.LinuxTime.CLOCK_MONOTONIC;
28-
import static com.oracle.svm.core.posix.headers.linux.LinuxTime.clock_gettime;
29-
3027
import org.graalvm.nativeimage.StackValue;
31-
import org.graalvm.word.WordFactory;
3228

29+
import com.oracle.svm.core.Uninterruptible;
3330
import com.oracle.svm.core.annotate.Substitute;
3431
import com.oracle.svm.core.annotate.TargetClass;
35-
import com.oracle.svm.core.Uninterruptible;
32+
import com.oracle.svm.core.posix.PosixUtils;
3633
import com.oracle.svm.core.posix.headers.Time;
37-
import com.oracle.svm.core.posix.headers.Time.timespec;
38-
import com.oracle.svm.core.posix.headers.Time.timeval;
39-
import com.oracle.svm.core.posix.headers.Time.timezone;
34+
import com.oracle.svm.core.posix.headers.linux.LinuxTime;
35+
import com.oracle.svm.core.util.TimeUtils;
4036

4137
@TargetClass(java.lang.System.class)
4238
final class Target_java_lang_System_Linux {
4339

4440
@Substitute
4541
@Uninterruptible(reason = "Does basic math after a simple system call")
4642
private static long nanoTime() {
47-
timespec timespec = StackValue.get(timespec.class);
48-
if (clock_gettime(CLOCK_MONOTONIC(), timespec) == 0) {
49-
return timespec.tv_sec() * 1_000_000_000L + timespec.tv_nsec();
50-
51-
} else {
52-
/* High precision time is not available, fall back to low precision. */
53-
timeval timeval = StackValue.get(timeval.class);
54-
timezone timezone = WordFactory.nullPointer();
55-
Time.NoTransitions.gettimeofday(timeval, timezone);
56-
return timeval.tv_sec() * 1_000_000_000L + timeval.tv_usec() * 1_000L;
57-
}
43+
Time.timespec tp = StackValue.get(Time.timespec.class);
44+
int status = LinuxTime.NoTransitions.clock_gettime(LinuxTime.CLOCK_MONOTONIC(), tp);
45+
PosixUtils.checkStatusIs0(status, "System.nanoTime(): clock_gettime(CLOCK_MONOTONIC) failed.");
46+
return tp.tv_sec() * TimeUtils.nanosPerSecond + tp.tv_nsec();
5847
}
5948

6049
@Substitute

substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxThreadCpuTimeSupport.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.oracle.svm.core.posix.headers.linux.LinuxTime;
3737
import com.oracle.svm.core.thread.ThreadCpuTimeSupport;
3838
import com.oracle.svm.core.thread.VMThreads.OSThreadHandle;
39+
import com.oracle.svm.core.util.TimeUtils;
3940

4041
@AutomaticallyRegisteredImageSingleton(ThreadCpuTimeSupport.class)
4142
final class LinuxThreadCpuTimeSupport implements ThreadCpuTimeSupport {
@@ -73,9 +74,9 @@ public long getThreadCpuTime(OSThreadHandle osThreadHandle, boolean includeSyste
7374
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
7475
private static long getThreadCpuTimeImpl(int clockId) {
7576
timespec time = UnsafeStackValue.get(timespec.class);
76-
if (LinuxTime.clock_gettime(clockId, time) != 0) {
77+
if (LinuxTime.NoTransitions.clock_gettime(clockId, time) != 0) {
7778
return -1;
7879
}
79-
return time.tv_sec() * 1_000_000_000 + time.tv_nsec();
80+
return time.tv_sec() * TimeUtils.nanosPerSecond + time.tv_nsec();
8081
}
8182
}

0 commit comments

Comments
 (0)