• 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 
27 import java.util.concurrent.TimeUnit;
28 import java.util.logging.Logger;
29 
30 /**
31  * Utilities for performing thread interruption in tests
32  *
33  * @author Kevin Bourrillion
34  * @author Chris Povirk
35  */
36 final class InterruptionUtil {
37   private static final Logger logger =
38       Logger.getLogger(InterruptionUtil.class.getName());
39 
40   /**
41    * Runnable which will interrupt the target thread repeatedly when run.
42    */
43   private static final class Interruptenator implements Runnable {
44     private final long everyMillis;
45     private final Thread interruptee;
46     private volatile boolean shouldStop = false;
47 
Interruptenator(Thread interruptee, long everyMillis)48     Interruptenator(Thread interruptee, long everyMillis) {
49       this.everyMillis = everyMillis;
50       this.interruptee = interruptee;
51     }
52 
53     @Override
run()54     public void run() {
55       while (true) {
56         try {
57           Thread.sleep(everyMillis);
58         } catch (InterruptedException e) {
59           // ok. just stop sleeping.
60         }
61         if (shouldStop) {
62           break;
63         }
64         interruptee.interrupt();
65       }
66     }
67 
stopInterrupting()68     void stopInterrupting() {
69       shouldStop = true;
70     }
71   }
72 
73   /**
74    * Interrupts the current thread after sleeping for the specified delay.
75    */
requestInterruptIn(final long time, final TimeUnit unit)76   static void requestInterruptIn(final long time, final TimeUnit unit) {
77     checkNotNull(unit);
78     final Thread interruptee = Thread.currentThread();
79     new Thread(new Runnable() {
80       @Override
81       public void run() {
82         try {
83           unit.sleep(time);
84         } catch (InterruptedException wontHappen) {
85           throw new AssertionError(wontHappen);
86         }
87         interruptee.interrupt();
88       }
89     }).start();
90   }
91 
repeatedlyInterruptTestThread( long interruptPeriodMillis, TearDownAccepter tearDownAccepter)92   static void repeatedlyInterruptTestThread(
93       long interruptPeriodMillis, TearDownAccepter tearDownAccepter) {
94     final Interruptenator interruptingTask =
95         new Interruptenator(Thread.currentThread(), interruptPeriodMillis);
96     final Thread interruptingThread = new Thread(interruptingTask);
97     interruptingThread.start();
98     tearDownAccepter.addTearDown(new TearDown() {
99       @Override public void tearDown() throws Exception {
100         interruptingTask.stopInterrupting();
101         interruptingThread.interrupt();
102         joinUninterruptibly(interruptingThread, 2500, MILLISECONDS);
103         Thread.interrupted();
104         if (interruptingThread.isAlive()) {
105           // This will be hidden by test-output redirection:
106           logger.severe(
107               "InterruptenatorTask did not exit; future tests may be affected");
108           /*
109            * This won't do any good under JUnit 3, but I'll leave it around in
110            * case we ever switch to JUnit 4:
111            */
112           fail();
113         }
114       }
115     });
116   }
117 
118   // TODO(cpovirk): promote to Uninterruptibles, and add untimed version
joinUninterruptibly( Thread thread, long timeout, TimeUnit unit)119   private static void joinUninterruptibly(
120       Thread thread, long timeout, TimeUnit unit) {
121     boolean interrupted = false;
122     try {
123       long remainingNanos = unit.toNanos(timeout);
124       long end = System.nanoTime() + remainingNanos;
125 
126       while (true) {
127         try {
128           // TimeUnit.timedJoin() treats negative timeouts just like zero.
129           NANOSECONDS.timedJoin(thread, remainingNanos);
130           return;
131         } catch (InterruptedException e) {
132           interrupted = true;
133           remainingNanos = end - System.nanoTime();
134         }
135       }
136     } finally {
137       if (interrupted) {
138         Thread.currentThread().interrupt();
139       }
140     }
141   }
142 }
143