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