1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.filter.executor;
21
22 import java.util.ArrayList;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Queue;
26 import java.util.Set;
27 import java.util.concurrent.BlockingQueue;
28 import java.util.concurrent.Executors;
29 import java.util.concurrent.LinkedBlockingQueue;
30 import java.util.concurrent.RejectedExecutionHandler;
31 import java.util.concurrent.SynchronousQueue;
32 import java.util.concurrent.ThreadFactory;
33 import java.util.concurrent.ThreadPoolExecutor;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicInteger;
36
37 import org.apache.mina.core.session.AttributeKey;
38 import org.apache.mina.core.session.DummySession;
39 import org.apache.mina.core.session.IoEvent;
40 import org.apache.mina.core.session.IoSession;
41 import org.apache.mina.util.CircularQueue;
42
43
44
45
46
47
48
49
50
51
52
53 public class OrderedThreadPoolExecutor extends ThreadPoolExecutor {
54
55 private static final IoSession EXIT_SIGNAL = new DummySession();
56
57 private final AttributeKey BUFFER = new AttributeKey(getClass(), "buffer");
58 private final BlockingQueue<IoSession> waitingSessions = new LinkedBlockingQueue<IoSession>();
59
60 private final Set<Worker> workers = new HashSet<Worker>();
61
62 private volatile int corePoolSize;
63 private volatile int maximumPoolSize;
64 private volatile int largestPoolSize;
65 private final AtomicInteger idleWorkers = new AtomicInteger();
66
67 private long completedTaskCount;
68 private volatile boolean shutdown;
69
70 private final IoEventQueueHandler queueHandler;
71
72 public OrderedThreadPoolExecutor() {
73 this(16);
74 }
75
76 public OrderedThreadPoolExecutor(int maximumPoolSize) {
77 this(0, maximumPoolSize);
78 }
79
80 public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize) {
81 this(corePoolSize, maximumPoolSize, 30, TimeUnit.SECONDS);
82 }
83
84 public OrderedThreadPoolExecutor(
85 int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
86 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory());
87 }
88
89 public OrderedThreadPoolExecutor(
90 int corePoolSize, int maximumPoolSize,
91 long keepAliveTime, TimeUnit unit,
92 IoEventQueueHandler queueHandler) {
93 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), queueHandler);
94 }
95
96 public OrderedThreadPoolExecutor(
97 int corePoolSize, int maximumPoolSize,
98 long keepAliveTime, TimeUnit unit,
99 ThreadFactory threadFactory) {
100 this(corePoolSize, maximumPoolSize, keepAliveTime, unit, threadFactory, null);
101 }
102
103 public OrderedThreadPoolExecutor(
104 int corePoolSize, int maximumPoolSize,
105 long keepAliveTime, TimeUnit unit,
106 ThreadFactory threadFactory, IoEventQueueHandler queueHandler) {
107 super(0, 1, keepAliveTime, unit, new SynchronousQueue<Runnable>(), threadFactory, new AbortPolicy());
108 if (corePoolSize < 0) {
109 throw new IllegalArgumentException("corePoolSize: " + corePoolSize);
110 }
111
112 if (maximumPoolSize == 0 || maximumPoolSize < corePoolSize) {
113 throw new IllegalArgumentException("maximumPoolSize: " + maximumPoolSize);
114 }
115
116 if (queueHandler == null) {
117 queueHandler = IoEventQueueHandler.NOOP;
118 }
119
120 this.corePoolSize = corePoolSize;
121 this.maximumPoolSize = maximumPoolSize;
122 this.queueHandler = queueHandler;
123 }
124
125 public IoEventQueueHandler getQueueHandler() {
126 return queueHandler;
127 }
128
129 @Override
130 public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
131
132 }
133
134 private void addWorker() {
135 synchronized (workers) {
136 if (workers.size() >= maximumPoolSize) {
137 return;
138 }
139
140 Worker worker = new Worker();
141 Thread thread = getThreadFactory().newThread(worker);
142 idleWorkers.incrementAndGet();
143 thread.start();
144 workers.add(worker);
145
146 if (workers.size() > largestPoolSize) {
147 largestPoolSize = workers.size();
148 }
149 }
150 }
151
152 private void addWorkerIfNecessary() {
153 if (idleWorkers.get() == 0) {
154 synchronized (workers) {
155 if (workers.isEmpty() || idleWorkers.get() == 0) {
156 addWorker();
157 }
158 }
159 }
160 }
161
162 private void removeWorker() {
163 synchronized (workers) {
164 if (workers.size() <= corePoolSize) {
165 return;
166 }
167 waitingSessions.offer(EXIT_SIGNAL);
168 }
169 }
170
171 @Override
172 public int getMaximumPoolSize() {
173 return maximumPoolSize;
174 }
175
176 @Override
177 public void setMaximumPoolSize(int maximumPoolSize) {
178 if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) {
179 throw new IllegalArgumentException("maximumPoolSize: "
180 + maximumPoolSize);
181 }
182
183 synchronized (workers) {
184 this.maximumPoolSize = maximumPoolSize;
185 int difference = workers.size() - maximumPoolSize;
186 while (difference > 0) {
187 removeWorker();
188 --difference;
189 }
190 }
191 }
192
193 @Override
194 public boolean awaitTermination(long timeout, TimeUnit unit)
195 throws InterruptedException {
196
197 long deadline = System.currentTimeMillis() + unit.toMillis(timeout);
198
199 synchronized (workers) {
200 while (!isTerminated()) {
201 long waitTime = deadline - System.currentTimeMillis();
202 if (waitTime <= 0) {
203 break;
204 }
205
206 workers.wait(waitTime);
207 }
208 }
209 return isTerminated();
210 }
211
212 @Override
213 public boolean isShutdown() {
214 return shutdown;
215 }
216
217 @Override
218 public boolean isTerminated() {
219 if (!shutdown) {
220 return false;
221 }
222
223 synchronized (workers) {
224 return workers.isEmpty();
225 }
226 }
227
228 @Override
229 public void shutdown() {
230 if (shutdown) {
231 return;
232 }
233
234 shutdown = true;
235
236 synchronized (workers) {
237 for (int i = workers.size(); i > 0; i --) {
238 waitingSessions.offer(EXIT_SIGNAL);
239 }
240 }
241 }
242
243 @Override
244 public List<Runnable> shutdownNow() {
245 shutdown();
246
247 List<Runnable> answer = new ArrayList<Runnable>();
248 IoSession session;
249 while ((session = waitingSessions.poll()) != null) {
250 if (session == EXIT_SIGNAL) {
251 waitingSessions.offer(EXIT_SIGNAL);
252 Thread.yield();
253 continue;
254 }
255
256 SessionBuffer buf = (SessionBuffer) session.getAttribute(BUFFER);
257 synchronized (buf.queue) {
258 for (Runnable task: buf.queue) {
259 getQueueHandler().polled(this, (IoEvent) task);
260 answer.add(task);
261 }
262 buf.queue.clear();
263 }
264 }
265
266 return answer;
267 }
268
269 @Override
270 public void execute(Runnable task) {
271 if (shutdown) {
272 rejectTask(task);
273 }
274
275 checkTaskType(task);
276
277 IoEvent e = (IoEvent) task;
278 IoSession s = e.getSession();
279 SessionBuffer buf = getSessionBuffer(s);
280 Queue<Runnable> queue = buf.queue;
281 boolean offerSession;
282 boolean offerEvent = queueHandler.accept(this, e);
283 if (offerEvent) {
284 synchronized (queue) {
285 queue.offer(e);
286 if (buf.processingCompleted) {
287 buf.processingCompleted = false;
288 offerSession = true;
289 } else {
290 offerSession = false;
291 }
292 }
293 } else {
294 offerSession = false;
295 }
296
297 if (offerSession) {
298 waitingSessions.offer(s);
299 }
300
301 addWorkerIfNecessary();
302
303 if (offerEvent) {
304 queueHandler.offered(this, e);
305 }
306 }
307
308 private void rejectTask(Runnable task) {
309 getRejectedExecutionHandler().rejectedExecution(task, this);
310 }
311
312 private void checkTaskType(Runnable task) {
313 if (!(task instanceof IoEvent)) {
314 throw new IllegalArgumentException("task must be an IoEvent or its subclass.");
315 }
316 }
317
318 @Override
319 public int getActiveCount() {
320 synchronized (workers) {
321 return workers.size() - idleWorkers.get();
322 }
323 }
324
325 @Override
326 public long getCompletedTaskCount() {
327 synchronized (workers) {
328 long answer = completedTaskCount;
329 for (Worker w: workers) {
330 answer += w.completedTaskCount;
331 }
332
333 return answer;
334 }
335 }
336
337 @Override
338 public int getLargestPoolSize() {
339 return largestPoolSize;
340 }
341
342 @Override
343 public int getPoolSize() {
344 synchronized (workers) {
345 return workers.size();
346 }
347 }
348
349 @Override
350 public long getTaskCount() {
351 return getCompletedTaskCount();
352 }
353
354 @Override
355 public boolean isTerminating() {
356 synchronized (workers) {
357 return isShutdown() && !isTerminated();
358 }
359 }
360
361 @Override
362 public int prestartAllCoreThreads() {
363 int answer = 0;
364 synchronized (workers) {
365 for (int i = corePoolSize - workers.size() ; i > 0; i --) {
366 addWorker();
367 answer ++;
368 }
369 }
370 return answer;
371 }
372
373 @Override
374 public boolean prestartCoreThread() {
375 synchronized (workers) {
376 if (workers.size() < corePoolSize) {
377 addWorker();
378 return true;
379 } else {
380 return false;
381 }
382 }
383 }
384
385 @Override
386 public BlockingQueue<Runnable> getQueue() {
387 throw new UnsupportedOperationException();
388 }
389
390 @Override
391 public void purge() {
392
393 }
394
395 @Override
396 public boolean remove(Runnable task) {
397 checkTaskType(task);
398 IoEvent e = (IoEvent) task;
399 IoSession s = e.getSession();
400 SessionBuffer buffer = (SessionBuffer) s.getAttribute(BUFFER);
401 if (buffer == null) {
402 return false;
403 }
404
405 boolean removed;
406 synchronized (buffer.queue) {
407 removed = buffer.queue.remove(task);
408 }
409
410 if (removed) {
411 getQueueHandler().polled(this, e);
412 }
413
414 return removed;
415 }
416
417 @Override
418 public int getCorePoolSize() {
419 return corePoolSize;
420 }
421
422 @Override
423 public void setCorePoolSize(int corePoolSize) {
424 if (corePoolSize < 0) {
425 throw new IllegalArgumentException("corePoolSize: " + corePoolSize);
426 }
427 if (corePoolSize > maximumPoolSize) {
428 throw new IllegalArgumentException("corePoolSize exceeds maximumPoolSize");
429 }
430
431 synchronized (workers) {
432 if (this.corePoolSize > corePoolSize) {
433 for (int i = this.corePoolSize - corePoolSize; i > 0; i --) {
434 removeWorker();
435 }
436 }
437 this.corePoolSize = corePoolSize;
438 }
439 }
440
441 private SessionBuffer getSessionBuffer(IoSession session) {
442 SessionBuffer buffer = (SessionBuffer) session.getAttribute(BUFFER);
443 if (buffer == null) {
444 buffer = new SessionBuffer();
445 SessionBuffer oldBuffer = (SessionBuffer) session.setAttributeIfAbsent(BUFFER, buffer);
446 if (oldBuffer != null) {
447 buffer = oldBuffer;
448 }
449 }
450 return buffer;
451 }
452
453 private static class SessionBuffer {
454 private final Queue<Runnable> queue = new CircularQueue<Runnable>();
455 private boolean processingCompleted = true;
456 }
457
458 private class Worker implements Runnable {
459
460 private volatile long completedTaskCount;
461 private Thread thread;
462
463 public void run() {
464 thread = Thread.currentThread();
465
466 try {
467 for (;;) {
468 IoSession session = fetchSession();
469
470 idleWorkers.decrementAndGet();
471
472 if (session == null) {
473 synchronized (workers) {
474 if (workers.size() > corePoolSize) {
475
476 workers.remove(this);
477 break;
478 }
479 }
480 }
481
482 if (session == EXIT_SIGNAL) {
483 break;
484 }
485
486 try {
487 if (session != null) {
488 runTasks(getSessionBuffer(session));
489 }
490 } finally {
491 idleWorkers.incrementAndGet();
492 }
493 }
494 } finally {
495 synchronized (workers) {
496 workers.remove(this);
497 OrderedThreadPoolExecutor.this.completedTaskCount += completedTaskCount;
498 workers.notifyAll();
499 }
500 }
501 }
502
503 private IoSession fetchSession() {
504 IoSession session = null;
505 long currentTime = System.currentTimeMillis();
506 long deadline = currentTime + getKeepAliveTime(TimeUnit.MILLISECONDS);
507 for (;;) {
508 try {
509 long waitTime = deadline - currentTime;
510 if (waitTime <= 0) {
511 break;
512 }
513
514 try {
515 session = waitingSessions.poll(waitTime, TimeUnit.MILLISECONDS);
516 break;
517 } finally {
518 if (session == null) {
519 currentTime = System.currentTimeMillis();
520 }
521 }
522 } catch (InterruptedException e) {
523
524 continue;
525 }
526 }
527 return session;
528 }
529
530 private void runTasks(SessionBuffer buf) {
531 for (;;) {
532 Runnable task;
533 synchronized (buf.queue) {
534 task = buf.queue.poll();
535
536 if (task == null) {
537 buf.processingCompleted = true;
538 break;
539 }
540 }
541
542 queueHandler.polled(OrderedThreadPoolExecutor.this, (IoEvent) task);
543
544 runTask(task);
545 }
546 }
547
548 private void runTask(Runnable task) {
549 beforeExecute(thread, task);
550 boolean ran = false;
551 try {
552 task.run();
553 ran = true;
554 afterExecute(task, null);
555 completedTaskCount ++;
556 } catch (RuntimeException e) {
557 if (!ran) {
558 afterExecute(task, e);
559 }
560 throw e;
561 }
562 }
563 }
564 }