diff options
author | Gary Gregory <garydgregory@gmail.com> | 2023-01-19 11:09:46 -0500 |
---|---|---|
committer | Gary Gregory <garydgregory@gmail.com> | 2023-01-19 11:09:46 -0500 |
commit | a8c3027f993f5a9c9a82681a56fa6c1b1df4bcf3 (patch) | |
tree | a41fe4b0db8343192a6569816fec6a8dff4dfbec | |
parent | 70d16028c81a8eca15093db794c51f00c97527f9 (diff) | |
download | apache-commons-io-a8c3027f993f5a9c9a82681a56fa6c1b1df4bcf3.tar.gz |
Add and use ThreadUtils
Inspired by DaGeRe's PR Avoid Code Duplication: Reuse Sleep from
ThreadMonitor #66
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.")); |