View Javadoc

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          // each request thread should create one executor thread
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          // clear it so that threads can get gced
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         // each request thread should create one executor thread
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         // don't clear requestThreads since we don't want app threads to be gced
143 
144         // we check whether the app thread has died every 100 seconds so wait for like 2 mins
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                 // LOG.info("Thread: id=" + info.getThreadId() + ", name=\"" + info.getThreadName() +
158                 // "\": is an executor thread");
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             // do nothing
177             return null;
178         }
179 
180     }
181 
182 }