aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Gregory <garydgregory@gmail.com>2023-01-19 11:09:46 -0500
committerGary Gregory <garydgregory@gmail.com>2023-01-19 11:09:46 -0500
commita8c3027f993f5a9c9a82681a56fa6c1b1df4bcf3 (patch)
treea41fe4b0db8343192a6569816fec6a8dff4dfbec
parent70d16028c81a8eca15093db794c51f00c97527f9 (diff)
downloadapache-commons-io-a8c3027f993f5a9c9a82681a56fa6c1b1df4bcf3.tar.gz
Add and use ThreadUtils
Inspired by DaGeRe's PR Avoid Code Duplication: Reuse Sleep from ThreadMonitor #66
-rw-r--r--src/changes/changes.xml3
-rw-r--r--src/main/java/org/apache/commons/io/ThreadMonitor.java24
-rw-r--r--src/main/java/org/apache/commons/io/ThreadUtils.java53
-rw-r--r--src/main/java/org/apache/commons/io/file/PathUtils.java3
-rw-r--r--src/main/java/org/apache/commons/io/input/Tailer.java7
-rw-r--r--src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java5
-rw-r--r--src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java6
-rw-r--r--src/test/java/org/apache/commons/io/input/TimestampedObserverTest.java8
-rw-r--r--src/test/java/org/apache/commons/io/monitor/FileAlterationMonitorTest.java4
9 files changed, 79 insertions, 34 deletions
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 17a74bfd..966d463a 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -454,6 +454,9 @@ The <action> type attribute can be add,update,fix,remove.
<action issue="IO-784" dev="ggregory" type="add" due-to="Fredrik Kjellberg, Gary Gregory">
Add support for Appendable to HexDump #418.
</action>
+ <action dev="ggregory" type="add" due-to="DaGeRe, Gary Gregory">
+ Add and use ThreadUtils.
+ </action>
<!-- UPDATE -->
<action dev="kinow" type="update" due-to="Dependabot, Gary Gregory">
Bump actions/cache from 2.1.6 to 3.0.10 #307, #337, #393.
diff --git a/src/main/java/org/apache/commons/io/ThreadMonitor.java b/src/main/java/org/apache/commons/io/ThreadMonitor.java
index 1b691904..a97036bc 100644
--- a/src/main/java/org/apache/commons/io/ThreadMonitor.java
+++ b/src/main/java/org/apache/commons/io/ThreadMonitor.java
@@ -17,7 +17,6 @@
package org.apache.commons.io;
import java.time.Duration;
-import java.time.Instant;
/**
* Monitors a thread, interrupting it if it reaches the specified timeout.
@@ -41,27 +40,6 @@ import java.time.Instant;
*/
class ThreadMonitor implements Runnable {
- private static int getNanosOfMilli(final Duration duration) {
- return duration.getNano() % 1_000_000;
- }
- /**
- * Sleeps for a guaranteed minimum duration unless interrupted.
- *
- * This method exists because Thread.sleep(100) can sleep for 0, 70, 100 or 200ms or anything else it deems appropriate.
- * Read {@link Thread#sleep(long, int)}} for further interesting details.
- *
- * @param duration the sleep duration.
- * @throws InterruptedException if interrupted.
- */
- static void sleep(final Duration duration) throws InterruptedException {
- 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());
- }
-
/**
* Starts monitoring the current thread.
*
@@ -123,7 +101,7 @@ class ThreadMonitor implements Runnable {
@Override
public void run() {
try {
- sleep(timeout);
+ ThreadUtils.sleep(timeout);
thread.interrupt();
} catch (final InterruptedException e) {
// timeout not reached
diff --git a/src/main/java/org/apache/commons/io/ThreadUtils.java b/src/main/java/org/apache/commons/io/ThreadUtils.java
new file mode 100644
index 00000000..2fd661c1
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/ThreadUtils.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io;
+
+import java.time.Duration;
+import java.time.Instant;
+
+/**
+ * Helps work with threads.
+ *
+ * @since 2.12.0
+ */
+public class ThreadUtils {
+
+ static int getNanosOfMilli(final Duration duration) {
+ return duration.getNano() % 1_000_000;
+ }
+
+ /**
+ * Sleeps for a guaranteed minimum duration unless interrupted.
+ *
+ * This method exists because Thread.sleep(100) can sleep for 0, 70, 100 or 200ms or anything else it deems appropriate.
+ * Read {@link Thread#sleep(long, int)}} for further interesting details.
+ *
+ * TODO The above needs confirmation now that we've been on Java 8 for a while.
+ *
+ * @param duration the sleep duration.
+ * @throws InterruptedException if interrupted.
+ */
+ public static void sleep(final Duration duration) throws InterruptedException {
+ 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());
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 6ad6be47..65900606 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -71,6 +71,7 @@ import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.ThreadUtils;
import org.apache.commons.io.file.Counters.PathCounters;
import org.apache.commons.io.file.attribute.FileTimes;
import org.apache.commons.io.filefilter.IOFileFilter;
@@ -1691,7 +1692,7 @@ public final class PathUtils {
return false;
}
try {
- Thread.sleep(Math.min(minSleepMillis, finishInstant.minusMillis(now.toEpochMilli()).toEpochMilli()));
+ ThreadUtils.sleep(Duration.ofMillis(Math.min(minSleepMillis, finishInstant.minusMillis(now.toEpochMilli()).toEpochMilli())));
} catch (final InterruptedException ignore) {
interrupted = true;
} catch (final Exception ex) {
diff --git a/src/main/java/org/apache/commons/io/input/Tailer.java b/src/main/java/org/apache/commons/io/input/Tailer.java
index 6e067c80..7212b692 100644
--- a/src/main/java/org/apache/commons/io/input/Tailer.java
+++ b/src/main/java/org/apache/commons/io/input/Tailer.java
@@ -36,6 +36,7 @@ import java.util.Arrays;
import java.util.Objects;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.ThreadUtils;
import org.apache.commons.io.file.PathUtils;
import org.apache.commons.io.file.attribute.FileTimes;
@@ -899,7 +900,7 @@ public class Tailer implements Runnable, AutoCloseable {
listener.fileNotFound();
}
if (reader == null) {
- Thread.sleep(delayDuration.toMillis());
+ ThreadUtils.sleep(delayDuration);
} else {
// The current position in the file
position = tailAtEnd ? tailable.size() : 0;
@@ -929,7 +930,7 @@ public class Tailer implements Runnable, AutoCloseable {
} catch (final FileNotFoundException e) {
// in this case we continue to use the previous reader and position values
listener.fileNotFound();
- Thread.sleep(delayDuration.toMillis());
+ ThreadUtils.sleep(delayDuration);
}
continue;
}
@@ -954,7 +955,7 @@ public class Tailer implements Runnable, AutoCloseable {
if (reOpen && reader != null) {
reader.close();
}
- Thread.sleep(delayDuration.toMillis());
+ ThreadUtils.sleep(delayDuration);
if (getRun() && reOpen) {
reader = tailable.getRandomAccess(RAF_READ_ONLY_MODE);
reader.seek(position);
diff --git a/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java b/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
index d2b418e1..f4c07e18 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileAlterationMonitor.java
@@ -16,6 +16,7 @@
*/
package org.apache.commons.io.monitor;
+import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
@@ -24,6 +25,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Stream;
+import org.apache.commons.io.ThreadUtils;
+
/**
* A runnable that spawns a monitoring thread triggering any
* registered {@link FileAlterationObserver} at a specified interval.
@@ -143,7 +146,7 @@ public final class FileAlterationMonitor implements Runnable {
break;
}
try {
- Thread.sleep(intervalMillis);
+ ThreadUtils.sleep(Duration.ofMillis(intervalMillis));
} catch (final InterruptedException ignored) {
// ignore
}
diff --git a/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java b/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java
index 6d24b947..a3a9ece6 100644
--- a/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java
+++ b/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java
@@ -27,6 +27,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
@@ -39,6 +40,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Stream;
+import org.apache.commons.io.ThreadUtils;
import org.apache.commons.io.filefilter.AndFileFilter;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.EmptyFileFilter;
@@ -185,7 +187,7 @@ public class AccumulatorPathVisitorTest {
public FileVisitResult visitFile(final Path path, final BasicFileAttributes attributes) throws IOException {
// Slow down the walking a bit to try and cause conflicts with the deletion thread
try {
- Thread.sleep(10);
+ ThreadUtils.sleep(Duration.ofMillis(10));
} catch (final InterruptedException ignore) {
// e.printStackTrace();
}
@@ -209,7 +211,7 @@ public class AccumulatorPathVisitorTest {
Files.walkFileTree(tempDirPath, countingFileFilter);
} finally {
if (!deleted.get()) {
- Thread.sleep(1000);
+ ThreadUtils.sleep(Duration.ofMillis(1000));
}
if (!deleted.get()) {
executor.awaitTermination(5, TimeUnit.SECONDS);
diff --git a/src/test/java/org/apache/commons/io/input/TimestampedObserverTest.java b/src/test/java/org/apache/commons/io/input/TimestampedObserverTest.java
index 51507366..54922005 100644
--- a/src/test/java/org/apache/commons/io/input/TimestampedObserverTest.java
+++ b/src/test/java/org/apache/commons/io/input/TimestampedObserverTest.java
@@ -17,16 +17,18 @@
package org.apache.commons.io.input;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.time.Duration;
import java.time.Instant;
import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.ThreadUtils;
import org.junit.jupiter.api.Test;
/**
@@ -38,11 +40,11 @@ public class TimestampedObserverTest {
public void test() throws IOException, InterruptedException {
final Instant before = Instant.now();
// Some OS' clock granularity may be high.
- Thread.sleep(20);
+ ThreadUtils.sleep(Duration.ofMillis(20));
final TimestampedObserver timestampedObserver = new TimestampedObserver();
assertFalse(timestampedObserver.isClosed());
// Java 8 instant resolution is not great.
- Thread.sleep(20);
+ ThreadUtils.sleep(Duration.ofMillis(20));
// toString() should not blow up before close().
assertNotNull(timestampedObserver.toString());
assertTrue(timestampedObserver.getOpenInstant().isAfter(before));
diff --git a/src/test/java/org/apache/commons/io/monitor/FileAlterationMonitorTest.java b/src/test/java/org/apache/commons/io/monitor/FileAlterationMonitorTest.java
index b7bc3e28..9399bf17 100644
--- a/src/test/java/org/apache/commons/io/monitor/FileAlterationMonitorTest.java
+++ b/src/test/java/org/apache/commons/io/monitor/FileAlterationMonitorTest.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.File;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -30,6 +31,7 @@ import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
+import org.apache.commons.io.ThreadUtils;
import org.apache.commons.io.test.TestUtils;
import org.junit.jupiter.api.Test;
@@ -192,7 +194,7 @@ public class FileAlterationMonitorTest extends AbstractMonitorTest {
monitor.start();
assertFalse(createdThreads.isEmpty());
- Thread.sleep(10); // wait until the watcher thread enters Thread.sleep()
+ ThreadUtils.sleep(Duration.ofMillis(10)); // wait until the watcher thread enters Thread.sleep()
monitor.stop(100);
createdThreads.forEach(thread -> assertFalse(thread.isAlive(), "The FileAlterationMonitor did not stop the threads it created."));