You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/01/19 16:09:51 UTC

[commons-io] branch master updated: Add and use ThreadUtils

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 a8c3027f Add and use ThreadUtils
a8c3027f is described below

commit a8c3027f993f5a9c9a82681a56fa6c1b1df4bcf3
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Thu Jan 19 11:09:46 2023 -0500

    Add and use ThreadUtils
    
    Inspired by DaGeRe's PR Avoid Code Duplication: Reuse Sleep from
    ThreadMonitor #66
---
 src/changes/changes.xml                            |  3 ++
 .../java/org/apache/commons/io/ThreadMonitor.java  | 24 +---------
 .../java/org/apache/commons/io/ThreadUtils.java    | 53 ++++++++++++++++++++++
 .../java/org/apache/commons/io/file/PathUtils.java |  3 +-
 .../java/org/apache/commons/io/input/Tailer.java   |  7 +--
 .../commons/io/monitor/FileAlterationMonitor.java  |  5 +-
 .../io/file/AccumulatorPathVisitorTest.java        |  6 ++-
 .../commons/io/input/TimestampedObserverTest.java  |  8 ++--
 .../io/monitor/FileAlterationMonitorTest.java      |  4 +-
 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."));