This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push:
new b8e788a08 ThreadUtils.sleep(Duration) should handle the underlying OS
time changing
new c722fe80b Merge branch 'master' of
https://gitbox.apache.org/repos/asf/commons-io.git
b8e788a08 is described below
commit b8e788a0875789303a957b077bef86e98cb83af3
Author: Gary Gregory <[email protected]>
AuthorDate: Tue Apr 8 12:21:40 2025 -0400
ThreadUtils.sleep(Duration) should handle the underlying OS time
changing
---
src/changes/changes.xml | 1 +
.../java/org/apache/commons/io/ThreadUtils.java | 28 ++++++++++++++++------
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 1684f86a7..fdfdf0a0c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -74,6 +74,7 @@ The <action> type attribute can be add,update,fix,remove.
<action dev="ggregory" type="fix" due-to="Gary
Gregory">General Javadoc improvements.</action>
<action dev="ggregory" type="fix" due-to="Gary
Gregory">Calling QueueInputStream.QueueInputStream(null) maps to the same kind
of default blocking queue as
QueueInputStream.Builder.setBlockingQueue(null).</action>
<action dev="ggregory" type="fix" due-to="Gary
Gregory">CopyDirectoryVisitor creates incorrect file names when copying between
different file systems that use different file system separators ("/" versus
"\"); fixes PathUtils.copyDirectory(Path, Path, CopyOption...).</action>
+ <action dev="ggregory" type="fix" due-to="zhouchongwen,
Gary Gregory">ThreadUtils.sleep(Duration) should handle the underlying OS time
changing.</action>
<!-- ADD -->
<action dev="ggregory" type="add" issue="IO-860" due-to="Nico Strecker,
Gary Gregory">Add ThrottledInputStream.Builder.setMaxBytes(long,
ChronoUnit).</action>
<action dev="ggregory" type="add" due-to="Gary
Gregory">Add IOIterable.</action>
diff --git a/src/main/java/org/apache/commons/io/ThreadUtils.java
b/src/main/java/org/apache/commons/io/ThreadUtils.java
index f6b7713ca..e41af9a84 100644
--- a/src/main/java/org/apache/commons/io/ThreadUtils.java
+++ b/src/main/java/org/apache/commons/io/ThreadUtils.java
@@ -38,17 +38,31 @@ private static int getNanosOfMilli(final Duration duration)
{
* </p>
*
* @param duration the sleep duration.
- * @throws InterruptedException if interrupted
+ * @throws InterruptedException if interrupted.
* @see Thread#sleep(long, int)
*/
public static void sleep(final Duration duration) throws
InterruptedException {
// Using this method avoids depending on the vagaries of the precision
and accuracy of system timers and schedulers.
- final Instant finishInstant = Instant.now().plus(duration);
- Duration remainingDuration = duration;
- do {
- Thread.sleep(remainingDuration.toMillis(),
getNanosOfMilli(remainingDuration));
- remainingDuration = Duration.between(Instant.now(), finishInstant);
- } while (!remainingDuration.isNegative());
+ try {
+ // Use the JVM elapsed time, avoids issues with DST changes and
manual OS time changes.
+ final long nanoStart = System.nanoTime();
+ final long finishNanos = nanoStart + duration.toNanos(); //
toNanos(): Possible ArithmeticException, otherwise wrap around OK.
+ Duration remainingDuration = duration;
+ long nowNano;
+ do {
+ Thread.sleep(remainingDuration.toMillis(),
getNanosOfMilli(remainingDuration));
+ nowNano = System.nanoTime();
+ remainingDuration = Duration.ofNanos(finishNanos - nowNano);
+ } while (nowNano - finishNanos < 0); // handles wrap around, see
Thread#sleep(long, int).
+ } catch (final ArithmeticException e) {
+ // Use the current time
+ final Instant finishInstant = Instant.now().plus(duration);
+ Duration remainingDuration = duration;
+ do {
+ Thread.sleep(remainingDuration.toMillis(),
getNanosOfMilli(remainingDuration));
+ remainingDuration = Duration.between(Instant.now(),
finishInstant);
+ } while (!remainingDuration.isNegative());
+ }
}
/**