1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
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 net.sf.ehcache.constructs.nonstop;
18
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.concurrent.Callable;
22 import java.util.concurrent.ThreadFactory;
23 import java.util.concurrent.atomic.AtomicInteger;
24
25 import junit.framework.Assert;
26 import junit.framework.TestCase;
27 import net.sf.ehcache.constructs.nonstop.ThreadDump.ThreadInformation;
28
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 public class NonStopThreadDestroyedTest extends TestCase {
33
34 private static final String TEST_EXECUTOR_THREAD_NAME_PREFIX = "Test Executor thread";
35 private static final String TEST_NONSTOP_REAPER_THREAD = "non stop reaper thread";
36 private NonstopExecutorServiceImpl service;
37 private static final Logger LOG = LoggerFactory.getLogger(ExecutorServiceTest.class);
38 private final Object waitObject = new Object();
39
40 @Override
41 protected void setUp() throws Exception {
42 int initialThreadsCount = countExecutorThreads();
43 LOG.info("Initial thread count: " + initialThreadsCount);
44 Assert.assertEquals(0, initialThreadsCount);
45 service = new NonstopExecutorServiceImpl(new ThreadFactory() {
46 private final AtomicInteger count = new AtomicInteger();
47
48 public Thread newThread(Runnable r) {
49 Thread thread = new Thread(r, TEST_EXECUTOR_THREAD_NAME_PREFIX + "-" + count.incrementAndGet() + " [for '"
50 + Thread.currentThread().getName() + "']");
51 thread.setDaemon(true);
52 return thread;
53 }
54 });
55 }
56
57 @Override
58 protected void tearDown() throws Exception {
59 service.shutdown();
60 Thread.sleep(2000);
61 int threads = countExecutorThreads();
62 LOG.info("After shutting down service, thread count: " + threads);
63 Assert.assertEquals(0, threads);
64 LOG.info("Test complete successfully");
65 }
66
67 public void testNonStopThreadDestroyedAfterGC() throws Exception {
68 int initialThreadsCount = countExecutorThreads();
69
70 List<Thread> requestThreads = new ArrayList<Thread>();
71 int extraRequests = 20;
72 for (int i = 0; i < extraRequests; i++) {
73 Thread thread = new Thread(new Runnable() {
74 public void run() {
75 try {
76 service.execute(new NoopCallable(), 5000);
77 } catch (Exception e) {
78 e.printStackTrace();
79 fail("Executing noopCallable should not fail");
80 }
81 }
82 }, "RequestThread-" + i);
83 requestThreads.add(thread);
84 thread.start();
85 }
86 for (Thread t : requestThreads) {
87 t.join();
88 }
89 Assert.assertEquals(extraRequests, countExecutorThreads() - initialThreadsCount);
90
91
92 requestThreads.clear();
93
94 Thread.sleep(5000);
95
96 long remaining = 0;
97 final int waitMins = 5;
98 final long end = System.currentTimeMillis() + (waitMins * 60 * 1000);
99 do {
100 remaining = end - System.currentTimeMillis();
101 Thread.sleep(2000);
102 for (int i = 0; i < 10; i++) {
103 System.gc();
104 }
105 int numThreads = countExecutorThreads() - initialThreadsCount;
106 if (numThreads <= 0) {
107 break;
108 }
109 System.out.println("All nonstop threads not gc'ed yet (remaining: " + remaining + "ms): Expected 0 but got numThreads: "
110 + numThreads);
111 } while (remaining > 0);
112 if (remaining <= 0) {
113 fail("Nonstop threads not gc'ed even after waiting " + waitMins + " minutes");
114 }
115 Assert.assertEquals(0, countExecutorThreads() - initialThreadsCount);
116 }
117
118 public void testNonStopThreadDestroyedWithoutGC() throws Exception {
119 int initialThreadsCount = countExecutorThreads();
120
121 List<Thread> requestThreads = new ArrayList<Thread>();
122 int extraRequests = 20;
123 for (int i = 0; i < extraRequests; i++) {
124 Thread thread = new Thread(new Runnable() {
125 public void run() {
126 try {
127 service.execute(new NoopCallable(), 5000);
128 } catch (Exception e) {
129 e.printStackTrace();
130 fail("Executing noopCallable should not fail");
131 }
132 }
133 }, "RequestThread-" + i);
134 requestThreads.add(thread);
135 thread.start();
136 }
137 for (Thread t : requestThreads) {
138 t.join();
139 }
140 Assert.assertEquals(extraRequests, countExecutorThreads() - initialThreadsCount);
141
142
143
144
145 System.out.println("waiting for 2 mins...");
146 Thread.sleep(2 * 60 * 1000);
147 Assert.assertEquals(0, countExecutorThreads() - initialThreadsCount);
148 }
149
150 private int countExecutorThreads() {
151 List<ThreadInformation> threadDump = ThreadDump.getThreadDump();
152 int rv = 0;
153 List<ThreadInformation> threads = new ArrayList();
154 for (ThreadInformation info : threadDump) {
155 if (info.getThreadName().contains(TEST_EXECUTOR_THREAD_NAME_PREFIX)
156 || info.getThreadName().contains(TEST_NONSTOP_REAPER_THREAD)) {
157
158
159 threads.add(info);
160 rv++;
161 }
162 }
163 LOG.info("Counting number of executor threads created till now: " + rv);
164 String string = "{";
165 for (ThreadInformation info : threads) {
166 string += info.getThreadName() + " [id=" + info.getThreadId() + "], ";
167 }
168 string += "}";
169 LOG.info("Thread name/ids: " + string);
170 return rv;
171 }
172
173 private static class NoopCallable implements Callable<Void> {
174
175 public Void call() throws Exception {
176
177 return null;
178 }
179
180 }
181
182 }