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