SignalingThreadMock.java revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.components.devtools_bridge; 6 7import java.util.concurrent.BlockingQueue; 8import java.util.concurrent.Callable; 9import java.util.concurrent.ExecutionException; 10import java.util.concurrent.ExecutorService; 11import java.util.concurrent.Executors; 12import java.util.concurrent.LinkedBlockingDeque; 13import java.util.concurrent.ScheduledExecutorService; 14import java.util.concurrent.ScheduledFuture; 15import java.util.concurrent.TimeUnit; 16import java.util.concurrent.atomic.AtomicInteger; 17 18/** 19 * Convinience class for tests. Like WebRTC threads supports posts 20 * and synchromous invokes. 21 */ 22class SignalingThreadMock { 23 // TODO: use scaleTimeout when natives for org.chromium.base get available. 24 private static final int EXECUTION_TIME_LIMIT_MS = 5000; 25 26 private final AtomicInteger mInvokationCounter = new AtomicInteger(0); 27 private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); 28 private final ScheduledExecutorService mWatchDogExecutor = 29 Executors.newSingleThreadScheduledExecutor(); 30 private ScheduledFuture<?> mWatchDogFuture; 31 private final Thread mThread; 32 private final BlockingQueue<Runnable> mExecutionQueue = new LinkedBlockingDeque<Runnable>(); 33 34 public SignalingThreadMock() { 35 mThread = new Thread() { 36 @Override 37 public void run() { 38 try { 39 runExecutionLoop(); 40 } catch (InterruptedException e) { 41 // Normal finish. 42 } 43 } 44 }; 45 mThread.start(); 46 } 47 48 private void runExecutionLoop() throws InterruptedException { 49 while (true) { 50 mExecutionQueue.take().run(); 51 } 52 } 53 54 public void invoke(final Runnable runnable) { 55 try { 56 invoke(new TestUtils.RunnableAdapter(runnable)); 57 } catch (Exception e) { 58 throw new RuntimeException(e); 59 } 60 } 61 62 public <T> T invoke(final Callable<T> callable) throws Exception { 63 if (isOnThread()) return callable.call(); 64 65 try { 66 return new InvokeWrapper<T>(callable).invoke(); 67 } catch (InterruptedException e) { 68 throw new RuntimeException(e); 69 } catch (ExecutionException e) { 70 throw (Exception) e.getCause(); 71 } 72 } 73 74 public void post(Runnable runnable) { 75 boolean success = mExecutionQueue.offer(new PostWrapper(runnable)); 76 assert success; 77 } 78 79 public void dispose() { 80 mWatchDogExecutor.shutdown(); 81 mThread.interrupt(); 82 try { 83 mThread.join(); 84 } catch (InterruptedException e) { 85 Thread.currentThread().interrupt(); 86 } 87 } 88 89 public boolean isOnThread() { 90 return Thread.currentThread() == mThread; 91 } 92 93 private void onStartedExecution(final int index, final Exception timeoutException) { 94 mWatchDogFuture = mWatchDogExecutor.schedule(new Runnable() { 95 @Override 96 public void run() { 97 throw new RuntimeException( 98 "Time limit on " + Integer.toString(index) + " invocation", 99 timeoutException); 100 } 101 }, EXECUTION_TIME_LIMIT_MS, TimeUnit.MILLISECONDS); 102 } 103 104 private void onFinishedExecution() { 105 mWatchDogFuture.cancel(false); 106 } 107 108 private abstract class WrapperBase implements Runnable { 109 private final int mIndex; 110 private final Exception mTimeoutException; 111 112 protected WrapperBase() { 113 mIndex = mInvokationCounter.incrementAndGet(); 114 mTimeoutException = new Exception("Timeout exception"); 115 } 116 117 @Override 118 public final void run() { 119 onStartedExecution(mIndex, mTimeoutException); 120 try { 121 runWrapped(); 122 } finally { 123 onFinishedExecution(); 124 } 125 } 126 127 protected abstract void runWrapped(); 128 } 129 130 private class InvokeWrapper<T> extends WrapperBase { 131 private final Callable<T> mWrapped; 132 private final TestUtils.InvokeHelper<T> mHelper = new TestUtils.InvokeHelper<T>(); 133 134 public InvokeWrapper(Callable<T> wrapped) { 135 mWrapped = wrapped; 136 } 137 138 @Override 139 protected void runWrapped() { 140 mHelper.runOnTargetThread(mWrapped); 141 } 142 143 public T invoke() throws Exception { 144 boolean success = mExecutionQueue.offer(this); 145 assert success; 146 return mHelper.takeResult(); 147 } 148 } 149 150 private class PostWrapper extends WrapperBase { 151 private final Runnable mWrapped; 152 153 public PostWrapper(Runnable wrapped) { 154 mWrapped = wrapped; 155 } 156 157 @Override 158 protected void runWrapped() { 159 mWrapped.run(); 160 } 161 } 162} 163