1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.util;
18
19 import io.netty.util.concurrent.DefaultThreadFactory;
20 import io.netty.util.internal.ObjectUtil;
21 import io.netty.util.internal.StringUtil;
22 import io.netty.util.internal.SystemPropertyUtil;
23 import io.netty.util.internal.logging.InternalLogger;
24 import io.netty.util.internal.logging.InternalLoggerFactory;
25
26 import java.security.AccessController;
27 import java.security.PrivilegedAction;
28 import java.util.ArrayList;
29 import java.util.List;
30 import java.util.Queue;
31 import java.util.concurrent.ConcurrentLinkedQueue;
32 import java.util.concurrent.ThreadFactory;
33 import java.util.concurrent.TimeUnit;
34 import java.util.concurrent.atomic.AtomicBoolean;
35
36
37
38
39
40
41
42
43
44
45
46 @Deprecated
47 public final class ThreadDeathWatcher {
48
49 private static final InternalLogger logger = InternalLoggerFactory.getInstance(ThreadDeathWatcher.class);
50
51 static final ThreadFactory threadFactory;
52
53
54
55 private static final Queue<Entry> pendingEntries = new ConcurrentLinkedQueue<Entry>();
56 private static final Watcher watcher = new Watcher();
57 private static final AtomicBoolean started = new AtomicBoolean();
58 private static volatile Thread watcherThread;
59
60 static {
61 String poolName = "threadDeathWatcher";
62 String serviceThreadPrefix = SystemPropertyUtil.get("io.netty.serviceThreadPrefix");
63 if (!StringUtil.isNullOrEmpty(serviceThreadPrefix)) {
64 poolName = serviceThreadPrefix + poolName;
65 }
66
67
68
69 threadFactory = new DefaultThreadFactory(poolName, true, Thread.MIN_PRIORITY, null);
70 }
71
72
73
74
75
76
77
78
79
80 public static void watch(Thread thread, Runnable task) {
81 ObjectUtil.checkNotNull(thread, "thread");
82 ObjectUtil.checkNotNull(task, "task");
83
84 if (!thread.isAlive()) {
85 throw new IllegalArgumentException("thread must be alive.");
86 }
87
88 schedule(thread, task, true);
89 }
90
91
92
93
94 public static void unwatch(Thread thread, Runnable task) {
95 schedule(ObjectUtil.checkNotNull(thread, "thread"),
96 ObjectUtil.checkNotNull(task, "task"),
97 false);
98 }
99
100 private static void schedule(Thread thread, Runnable task, boolean isWatch) {
101 pendingEntries.add(new Entry(thread, task, isWatch));
102
103 if (started.compareAndSet(false, true)) {
104 final Thread watcherThread = threadFactory.newThread(watcher);
105
106
107
108
109
110 AccessController.doPrivileged(new PrivilegedAction<Void>() {
111 @Override
112 public Void run() {
113 watcherThread.setContextClassLoader(null);
114 return null;
115 }
116 });
117
118 watcherThread.start();
119 ThreadDeathWatcher.watcherThread = watcherThread;
120 }
121 }
122
123
124
125
126
127
128
129
130
131
132 public static boolean awaitInactivity(long timeout, TimeUnit unit) throws InterruptedException {
133 ObjectUtil.checkNotNull(unit, "unit");
134
135 Thread watcherThread = ThreadDeathWatcher.watcherThread;
136 if (watcherThread != null) {
137 watcherThread.join(unit.toMillis(timeout));
138 return !watcherThread.isAlive();
139 } else {
140 return true;
141 }
142 }
143
144 private ThreadDeathWatcher() { }
145
146 private static final class Watcher implements Runnable {
147
148 private final List<Entry> watchees = new ArrayList<Entry>();
149
150 @Override
151 public void run() {
152 for (;;) {
153 fetchWatchees();
154 notifyWatchees();
155
156
157 fetchWatchees();
158 notifyWatchees();
159
160 try {
161 Thread.sleep(1000);
162 } catch (InterruptedException ignore) {
163
164 }
165
166 if (watchees.isEmpty() && pendingEntries.isEmpty()) {
167
168
169
170
171 boolean stopped = started.compareAndSet(true, false);
172 assert stopped;
173
174
175 if (pendingEntries.isEmpty()) {
176
177
178
179
180 break;
181 }
182
183
184 if (!started.compareAndSet(false, true)) {
185
186
187 break;
188 }
189
190
191
192
193 }
194 }
195 }
196
197 private void fetchWatchees() {
198 for (;;) {
199 Entry e = pendingEntries.poll();
200 if (e == null) {
201 break;
202 }
203
204 if (e.isWatch) {
205 watchees.add(e);
206 } else {
207 watchees.remove(e);
208 }
209 }
210 }
211
212 private void notifyWatchees() {
213 List<Entry> watchees = this.watchees;
214 for (int i = 0; i < watchees.size();) {
215 Entry e = watchees.get(i);
216 if (!e.thread.isAlive()) {
217 watchees.remove(i);
218 try {
219 e.task.run();
220 } catch (Throwable t) {
221 logger.warn("Thread death watcher task raised an exception:", t);
222 }
223 } else {
224 i ++;
225 }
226 }
227 }
228 }
229
230 private static final class Entry {
231 final Thread thread;
232 final Runnable task;
233 final boolean isWatch;
234
235 Entry(Thread thread, Runnable task, boolean isWatch) {
236 this.thread = thread;
237 this.task = task;
238 this.isWatch = isWatch;
239 }
240
241 @Override
242 public int hashCode() {
243 return thread.hashCode() ^ task.hashCode();
244 }
245
246 @Override
247 public boolean equals(Object obj) {
248 if (obj == this) {
249 return true;
250 }
251
252 if (!(obj instanceof Entry)) {
253 return false;
254 }
255
256 Entry that = (Entry) obj;
257 return thread == that.thread && task == that.task;
258 }
259 }
260 }