• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #18955

19 Dec 2023 05:38PM UTC coverage: 88.243% (+0.01%) from 88.232%
#18955

push

github

web-flow
buildscripts: Use the Kokoro shared install lib from the new repo (#10757) (#10762)

Source: https://github.com/grpc/grpc/blob/4f7ead2c7/tools/internal_ci/linux/grpc_xds_k8s_install_test_driver.sh
New repo: https://github.com/grpc/psm-interop
New path: `.kokoro/psm_interop_kokoro_lib.sh`

30946 of 35069 relevant lines covered (88.24%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.3
/../context/src/main/java/io/grpc/Deadline.java
1
/*
2
 * Copyright 2016 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;
18

19
import java.util.Arrays;
20
import java.util.Locale;
21
import java.util.concurrent.ScheduledExecutorService;
22
import java.util.concurrent.ScheduledFuture;
23
import java.util.concurrent.TimeUnit;
24

25
/**
26
 * An absolute point in time, generally for tracking when a task should be completed. A deadline is
27
 * immutable except for the passage of time causing it to expire.
28
 *
29
 * <p>Many systems use timeouts, which are relative to the start of the operation. However, being
30
 * relative causes them to be poorly suited for managing higher-level tasks where there are many
31
 * components and sub-operations that may not know the time of the initial "start of the operation."
32
 * However, a timeout can be converted to a {@code Deadline} at the start of the operation and then
33
 * passed to the various components unambiguously.
34
 */
35
public final class Deadline implements Comparable<Deadline> {
36
  private static final SystemTicker SYSTEM_TICKER = new SystemTicker();
1✔
37
  // nanoTime has a range of just under 300 years. Only allow up to 100 years in the past or future
38
  // to prevent wraparound as long as process runs for less than ~100 years.
39
  private static final long MAX_OFFSET = TimeUnit.DAYS.toNanos(100 * 365);
1✔
40
  private static final long MIN_OFFSET = -MAX_OFFSET;
1✔
41
  private static final long NANOS_PER_SECOND = TimeUnit.SECONDS.toNanos(1);
1✔
42

43
  /**
44
   * Returns the ticker that's based on system clock.
45
   *
46
   * <p>This is <strong>EXPERIMENTAL</strong> API and may subject to change.  If you'd like it to be
47
   * stabilized or have any feedback, please
48
   * <a href="https://github.com/grpc/grpc-java/issues/6030">let us know</a>.
49
   *
50
   * @since 1.24.0
51
   */
52
  public static Ticker getSystemTicker() {
53
    return SYSTEM_TICKER;
1✔
54
  }
55

56
  /**
57
   * Create a deadline that will expire at the specified offset based on the {@link #getSystemTicker
58
   * system ticker}.
59
   *
60
   * <p>If the given offset is extraordinarily long, say 100 years, the actual deadline created
61
   * might saturate.
62
   *
63
   * @param duration A non-negative duration.
64
   * @param units The time unit for the duration.
65
   * @return A new deadline.
66
   */
67
  public static Deadline after(long duration, TimeUnit units) {
68
    return after(duration, units, SYSTEM_TICKER);
1✔
69
  }
70

71
  /**
72
   * Create a deadline that will expire at the specified offset based on the given {@link Ticker}.
73
   *
74
   * <p>If the given offset is extraordinarily long, say 100 years, the actual deadline created
75
   * might saturate.
76
   *
77
   * <p><strong>CAUTION</strong>: Only deadlines created with the same {@link Ticker} instance can
78
   * be compared by methods like {@link #minimum}, {@link #isBefore} and {@link #compareTo}.  Custom
79
   * Tickers should only be used in tests where you fake out the clock.  Always use the {@link
80
   * #getSystemTicker system ticker} in production, or serious errors may occur.
81
   *
82
   * <p>This is <strong>EXPERIMENTAL</strong> API and may subject to change.  If you'd like it to be
83
   * stabilized or have any feedback, please
84
   * <a href="https://github.com/grpc/grpc-java/issues/6030">let us know</a>.
85
   *
86
   * @param duration A non-negative duration.
87
   * @param units The time unit for the duration.
88
   * @param ticker Where this deadline refer the current time
89
   * @return A new deadline.
90
   *
91
   * @since 1.24.0
92
   */
93
  public static Deadline after(long duration, TimeUnit units, Ticker ticker) {
94
    checkNotNull(units, "units");
1✔
95
    return new Deadline(ticker, units.toNanos(duration), true);
1✔
96
  }
97

98
  private final Ticker ticker;
99
  private final long deadlineNanos;
100
  private volatile boolean expired;
101

102
  private Deadline(Ticker ticker, long offset, boolean baseInstantAlreadyExpired) {
103
    this(ticker, ticker.nanoTime(), offset, baseInstantAlreadyExpired);
1✔
104
  }
1✔
105

106
  private Deadline(Ticker ticker, long baseInstant, long offset,
107
      boolean baseInstantAlreadyExpired) {
1✔
108
    this.ticker = ticker;
1✔
109
    // Clamp to range [MIN_OFFSET, MAX_OFFSET]
110
    offset = Math.min(MAX_OFFSET, Math.max(MIN_OFFSET, offset));
1✔
111
    deadlineNanos = baseInstant + offset;
1✔
112
    expired = baseInstantAlreadyExpired && offset <= 0;
1✔
113
  }
1✔
114

115
  /**
116
   * Returns whether this has deadline expired.
117
   *
118
   * @return {@code true} if it has, otherwise {@code false}.
119
   */
120
  public boolean isExpired() {
121
    if (!expired) {
1✔
122
      if (deadlineNanos - ticker.nanoTime() <= 0) {
1✔
123
        expired = true;
1✔
124
      } else {
125
        return false;
1✔
126
      }
127
    }
128
    return true;
1✔
129
  }
130

131
  /**
132
   * Is {@code this} deadline before another.  Two deadlines must be created using the same {@link
133
   * Ticker}.
134
   */
135
  public boolean isBefore(Deadline other) {
136
    checkTicker(other);
1✔
137
    return this.deadlineNanos - other.deadlineNanos < 0;
1✔
138
  }
139

140
  /**
141
   * Return the minimum deadline of {@code this} or an other deadline.  They must be created using
142
   * the same {@link Ticker}.
143
   *
144
   * @param other deadline to compare with {@code this}.
145
   */
146
  public Deadline minimum(Deadline other) {
147
    checkTicker(other);
1✔
148
    return isBefore(other) ? this : other;
1✔
149
  }
150

151
  /**
152
   * Create a new deadline that is offset from {@code this}.
153
   *
154
   * <p>If the given offset is extraordinarily long, say 100 years, the actual deadline created
155
   * might saturate.
156
   */
157
  // TODO(ejona): This method can cause deadlines to grow too far apart. For example:
158
  // Deadline.after(100 * 365, DAYS).offset(100 * 365, DAYS) would be less than
159
  // Deadline.after(-100 * 365, DAYS)
160
  public Deadline offset(long offset, TimeUnit units) {
161
    // May already be expired
162
    if (offset == 0) {
1✔
163
      return this;
1✔
164
    }
165
    return new Deadline(ticker, deadlineNanos, units.toNanos(offset), isExpired());
1✔
166
  }
167

168
  /**
169
   * How much time is remaining in the specified time unit. Internal units are maintained as
170
   * nanoseconds and conversions are subject to the constraints documented for
171
   * {@link TimeUnit#convert}. If there is no time remaining, the returned duration is how
172
   * long ago the deadline expired.
173
   */
174
  public long timeRemaining(TimeUnit unit) {
175
    final long nowNanos = ticker.nanoTime();
1✔
176
    if (!expired && deadlineNanos - nowNanos <= 0) {
1✔
177
      expired = true;
×
178
    }
179
    return unit.convert(deadlineNanos - nowNanos, TimeUnit.NANOSECONDS);
1✔
180
  }
181

182
  /**
183
   * Schedule a task to be run when the deadline expires.
184
   *
185
   * <p>Note if this deadline was created with a custom {@link Ticker}, the {@code scheduler}'s
186
   * underlying clock should be synchronized with that Ticker.  Otherwise the task won't be run at
187
   * the expected point of time.
188
   *
189
   * @param task to run on expiration
190
   * @param scheduler used to execute the task
191
   * @return {@link ScheduledFuture} which can be used to cancel execution of the task
192
   */
193
  public ScheduledFuture<?> runOnExpiration(Runnable task, ScheduledExecutorService scheduler) {
194
    checkNotNull(task, "task");
1✔
195
    checkNotNull(scheduler, "scheduler");
1✔
196
    return scheduler.schedule(task, deadlineNanos - ticker.nanoTime(), TimeUnit.NANOSECONDS);
1✔
197
  }
198

199
  @Override
200
  public String toString() {
201
    long remainingNanos = timeRemaining(TimeUnit.NANOSECONDS);
1✔
202
    long seconds = Math.abs(remainingNanos) / NANOS_PER_SECOND;
1✔
203
    long nanos = Math.abs(remainingNanos) % NANOS_PER_SECOND;
1✔
204

205
    StringBuilder buf = new StringBuilder();
1✔
206
    if (remainingNanos < 0) {
1✔
207
      buf.append('-');
1✔
208
    }
209
    buf.append(seconds);
1✔
210
    if (nanos > 0) {
1✔
211
      buf.append(String.format(Locale.US, ".%09d", nanos));
1✔
212
    }
213
    buf.append("s from now");
1✔
214
    if (ticker != SYSTEM_TICKER) {
1✔
215
      buf.append(" (ticker=" + ticker + ")");
1✔
216
    }
217
    return buf.toString();
1✔
218
  }
219

220
  /**
221
   * {@inheritDoc}
222
   *
223
   * <p>Both deadlines must be created with the same {@link Ticker}.
224
   */
225
  @Override
226
  public int compareTo(Deadline that) {
227
    checkTicker(that);
1✔
228
    long diff = this.deadlineNanos - that.deadlineNanos;
1✔
229
    if (diff < 0) {
1✔
230
      return -1;
1✔
231
    } else if (diff > 0) {
1✔
232
      return 1;
1✔
233
    }
234
    return 0;
1✔
235
  }
236

237
  @Override
238
  public int hashCode() {
239
    return Arrays.asList(this.ticker, this.deadlineNanos).hashCode();
1✔
240
  }
241

242
  @Override
243
  public boolean equals(final Object o) {
244
    if (o == this) {
1✔
245
      return true;
1✔
246
    }
247
    if (!(o instanceof Deadline)) {
1✔
248
      return false;
1✔
249
    }
250

251
    final Deadline other = (Deadline) o;
1✔
252
    if (this.ticker == null ? other.ticker != null : this.ticker != other.ticker) {
1✔
253
      return false;
1✔
254
    }
255
    if (this.deadlineNanos != other.deadlineNanos) {
1✔
256
      return false;
1✔
257
    }
258
    return true;
1✔
259
  }
260

261
  /**
262
   * Time source representing nanoseconds since fixed but arbitrary point in time.
263
   *
264
   * <p>DO NOT use custom {@link Ticker} implementations in production, because deadlines created
265
   * with custom tickers are incompatible with those created with the system ticker.  Always use
266
   * the {@link #getSystemTicker system ticker} whenever you need to provide one in production code.
267
   *
268
   * <p>This is <strong>EXPERIMENTAL</strong> API and may subject to change.  If you'd like it to be
269
   * stabilized or have any feedback, please
270
   * <a href="https://github.com/grpc/grpc-java/issues/6030">let us know</a>.
271
   *
272
   * <p>In general implementations should be thread-safe, unless it's implemented and used in a
273
   * localized environment (like unit tests) where you are sure the usages are synchronized.
274
   *
275
   * @since 1.24.0
276
   */
277
  public abstract static class Ticker {
1✔
278
    /** Returns the number of nanoseconds since this source's epoch. */
279
    public abstract long nanoTime();
280
  }
281

282
  private static class SystemTicker extends Ticker {
283
    @Override
284
    public long nanoTime() {
285
      return System.nanoTime();
1✔
286
    }
287
  }
288

289
  private static <T> T checkNotNull(T reference, Object errorMessage) {
290
    if (reference == null) {
1✔
291
      throw new NullPointerException(String.valueOf(errorMessage));
×
292
    }
293
    return reference;
1✔
294
  }
295

296
  private void checkTicker(Deadline other) {
297
    if (ticker != other.ticker) {
1✔
298
      throw new AssertionError(
1✔
299
          "Tickers (" + ticker + " and " + other.ticker + ") don't match."
300
          + " Custom Ticker should only be used in tests!");
301
    }
302
  }
1✔
303
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc