1 /*
2 * Copyright (C) 2016, Google Inc. and others
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Distribution License v. 1.0 which is available at
6 * https://www.eclipse.org/org/documents/edl-v10.php.
7 *
8 * SPDX-License-Identifier: BSD-3-Clause
9 */
10
11 package org.eclipse.jgit.util.time;
12
13 import static java.util.concurrent.TimeUnit.MICROSECONDS;
14 import static java.util.concurrent.TimeUnit.MILLISECONDS;
15
16 import java.sql.Timestamp;
17 import java.time.Duration;
18 import java.time.Instant;
19 import java.util.Date;
20 import java.util.Iterator;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23
24 /**
25 * A timestamp generated by
26 * {@link org.eclipse.jgit.util.time.MonotonicClock#propose()}.
27 * <p>
28 * ProposedTimestamp implements AutoCloseable so that implementations can
29 * release resources associated with obtaining certainty about time elapsing.
30 * For example the constructing MonotonicClock may start network IO with peers
31 * when creating the ProposedTimestamp, and {@link #close()} can ensure those
32 * network resources are released in a timely fashion.
33 *
34 * @since 4.6
35 */
36 public abstract class ProposedTimestamp implements AutoCloseable {
37 /**
38 * Wait for several timestamps.
39 *
40 * @param times
41 * timestamps to wait on.
42 * @param maxWait
43 * how long to wait for the timestamps.
44 * @throws java.lang.InterruptedException
45 * current thread was interrupted before the waiting process
46 * completed normally.
47 * @throws java.util.concurrent.TimeoutException
48 * the timeout was reached without the proposed timestamp become
49 * certainly in the past.
50 */
51 public static void blockUntil(Iterable<ProposedTimestamp> times,
52 Duration maxWait) throws TimeoutException, InterruptedException {
53 Iterator<ProposedTimestamp> itr = times.iterator();
54 if (!itr.hasNext()) {
55 return;
56 }
57
58 long now = System.currentTimeMillis();
59 long deadline = now + maxWait.toMillis();
60 for (;;) {
61 long w = deadline - now;
62 if (w < 0) {
63 throw new TimeoutException();
64 }
65 itr.next().blockUntil(Duration.ofMillis(w));
66 if (itr.hasNext()) {
67 now = System.currentTimeMillis();
68 } else {
69 break;
70 }
71 }
72 }
73
74 /**
75 * Read the timestamp as {@code unit} since the epoch.
76 * <p>
77 * The timestamp value for a specific {@code ProposedTimestamp} object never
78 * changes, and can be read before {@link #blockUntil(Duration)}.
79 *
80 * @param unit
81 * what unit to return the timestamp in. The timestamp will be
82 * rounded if the unit is bigger than the clock's granularity.
83 * @return {@code unit} since the epoch.
84 */
85 public abstract long read(TimeUnit unit);
86
87 /**
88 * Wait for this proposed timestamp to be certainly in the recent past.
89 * <p>
90 * This method forces the caller to wait up to {@code timeout} for
91 * {@code this} to pass sufficiently into the past such that the creating
92 * {@link org.eclipse.jgit.util.time.MonotonicClock} instance will not
93 * create an earlier timestamp.
94 *
95 * @param maxWait
96 * how long the implementation may block the caller.
97 * @throws java.lang.InterruptedException
98 * current thread was interrupted before the waiting process
99 * completed normally.
100 * @throws java.util.concurrent.TimeoutException
101 * the timeout was reached without the proposed timestamp
102 * becoming certainly in the past.
103 */
104 public abstract void blockUntil(Duration maxWait)
105 throws InterruptedException, TimeoutException;
106
107 /**
108 * Get milliseconds since epoch; {@code read(MILLISECONDS}).
109 *
110 * @return milliseconds since epoch; {@code read(MILLISECONDS}).
111 */
112 public long millis() {
113 return read(MILLISECONDS);
114 }
115
116 /**
117 * Get microseconds since epoch; {@code read(MICROSECONDS}).
118 *
119 * @return microseconds since epoch; {@code read(MICROSECONDS}).
120 */
121 public long micros() {
122 return read(MICROSECONDS);
123 }
124
125 /**
126 * Get time since epoch, with up to microsecond resolution.
127 *
128 * @return time since epoch, with up to microsecond resolution.
129 */
130 public Instant instant() {
131 long usec = micros();
132 long secs = usec / 1000000L;
133 long nanos = (usec % 1000000L) * 1000L;
134 return Instant.ofEpochSecond(secs, nanos);
135 }
136
137 /**
138 * Get time since epoch, with up to microsecond resolution.
139 *
140 * @return time since epoch, with up to microsecond resolution.
141 */
142 public Timestamp timestamp() {
143 return Timestamp.from(instant());
144 }
145
146 /**
147 * Get time since epoch, with up to millisecond resolution.
148 *
149 * @return time since epoch, with up to millisecond resolution.
150 */
151 public Date date() {
152 return new Date(millis());
153 }
154
155 /**
156 * {@inheritDoc}
157 * <p>
158 * Release resources allocated by this timestamp.
159 */
160 @Override
161 public void close() {
162 // Do nothing by default.
163 }
164
165 /** {@inheritDoc} */
166 @Override
167 public String toString() {
168 return instant().toString();
169 }
170 }