• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static java.util.concurrent.TimeUnit.MILLISECONDS;
21 import static java.util.concurrent.TimeUnit.NANOSECONDS;
22 import static junit.framework.Assert.fail;
23 
24 import com.google.common.testing.TearDown;
25 import com.google.common.testing.TearDownAccepter;
26 import java.util.concurrent.TimeUnit;
27 import java.util.logging.Logger;
28 
29 /**
30  * Utilities for performing thread interruption in tests
31  *
32  * @author Kevin Bourrillion
33  * @author Chris Povirk
34  */
35 final class InterruptionUtil {
36   private static final Logger logger = Logger.getLogger(InterruptionUtil.class.getName());
37 
38   /** Runnable which will interrupt the target thread repeatedly when run. */
39   private static final class Interruptenator implements Runnable {
40     private final long everyMillis;
41     private final Thread interruptee;
42     private volatile boolean shouldStop = false;
43 
Interruptenator(Thread interruptee, long everyMillis)44     Interruptenator(Thread interruptee, long everyMillis) {
45       this.everyMillis = everyMillis;
46       this.interruptee = interruptee;
47     }
48 
49     @Override
run()50     public void run() {
51       while (true) {
52         try {
53           Thread.sleep(everyMillis);
54         } catch (InterruptedException e) {
55           // ok. just stop sleeping.
56         }
57         if (shouldStop) {
58           break;
59         }
60         interruptee.interrupt();
61       }
62     }
63 
stopInterrupting()64     void stopInterrupting() {
65       shouldStop = true;
66     }
67   }
68 
69   /** Interrupts the current thread after sleeping for the specified delay. */
requestInterruptIn(final long time, final TimeUnit unit)70   static void requestInterruptIn(final long time, final TimeUnit unit) {
71     checkNotNull(unit);
72     final Thread interruptee = Thread.currentThread();
73     new Thread(
74             new Runnable() {
75               @Override
76               public void run() {
77                 try {
78                   unit.sleep(time);
79                 } catch (InterruptedException wontHappen) {
80                   throw new AssertionError(wontHappen);
81                 }
82                 interruptee.interrupt();
83               }
84             })
85         .start();
86   }
87 
repeatedlyInterruptTestThread( long interruptPeriodMillis, TearDownAccepter tearDownAccepter)88   static void repeatedlyInterruptTestThread(
89       long interruptPeriodMillis, TearDownAccepter tearDownAccepter) {
90     final Interruptenator interruptingTask =
91         new Interruptenator(Thread.currentThread(), interruptPeriodMillis);
92     final Thread interruptingThread = new Thread(interruptingTask);
93     interruptingThread.start();
94     tearDownAccepter.addTearDown(
95         new TearDown() {
96           @Override
97           public void tearDown() throws Exception {
98             interruptingTask.stopInterrupting();
99             interruptingThread.interrupt();
100             joinUninterruptibly(interruptingThread, 2500, MILLISECONDS);
101             Thread.interrupted();
102             if (interruptingThread.isAlive()) {
103               // This will be hidden by test-output redirection:
104               logger.severe("InterruptenatorTask did not exit; future tests may be affected");
105               /*
106                * This won't do any good under JUnit 3, but I'll leave it around in
107                * case we ever switch to JUnit 4:
108                */
109               fail();
110             }
111           }
112         });
113   }
114 
115   // TODO(cpovirk): promote to Uninterruptibles, and add untimed version
joinUninterruptibly(Thread thread, long timeout, TimeUnit unit)116   private static void joinUninterruptibly(Thread thread, long timeout, TimeUnit unit) {
117     boolean interrupted = false;
118     try {
119       long remainingNanos = unit.toNanos(timeout);
120       long end = System.nanoTime() + remainingNanos;
121 
122       while (true) {
123         try {
124           // TimeUnit.timedJoin() treats negative timeouts just like zero.
125           NANOSECONDS.timedJoin(thread, remainingNanos);
126           return;
127         } catch (InterruptedException e) {
128           interrupted = true;
129           remainingNanos = end - System.nanoTime();
130         }
131       }
132     } finally {
133       if (interrupted) {
134         Thread.currentThread().interrupt();
135       }
136     }
137   }
138 }
139