Use @Serial in util
[yangtools.git] / common / util / src / test / java / org / opendaylight / yangtools / util / concurrent / DeadlockDetectingListeningExecutorServiceTest.java
1 /*
2  * Copyright (c) 2014 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util.concurrent;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertNotNull;
12 import static org.junit.Assert.assertTrue;
13 import static org.opendaylight.yangtools.util.concurrent.AsyncNotifyingListeningExecutorServiceTest.testListenerCallback;
14 import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_CALLABLE;
15 import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE;
16 import static org.opendaylight.yangtools.util.concurrent.CommonTestUtils.SUBMIT_RUNNABLE_WITH_RESULT;
17
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures;
20 import com.google.common.util.concurrent.ListenableFuture;
21 import com.google.common.util.concurrent.ListeningExecutorService;
22 import com.google.common.util.concurrent.MoreExecutors;
23 import com.google.common.util.concurrent.ThreadFactoryBuilder;
24 import java.io.Serial;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.Executor;
28 import java.util.concurrent.ExecutorService;
29 import java.util.concurrent.Executors;
30 import java.util.concurrent.TimeUnit;
31 import java.util.concurrent.atomic.AtomicReference;
32 import java.util.function.Supplier;
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.opendaylight.yangtools.util.concurrent.CommonTestUtils.Invoker;
37
38 /**
39  * Unit tests for DeadlockDetectingListeningExecutorService.
40  *
41  * @author Thomas Pantelis
42  */
43 public class DeadlockDetectingListeningExecutorServiceTest {
44
45     interface InitialInvoker {
46         void invokeExecutor(ListeningExecutorService executor, Runnable task);
47     }
48
49     static final InitialInvoker SUBMIT = ListeningExecutorService::submit;
50
51     static final InitialInvoker EXECUTE = Executor::execute;
52
53     public static class TestDeadlockException extends Exception {
54         @Serial
55         private static final long serialVersionUID = 1L;
56     }
57
58     private static final Supplier<Exception> DEADLOCK_EXECUTOR_SUPPLIER = TestDeadlockException::new;
59
60     DeadlockDetectingListeningExecutorService executor;
61
62     @Before
63     public void setup() {
64     }
65
66     @After
67     public void tearDown() {
68         if (executor != null) {
69             executor.shutdownNow();
70         }
71     }
72
73     DeadlockDetectingListeningExecutorService newExecutor() {
74         return new DeadlockDetectingListeningExecutorService(Executors.newSingleThreadExecutor(),
75                 DEADLOCK_EXECUTOR_SUPPLIER);
76     }
77
78     @Test
79     public void testBlockingSubmitOffExecutor() throws Exception {
80
81         executor = newExecutor();
82
83         // Test submit with Callable.
84
85         ListenableFuture<String> future = executor.submit(() -> "foo");
86
87         assertEquals("Future result", "foo", future.get(5, TimeUnit.SECONDS));
88
89         // Test submit with Runnable.
90
91         executor.submit(() -> { }).get();
92
93         // Test submit with Runnable and value.
94
95         future = executor.submit(() -> { }, "foo");
96
97         assertEquals("Future result", "foo", future.get(5, TimeUnit.SECONDS));
98     }
99
100     @Test
101     @SuppressWarnings("checkstyle:illegalThrows")
102     public void testNonBlockingSubmitOnExecutorThread() throws Throwable {
103
104         executor = newExecutor();
105
106         testNonBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_CALLABLE);
107         testNonBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE);
108         testNonBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT);
109
110         testNonBlockingSubmitOnExecutorThread(EXECUTE, SUBMIT_CALLABLE);
111     }
112
113     @SuppressWarnings("checkstyle:illegalThrows")
114     void testNonBlockingSubmitOnExecutorThread(final InitialInvoker initialInvoker, final Invoker invoker)
115             throws Throwable {
116
117         final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
118         final CountDownLatch futureCompletedLatch = new CountDownLatch(1);
119
120         Runnable task = () -> Futures.addCallback(invoker.invokeExecutor(executor, null), new FutureCallback<Object>() {
121             @Override
122             public void onSuccess(final Object result) {
123                 futureCompletedLatch.countDown();
124             }
125
126             @Override
127             @SuppressWarnings("checkstyle:parameterName")
128             public void onFailure(final Throwable t) {
129                 caughtEx.set(t);
130                 futureCompletedLatch.countDown();
131             }
132         }, MoreExecutors.directExecutor());
133
134         initialInvoker.invokeExecutor(executor, task);
135
136         assertTrue("Task did not complete - executor likely deadlocked",
137                 futureCompletedLatch.await(5, TimeUnit.SECONDS));
138
139         if (caughtEx.get() != null) {
140             throw caughtEx.get();
141         }
142     }
143
144     @Test
145     public void testBlockingSubmitOnExecutorThread() throws InterruptedException {
146
147         executor = newExecutor();
148
149         testBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_CALLABLE);
150         testBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE);
151         testBlockingSubmitOnExecutorThread(SUBMIT, SUBMIT_RUNNABLE_WITH_RESULT);
152
153         testBlockingSubmitOnExecutorThread(EXECUTE, SUBMIT_CALLABLE);
154     }
155
156     @SuppressWarnings("checkstyle:illegalCatch")
157     void testBlockingSubmitOnExecutorThread(final InitialInvoker initialInvoker, final Invoker invoker)
158             throws InterruptedException {
159
160         final AtomicReference<Throwable> caughtEx = new AtomicReference<>();
161         final CountDownLatch latch = new CountDownLatch(1);
162
163         Runnable task = () -> {
164
165             try {
166                 invoker.invokeExecutor(executor, null).get();
167             } catch (ExecutionException e) {
168                 caughtEx.set(e.getCause());
169             } catch (Throwable e) {
170                 caughtEx.set(e);
171             } finally {
172                 latch.countDown();
173             }
174         };
175
176         initialInvoker.invokeExecutor(executor, task);
177
178         assertTrue("Task did not complete - executor likely deadlocked", latch.await(5, TimeUnit.SECONDS));
179         assertNotNull("Expected exception thrown", caughtEx.get());
180         assertEquals("Caught exception type", TestDeadlockException.class, caughtEx.get().getClass());
181     }
182
183     @Test
184     public void testListenableFutureCallbackWithExecutor() throws InterruptedException {
185
186         String listenerThreadPrefix = "ListenerThread";
187         ExecutorService listenerExecutor = Executors.newFixedThreadPool(1,
188                 new ThreadFactoryBuilder().setNameFormat(listenerThreadPrefix + "-%d").build());
189
190         executor = new DeadlockDetectingListeningExecutorService(
191             Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("SingleThread").build()),
192                 DEADLOCK_EXECUTOR_SUPPLIER, listenerExecutor);
193
194         try {
195             testListenerCallback(executor, SUBMIT_CALLABLE, listenerThreadPrefix);
196             testListenerCallback(executor, SUBMIT_RUNNABLE, listenerThreadPrefix);
197             testListenerCallback(executor, SUBMIT_RUNNABLE_WITH_RESULT, listenerThreadPrefix);
198         } finally {
199             listenerExecutor.shutdownNow();
200         }
201     }
202 }