1 /* 2 * libjingle 3 * Copyright 2015 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 package org.webrtc; 29 30 import android.os.Handler; 31 import android.os.SystemClock; 32 33 import java.util.concurrent.Callable; 34 import java.util.concurrent.CountDownLatch; 35 import java.util.concurrent.TimeUnit; 36 37 public class ThreadUtils { 38 /** 39 * Utility class to be used for checking that a method is called on the correct thread. 40 */ 41 public static class ThreadChecker { 42 private Thread thread = Thread.currentThread(); 43 checkIsOnValidThread()44 public void checkIsOnValidThread() { 45 if (thread == null) { 46 thread = Thread.currentThread(); 47 } 48 if (Thread.currentThread() != thread) { 49 throw new IllegalStateException("Wrong thread"); 50 } 51 } 52 detachThread()53 public void detachThread() { 54 thread = null; 55 } 56 } 57 58 /** 59 * Utility interface to be used with executeUninterruptibly() to wait for blocking operations 60 * to complete without getting interrupted.. 61 */ 62 public interface BlockingOperation { run()63 void run() throws InterruptedException; 64 } 65 66 /** 67 * Utility method to make sure a blocking operation is executed to completion without getting 68 * interrupted. This should be used in cases where the operation is waiting for some critical 69 * work, e.g. cleanup, that must complete before returning. If the thread is interrupted during 70 * the blocking operation, this function will re-run the operation until completion, and only then 71 * re-interrupt the thread. 72 */ executeUninterruptibly(BlockingOperation operation)73 public static void executeUninterruptibly(BlockingOperation operation) { 74 boolean wasInterrupted = false; 75 while (true) { 76 try { 77 operation.run(); 78 break; 79 } catch (InterruptedException e) { 80 // Someone is asking us to return early at our convenience. We can't cancel this operation, 81 // but we should preserve the information and pass it along. 82 wasInterrupted = true; 83 } 84 } 85 // Pass interruption information along. 86 if (wasInterrupted) { 87 Thread.currentThread().interrupt(); 88 } 89 } 90 joinUninterruptibly(final Thread thread, long timeoutMs)91 public static boolean joinUninterruptibly(final Thread thread, long timeoutMs) { 92 final long startTimeMs = SystemClock.elapsedRealtime(); 93 long timeRemainingMs = timeoutMs; 94 boolean wasInterrupted = false; 95 while (timeRemainingMs > 0) { 96 try { 97 thread.join(timeRemainingMs); 98 break; 99 } catch (InterruptedException e) { 100 // Someone is asking us to return early at our convenience. We can't cancel this operation, 101 // but we should preserve the information and pass it along. 102 wasInterrupted = true; 103 final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs; 104 timeRemainingMs = timeoutMs - elapsedTimeMs; 105 } 106 } 107 // Pass interruption information along. 108 if (wasInterrupted) { 109 Thread.currentThread().interrupt(); 110 } 111 return !thread.isAlive(); 112 } 113 joinUninterruptibly(final Thread thread)114 public static void joinUninterruptibly(final Thread thread) { 115 executeUninterruptibly(new BlockingOperation() { 116 @Override 117 public void run() throws InterruptedException { 118 thread.join(); 119 } 120 }); 121 } 122 awaitUninterruptibly(final CountDownLatch latch)123 public static void awaitUninterruptibly(final CountDownLatch latch) { 124 executeUninterruptibly(new BlockingOperation() { 125 @Override 126 public void run() throws InterruptedException { 127 latch.await(); 128 } 129 }); 130 } 131 awaitUninterruptibly(CountDownLatch barrier, long timeoutMs)132 public static boolean awaitUninterruptibly(CountDownLatch barrier, long timeoutMs) { 133 final long startTimeMs = SystemClock.elapsedRealtime(); 134 long timeRemainingMs = timeoutMs; 135 boolean wasInterrupted = false; 136 boolean result = false; 137 do { 138 try { 139 result = barrier.await(timeRemainingMs, TimeUnit.MILLISECONDS); 140 break; 141 } catch (InterruptedException e) { 142 // Someone is asking us to return early at our convenience. We can't cancel this operation, 143 // but we should preserve the information and pass it along. 144 wasInterrupted = true; 145 final long elapsedTimeMs = SystemClock.elapsedRealtime() - startTimeMs; 146 timeRemainingMs = timeoutMs - elapsedTimeMs; 147 } 148 } while (timeRemainingMs > 0); 149 // Pass interruption information along. 150 if (wasInterrupted) { 151 Thread.currentThread().interrupt(); 152 } 153 return result; 154 } 155 156 /** 157 * Post |callable| to |handler| and wait for the result. 158 */ invokeUninterruptibly(final Handler handler, final Callable<V> callable)159 public static <V> V invokeUninterruptibly(final Handler handler, final Callable<V> callable) { 160 class Result { 161 public V value; 162 } 163 final Result result = new Result(); 164 final CountDownLatch barrier = new CountDownLatch(1); 165 handler.post(new Runnable() { 166 @Override public void run() { 167 try { 168 result.value = callable.call(); 169 } catch (Exception e) { 170 throw new RuntimeException("Callable threw exception: " + e); 171 } 172 barrier.countDown(); 173 } 174 }); 175 awaitUninterruptibly(barrier); 176 return result.value; 177 } 178 179 /** 180 * Post |runner| to |handler| and wait for the result. 181 */ invokeUninterruptibly(final Handler handler, final Runnable runner)182 public static void invokeUninterruptibly(final Handler handler, final Runnable runner) { 183 final CountDownLatch barrier = new CountDownLatch(1); 184 handler.post(new Runnable() { 185 @Override public void run() { 186 runner.run(); 187 barrier.countDown(); 188 } 189 }); 190 awaitUninterruptibly(barrier); 191 } 192 } 193