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

grpc / grpc-java / #18689

pending completion
#18689

push

github-actions

web-flow
buildscripts: Build Android with main build in linux_artifacts

Note that this changes the JDK used to compile releases to Java 11. That
should only impact the appearance of the Javadoc.

This adds the Android SDK to the build container, removing the
dependency on the Android SDK being available on the CI host. This
allows running on newer Kokoro images. 'Android' and 'Android interop'
CIs still depend on the Android SDK being available on the host, but
since they aren't used as part of the release process, they can more
easily migrate off Kokoro as part of future work.

This also causes Android components to now be built with -Werror, as we
use -PfailOnWarnings=true in unix.sh but were missing it from the
Android build invocations.

Gradle will auto-download the necessary version of build-tools. We don't
want to download it ourselves because the version we specify might not
even be used. Looking at logs, we were previously downloading a version
that was unused.

We now fork javac to avoid OOM. The build fails 2/3 times before the
forking, and 0/3 after.

31011 of 35117 relevant lines covered (88.31%)

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

© 2026 Coveralls, Inc