Skip to content

Commit 807d185

Browse files
committed
Add Timer and use it in Timeout
JAVA-5076
1 parent 46903fb commit 807d185

File tree

10 files changed

+368
-127
lines changed

10 files changed

+368
-127
lines changed

driver-core/src/main/com/mongodb/internal/connection/ConcurrentPool.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import com.mongodb.MongoTimeoutException;
2424
import com.mongodb.annotations.ThreadSafe;
2525
import com.mongodb.internal.VisibleForTesting;
26+
import com.mongodb.internal.time.Timeout;
27+
import com.mongodb.internal.time.Timer;
2628
import com.mongodb.lang.Nullable;
2729

2830
import java.util.Deque;
@@ -142,7 +144,7 @@ public T get() {
142144
* Gets an object from the pool. Blocks until an object is available, or the specified {@code timeout} expires,
143145
* or the pool is {@linkplain #close() closed}/{@linkplain #pause(Supplier) paused}.
144146
*
145-
* @param timeout See {@link com.mongodb.internal.Timeout#startNow(long, TimeUnit)}.
147+
* @param timeout See {@link Timeout#started(long, TimeUnit, Timer)}.
146148
* @param timeUnit the time unit of the timeout
147149
* @return An object from the pool, or null if can't get one in the given waitTime
148150
* @throws MongoTimeoutException if the timeout has been exceeded
@@ -226,7 +228,7 @@ private T createNewAndReleasePermitIfFailure() {
226228
}
227229

228230
/**
229-
* @param timeout See {@link com.mongodb.internal.Timeout#startNow(long, TimeUnit)}.
231+
* @param timeout See {@link Timeout#started(long, TimeUnit, Timer)}.
230232
*/
231233
@VisibleForTesting(otherwise = PRIVATE)
232234
boolean acquirePermit(final long timeout, final TimeUnit timeUnit) {
@@ -386,7 +388,7 @@ boolean acquirePermitImmediateUnfair() {
386388
* This method also emulates the eager {@link InterruptedException} behavior of
387389
* {@link java.util.concurrent.Semaphore#tryAcquire(long, TimeUnit)}.
388390
*
389-
* @param timeout See {@link com.mongodb.internal.Timeout#startNow(long, TimeUnit)}.
391+
* @param timeout See {@link Timeout#started(long, TimeUnit, Timer)}.
390392
*/
391393
boolean acquirePermit(final long timeout, final TimeUnit unit) throws MongoInterruptedException {
392394
long remainingNanos = unit.toNanos(timeout);

driver-core/src/main/com/mongodb/internal/connection/ConnectionPool.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import com.mongodb.annotations.ThreadSafe;
2121
import com.mongodb.connection.ConnectionPoolSettings;
2222
import com.mongodb.internal.async.SingleResultCallback;
23+
import com.mongodb.internal.time.Timeout;
24+
import com.mongodb.internal.time.Timer;
2325
import org.bson.types.ObjectId;
2426
import com.mongodb.lang.Nullable;
2527

@@ -38,7 +40,7 @@ interface ConnectionPool extends Closeable {
3840

3941
/**
4042
* @param operationContext operation context
41-
* @param timeout See {@link com.mongodb.internal.Timeout#startNow(long, TimeUnit)}.
43+
* @param timeout See {@link Timeout#started(long, TimeUnit, Timer)}.
4244
* @throws MongoConnectionPoolClearedException If detects that the pool is {@linkplain #invalidate(Throwable) paused}.
4345
*/
4446
InternalConnection get(OperationContext operationContext, long timeout, TimeUnit timeUnit) throws MongoConnectionPoolClearedException;

driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import com.mongodb.event.ConnectionPoolListener;
4444
import com.mongodb.event.ConnectionPoolReadyEvent;
4545
import com.mongodb.event.ConnectionReadyEvent;
46-
import com.mongodb.internal.Timeout;
46+
import com.mongodb.internal.time.Timeout;
4747
import com.mongodb.internal.VisibleForTesting;
4848
import com.mongodb.internal.async.SingleResultCallback;
4949
import com.mongodb.internal.connection.SdamServerDescriptionManager.SdamIssue;
@@ -55,6 +55,7 @@
5555
import com.mongodb.internal.logging.StructuredLogger;
5656
import com.mongodb.internal.session.SessionContext;
5757
import com.mongodb.internal.thread.DaemonThreadFactory;
58+
import com.mongodb.internal.time.Timer;
5859
import com.mongodb.lang.NonNull;
5960
import com.mongodb.lang.Nullable;
6061
import org.bson.ByteBuf;
@@ -190,7 +191,7 @@ public InternalConnection get(final OperationContext operationContext) {
190191
@Override
191192
public InternalConnection get(final OperationContext operationContext, final long timeoutValue, final TimeUnit timeUnit) {
192193
connectionCheckoutStarted(operationContext);
193-
Timeout timeout = Timeout.startNow(timeoutValue, timeUnit);
194+
Timeout timeout = Timeout.started(timeoutValue, timeUnit, Timer.start());
194195
try {
195196
stateAndGeneration.throwIfClosedOrPaused();
196197
PooledConnection connection = getPooledConnection(timeout);
@@ -208,7 +209,7 @@ public InternalConnection get(final OperationContext operationContext, final lon
208209
@Override
209210
public void getAsync(final OperationContext operationContext, final SingleResultCallback<InternalConnection> callback) {
210211
connectionCheckoutStarted(operationContext);
211-
Timeout timeout = Timeout.startNow(settings.getMaxWaitTime(NANOSECONDS));
212+
Timeout timeout = Timeout.started(settings.getMaxWaitTime(NANOSECONDS), Timer.start());
212213
SingleResultCallback<PooledConnection> eventSendingCallback = (connection, failure) -> {
213214
SingleResultCallback<InternalConnection> errHandlingCallback = errorHandlingCallback(callback, LOGGER);
214215
if (failure == null) {
@@ -1123,7 +1124,7 @@ void signalClosedOrPaused() {
11231124
}
11241125

11251126
/**
1126-
* @param timeoutNanos See {@link Timeout#startNow(long)}.
1127+
* @param timeoutNanos See {@link Timeout#started(long, Timer)}.
11271128
* @return The remaining duration as per {@link Timeout#remainingOrInfinite(TimeUnit)} if waiting ended early either
11281129
* spuriously or because of receiving a signal.
11291130
*/

driver-core/src/main/com/mongodb/internal/Timeout.java renamed to driver-core/src/main/com/mongodb/internal/time/Timeout.java

Lines changed: 41 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -13,164 +13,119 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package com.mongodb.internal;
16+
package com.mongodb.internal.time;
1717

1818
import com.mongodb.annotations.Immutable;
19+
import com.mongodb.internal.VisibleForTesting;
1920

2021
import java.util.Objects;
2122
import java.util.concurrent.TimeUnit;
2223

2324
import static com.mongodb.assertions.Assertions.assertFalse;
24-
import static com.mongodb.assertions.Assertions.assertNotNull;
2525
import static com.mongodb.assertions.Assertions.assertTrue;
2626
import static com.mongodb.internal.VisibleForTesting.AccessModifier.PRIVATE;
2727
import static java.util.concurrent.TimeUnit.MILLISECONDS;
2828
import static java.util.concurrent.TimeUnit.NANOSECONDS;
2929

3030
/**
3131
* A <a href="https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html">value-based</a> class
32-
* useful for tracking timeouts.
33-
*
34-
* <p>This class is not part of the public API and may be removed or changed at any time</p>
32+
* for tracking timeouts.
33+
* <p>
34+
* This class is not part of the public API and may be removed or changed at any time.</p>
3535
*/
3636
@Immutable
3737
public final class Timeout {
38-
private static final Timeout INFINITE = new Timeout(-1, 0);
39-
private static final Timeout IMMEDIATE = new Timeout(0, 0);
38+
private static final Timeout INFINITE = new Timeout(-1, Timer.useless());
39+
private static final Timeout IMMEDIATE = new Timeout(0, Timer.useless());
4040

4141
private final long durationNanos;
42-
private final long startNanos;
42+
private final Timer timer;
4343

44-
private Timeout(final long durationNanos, final long startNanos) {
44+
private Timeout(final long durationNanos, final Timer timer) {
4545
this.durationNanos = durationNanos;
46-
this.startNanos = startNanos;
46+
this.timer = timer;
4747
}
4848

4949
/**
50-
* Converts the specified {@code duration} from {@code unit}s to {@link TimeUnit#NANOSECONDS} via {@link TimeUnit#toNanos(long)}
51-
* and then acts identically to {@link #startNow(long)}.
50+
* Converts the specified {@code duration} from {@code unit}s to {@link TimeUnit#NANOSECONDS}
51+
* as specified by {@link TimeUnit#toNanos(long)} and then acts identically to {@link #started(long, Timer)}.
5252
* <p>
5353
* Note that the contract of this method is also used in some places to specify the behavior of methods that accept
5454
* {@code (long timeout, TimeUnit unit)}, e.g., {@link com.mongodb.internal.connection.ConcurrentPool#get(long, TimeUnit)},
55-
* so it cannot be changed without updating those methods.
56-
* @see #startNow(long)
55+
* so it cannot be changed without updating those methods.</p>
56+
* @see #started(long, Timer)
5757
*/
58-
public static Timeout startNow(final long duration, final TimeUnit unit) {
59-
assertNotNull(unit);
60-
return startNow(unit.toNanos(duration));
58+
public static Timeout started(final long duration, final TimeUnit unit, final Timer timer) {
59+
return started(unit.toNanos(duration), timer);
6160
}
6261

6362
/**
6463
* Returns an {@linkplain #isInfinite() infinite} timeout if {@code durationNanos} is either negative
6564
* or is equal to {@link Long#MAX_VALUE},
6665
* an {@linkplain #isImmediate() immediate} timeout if {@code durationNanos} is 0,
67-
* otherwise an object that represents the specified {@code durationNanos}.
66+
* otherwise a timeout of {@code durationNanos}.
6867
* <p>
6968
* Note that the contract of this method is also used in some places to specify the behavior of methods that accept
7069
* {@code (long timeout, TimeUnit unit)}, e.g., {@link com.mongodb.internal.connection.ConcurrentPool#get(long, TimeUnit)},
71-
* so it cannot be changed without updating those methods.
70+
* so it cannot be changed without updating those methods.</p>
7271
*/
73-
public static Timeout startNow(final long durationNanos) {
72+
public static Timeout started(final long durationNanos, final Timer timer) {
7473
if (durationNanos < 0 || durationNanos == Long.MAX_VALUE) {
7574
return infinite();
7675
} else if (durationNanos == 0) {
7776
return immediate();
7877
} else {
79-
return new Timeout(durationNanos, System.nanoTime());
78+
return new Timeout(durationNanos, timer);
8079
}
8180
}
8281

8382
/**
84-
* @see #startNow(long)
83+
* @see #started(long, Timer)
8584
*/
8685
public static Timeout infinite() {
8786
return INFINITE;
8887
}
8988

9089
/**
91-
* @see #startNow(long)
90+
* @see #started(long, Timer)
9291
*/
9392
public static Timeout immediate() {
9493
return IMMEDIATE;
9594
}
9695

97-
/**
98-
* Must not be called on {@linkplain #isInfinite() infinite} or {@linkplain #isImmediate() immediate} timeouts.
99-
* <p>
100-
* Returns {@code currentNanos} - {@link #startNanos}:
101-
* <ul>
102-
* <li>
103-
* A negative value means either of the following
104-
* <ol>
105-
* <li>the clock from which {@code currentNanos} was read jumped backwards,
106-
* in which case the behaviour of this class is undefined;</li>
107-
* <li>(n * 2<sup>63</sup> - 1; (n + 1) * 2<sup>63</sup>)<sup>(*)</sup> nanoseconds has elapsed,
108-
* in which case the timeout has expired.</li>
109-
* </ol>
110-
* </li>
111-
* <li>
112-
* 0 means either of the following
113-
* <ol>
114-
* <li>0 nanoseconds has elapsed;</li>
115-
* <li>(n + 1) * 2<sup>63</sup><sup>(*)</sup> nanoseconds has elapsed,
116-
* in which case the timeout has expired.</li>
117-
* </ol>
118-
* Since it is impossible to differentiate the former from the latter, and the former is much more likely to happen in practice,
119-
* this class interprets 0 value as 0 elapsed nanoseconds.
120-
* </li>
121-
* <li>
122-
* A positive value means either of the following
123-
* <ol>
124-
* <li>this exact number of nanoseconds has elapsed;</li>
125-
* <li>((n + 1) * 2<sup>63</sup>; (n + 2) * 2<sup>63</sup> - 1]<sup>(*)</sup> nanoseconds has elapsed,
126-
* in which case the timeout has expired.</li>
127-
* </ol>
128-
* Since it is impossible to differentiate the former from the latter, and the former is much more likely to happen in practice,
129-
* this class interprets a positive value as the exact number of elapsed nanoseconds.
130-
* </li>
131-
* </ul>
132-
* <hr>
133-
* <sup>(*)</sup> n is positive and odd.
134-
*/
135-
private long elapsedNanos(final long currentNanos) {
136-
assertFalse(isInfinite() || isImmediate());
137-
return currentNanos - startNanos;
138-
}
139-
14096
/**
14197
* Returns 0 or a positive value.
14298
* 0 means that the timeout has expired.
143-
* <p>
144-
* Must not be called on {@linkplain #isInfinite() infinite} timeouts.
99+
*
100+
* @throws AssertionError If the timeout is {@linkplain #isInfinite() infinite} or {@linkplain #isImmediate() immediate},
101+
* because such timeouts use {@link Timer#useless()}.
145102
*/
146103
@VisibleForTesting(otherwise = PRIVATE)
147-
long remainingNanos(final long currentNanos) {
104+
long nonNegativeRemainingNanos(final long currentNanos) {
148105
assertFalse(isInfinite() || isImmediate());
149-
long elapsedNanos = elapsedNanos(currentNanos);
150-
return elapsedNanos < 0 ? 0 : Math.max(0, durationNanos - elapsedNanos);
106+
return Math.max(0, durationNanos - timer.elapsedNanos(currentNanos));
151107
}
152108

153109
/**
154110
* Returns 0 or a positive value converted to the specified {@code unit}s.
155111
* Use {@link #expired(long)} to check if the returned value signifies that a timeout is expired.
156112
*
157113
* @param unit If not {@link TimeUnit#NANOSECONDS}, then coarsening conversion is done that may result in returning a value
158-
* that represents a longer time duration than is actually remaining (this is done to prevent treating a timeout as
159-
* {@linkplain #expired(long) expired} when it is not). Consequently, one should specify {@code unit} as small as
160-
* practically possible. Such rounding up happens if and only if the remaining time cannot be
161-
* represented exactly as an integral number of the {@code unit}s specified. It may result in
162-
* {@link #expired()} returning {@code true} and after that (in the happens-before order)
163-
* {@link #expired(long) expired}{@code (}{@link #remaining(TimeUnit) remaining(...)}{@code )}
164-
* returning {@code false}. If such a discrepancy is observed,
165-
* the result of the {@link #expired()} method should be preferred.
114+
* that represents a longer time duration than is actually remaining (this is done to prevent treating a timeout as
115+
* {@linkplain #expired(long) expired} when it is not). Consequently, one should specify {@code unit} as small as
116+
* practically possible. Such rounding up happens if and only if the remaining time cannot be
117+
* represented exactly as an integral number of the {@code unit}s specified. It may result in
118+
* {@link #expired()} returning {@code true} and after that (in the happens-before order)
119+
* {@link #expired(long) expired}{@code (}{@link #remaining(TimeUnit) remaining(...)}{@code )}
120+
* returning {@code false}. If such a discrepancy is observed,
121+
* the result of the {@link #expired()} method should be preferred.
166122
*
167123
* @throws AssertionError If the timeout is {@linkplain #isInfinite() infinite}.
168124
* @see #remainingOrInfinite(TimeUnit)
169125
*/
170126
public long remaining(final TimeUnit unit) {
171-
assertNotNull(unit);
172127
assertFalse(isInfinite());
173-
return isImmediate() ? 0 : convertRoundUp(remainingNanos(System.nanoTime()), unit);
128+
return isImmediate() ? 0 : convertRoundUp(nonNegativeRemainingNanos(System.nanoTime()), unit);
174129
}
175130

176131
/**
@@ -181,7 +136,6 @@ public long remaining(final TimeUnit unit) {
181136
* @see #remaining(TimeUnit)
182137
*/
183138
public long remainingOrInfinite(final TimeUnit unit) {
184-
assertNotNull(unit);
185139
return isInfinite() ? -1 : remaining(unit);
186140
}
187141

@@ -226,12 +180,12 @@ public boolean equals(final Object o) {
226180
return false;
227181
}
228182
Timeout other = (Timeout) o;
229-
return durationNanos == other.durationNanos && startNanos == other.startNanos;
183+
return durationNanos == other.durationNanos && timer == other.timer;
230184
}
231185

232186
@Override
233187
public int hashCode() {
234-
return Objects.hash(durationNanos, startNanos);
188+
return Objects.hash(durationNanos, timer);
235189
}
236190

237191
/**
@@ -243,7 +197,7 @@ public int hashCode() {
243197
public String toString() {
244198
return "Timeout{"
245199
+ "durationNanos=" + durationNanos
246-
+ ", startNanos=" + startNanos
200+
+ ", timer=" + timer
247201
+ '}';
248202
}
249203

@@ -268,8 +222,8 @@ long durationNanos() {
268222
}
269223

270224
@VisibleForTesting(otherwise = PRIVATE)
271-
long startNanos() {
272-
return startNanos;
225+
Timer timer() {
226+
return timer;
273227
}
274228

275229
@VisibleForTesting(otherwise = PRIVATE)

0 commit comments

Comments
 (0)