1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.io;
18
19 import java.time.Duration;
20
21 /**
22 * Monitors a thread, interrupting it if it reaches the specified timeout.
23 * <p>
24 * This works by sleeping until the specified timeout amount and then interrupting the thread being monitored. If the
25 * thread being monitored completes its work before being interrupted, it should {@code interrupt()} the <em>monitor</em>
26 * thread.
27 * </p>
28 *
29 * <pre>
30 * Duration timeout = Duration.ofSeconds(1);
31 * try {
32 * Thread monitor = ThreadMonitor.start(timeout);
33 * // do some work here
34 * ThreadMonitor.stop(monitor);
35 * } catch (InterruptedException e) {
36 * // timed amount was reached
37 * }
38 * </pre>
39 */
40 final class ThreadMonitor implements Runnable {
41
42 /**
43 * Starts monitoring the current thread.
44 *
45 * @param timeout The timeout amount. or no timeout if the value is zero or less.
46 * @return The monitor thread or {@code null} if the timeout amount is not greater than zero.
47 */
48 static Thread start(final Duration timeout) {
49 return start(Thread.currentThread(), timeout);
50 }
51
52 /**
53 * Starts monitoring the specified thread.
54 *
55 * @param thread The thread to monitor
56 * @param timeout The timeout amount. or no timeout if the value is zero or less.
57 * @return The monitor thread or {@code null} if the timeout amount is not greater than zero.
58 */
59 static Thread start(final Thread thread, final Duration timeout) {
60 if (timeout.isZero() || timeout.isNegative()) {
61 return null;
62 }
63 final Thread monitor = new Thread(new ThreadMonitor(thread, timeout), ThreadMonitor.class.getSimpleName());
64 monitor.setDaemon(true);
65 monitor.start();
66 return monitor;
67 }
68
69 /**
70 * Stops monitoring the specified thread.
71 *
72 * @param thread The monitor thread, may be {@code null}.
73 */
74 static void stop(final Thread thread) {
75 if (thread != null) {
76 thread.interrupt();
77 }
78 }
79
80 private final Thread thread;
81
82 private final Duration timeout;
83
84 /**
85 * Constructs a new monitor.
86 *
87 * @param thread The thread to monitor.
88 * @param timeout The timeout amount.
89 */
90 private ThreadMonitor(final Thread thread, final Duration timeout) {
91 this.thread = thread;
92 this.timeout = timeout;
93 }
94
95 /**
96 * Sleeps until the specified timeout amount and then interrupt the thread being monitored.
97 *
98 * @see Runnable#run()
99 */
100 @Override
101 public void run() {
102 try {
103 ThreadUtils.sleep(timeout);
104 thread.interrupt();
105 } catch (final InterruptedException ignored) {
106 // timeout not reached
107 }
108 }
109 }