Skip to content

Commit 9176b55

Browse files
authored
core: Make timestamp usage in Channelz use nanos from Java.time.Instant when available (#11604)
When java.time.Instant is available use the timestamp from this class in nano precision rather than using System.currentTimeInMillis and converting it to nanos. Fixes #5494.
1 parent 735b3f3 commit 9176b55

File tree

6 files changed

+217
-8
lines changed

6 files changed

+217
-8
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import java.util.concurrent.TimeUnit;
20+
21+
/**
22+
* {@link ConcurrentTimeProvider} resolves ConcurrentTimeProvider which implements
23+
* {@link TimeProvider}.
24+
*/
25+
26+
final class ConcurrentTimeProvider implements TimeProvider {
27+
28+
@Override
29+
public long currentTimeNanos() {
30+
return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
31+
}
32+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import static com.google.common.math.LongMath.saturatedAdd;
20+
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.lang.reflect.Method;
23+
import java.util.concurrent.TimeUnit;
24+
25+
/**
26+
* {@link InstantTimeProvider} resolves InstantTimeProvider which implements {@link TimeProvider}.
27+
*/
28+
final class InstantTimeProvider implements TimeProvider {
29+
private Method now;
30+
private Method getNano;
31+
private Method getEpochSecond;
32+
33+
public InstantTimeProvider(Class<?> instantClass) {
34+
try {
35+
this.now = instantClass.getMethod("now");
36+
this.getNano = instantClass.getMethod("getNano");
37+
this.getEpochSecond = instantClass.getMethod("getEpochSecond");
38+
} catch (NoSuchMethodException ex) {
39+
throw new AssertionError(ex);
40+
}
41+
}
42+
43+
@Override
44+
public long currentTimeNanos() {
45+
try {
46+
Object instant = now.invoke(null);
47+
int nanos = (int) getNano.invoke(instant);
48+
long epochSeconds = (long) getEpochSecond.invoke(instant);
49+
return saturatedAdd(TimeUnit.SECONDS.toNanos(epochSeconds), nanos);
50+
} catch (IllegalAccessException | InvocationTargetException ex) {
51+
throw new RuntimeException(ex);
52+
}
53+
}
54+
}

core/src/main/java/io/grpc/internal/TimeProvider.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package io.grpc.internal;
1818

19-
import java.util.concurrent.TimeUnit;
20-
2119
/**
2220
* Time source representing the current system time in nanos. Used to inject a fake clock
2321
* into unit tests.
@@ -26,10 +24,5 @@ public interface TimeProvider {
2624
/** Returns the current nano time. */
2725
long currentTimeNanos();
2826

29-
TimeProvider SYSTEM_TIME_PROVIDER = new TimeProvider() {
30-
@Override
31-
public long currentTimeNanos() {
32-
return TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis());
33-
}
34-
};
27+
TimeProvider SYSTEM_TIME_PROVIDER = TimeProviderResolverFactory.resolveTimeProvider();
3528
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
/**
20+
* {@link TimeProviderResolverFactory} resolves Time providers.
21+
*/
22+
23+
final class TimeProviderResolverFactory {
24+
static TimeProvider resolveTimeProvider() {
25+
try {
26+
Class<?> instantClass = Class.forName("java.time.Instant");
27+
return new InstantTimeProvider(instantClass);
28+
} catch (ClassNotFoundException ex) {
29+
return new ConcurrentTimeProvider();
30+
}
31+
}
32+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import java.time.Instant;
22+
import java.util.concurrent.TimeUnit;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.junit.runners.JUnit4;
26+
27+
/**
28+
* Unit tests for {@link ConcurrentTimeProvider}.
29+
*/
30+
@RunWith(JUnit4.class)
31+
public class ConcurrentTimeProviderTest {
32+
@Test
33+
public void testConcurrentCurrentTimeNanos() {
34+
35+
ConcurrentTimeProvider concurrentTimeProvider = new ConcurrentTimeProvider();
36+
// Get the current time from the ConcurrentTimeProvider
37+
long actualTimeNanos = concurrentTimeProvider.currentTimeNanos();
38+
39+
// Get the current time from Instant for comparison
40+
Instant instantNow = Instant.now();
41+
long expectedTimeNanos = TimeUnit.SECONDS.toNanos(instantNow.getEpochSecond())
42+
+ instantNow.getNano();
43+
44+
// Validate the time returned is close to the expected value within a tolerance
45+
// (i,e 10 millisecond tolerance in nanoseconds).
46+
assertThat(actualTimeNanos).isWithin(10_000_000L).of(expectedTimeNanos);
47+
}
48+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2024 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.internal;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import java.time.Instant;
22+
import java.util.concurrent.TimeUnit;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.junit.runners.JUnit4;
26+
27+
/**
28+
* Unit tests for {@link InstantTimeProvider}.
29+
*/
30+
@RunWith(JUnit4.class)
31+
public class InstantTimeProviderTest {
32+
@Test
33+
public void testInstantCurrentTimeNanos() throws Exception {
34+
35+
InstantTimeProvider instantTimeProvider = new InstantTimeProvider(
36+
Class.forName("java.time.Instant"));
37+
38+
// Get the current time from the InstantTimeProvider
39+
long actualTimeNanos = instantTimeProvider.currentTimeNanos();
40+
41+
// Get the current time from Instant for comparison
42+
Instant instantNow = Instant.now();
43+
long expectedTimeNanos = TimeUnit.SECONDS.toNanos(instantNow.getEpochSecond())
44+
+ instantNow.getNano();
45+
46+
// Validate the time returned is close to the expected value within a tolerance
47+
// (i,e 10 millisecond tolerance in nanoseconds).
48+
assertThat(actualTimeNanos).isWithin(10_000_000L).of(expectedTimeNanos);
49+
}
50+
}

0 commit comments

Comments
 (0)