All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.mina.filter.executor.OrderedThreadPoolExecutor Maven / Gradle / Ivy

There is a newer version: 3.0.0-M2
Show newest version
/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.apache.mina.filter.executor;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.DummySession;
import org.apache.mina.core.session.IoEvent;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A {@link ThreadPoolExecutor} that maintains the order of {@link IoEvent}s.
 * 

* If you don't need to maintain the order of events per session, please use * {@link UnorderedThreadPoolExecutor}. * @author Apache MINA Project * @org.apache.xbean.XBean */ public class OrderedThreadPoolExecutor extends ThreadPoolExecutor { /** A logger for this class (commented as it breaks MDCFlter tests) */ private static final Logger LOGGER = LoggerFactory.getLogger(OrderedThreadPoolExecutor.class); /** A default value for the initial pool size */ private static final int DEFAULT_INITIAL_THREAD_POOL_SIZE = 0; /** A default value for the maximum pool size */ private static final int DEFAULT_MAX_THREAD_POOL = 16; /** A default value for the KeepAlive delay */ private static final int DEFAULT_KEEP_ALIVE = 30; private static final IoSession EXIT_SIGNAL = new DummySession(); /** A key stored into the session's attribute for the event tasks being queued */ private static final AttributeKey TASKS_QUEUE = new AttributeKey(OrderedThreadPoolExecutor.class, "tasksQueue"); /** A queue used to store the available sessions */ private final BlockingQueue waitingSessions = new LinkedBlockingQueue<>(); private final Set workers = new HashSet<>(); private volatile int largestPoolSize; private final AtomicInteger idleWorkers = new AtomicInteger(); private long completedTaskCount; private volatile boolean shutdown; private final IoEventQueueHandler eventQueueHandler; /** * Creates a default ThreadPool, with default values : * - minimum pool size is 0 * - maximum pool size is 16 * - keepAlive set to 30 seconds * - A default ThreadFactory * - All events are accepted */ public OrderedThreadPoolExecutor() { this(DEFAULT_INITIAL_THREAD_POOL_SIZE, DEFAULT_MAX_THREAD_POOL, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, Executors .defaultThreadFactory(), null); } /** * Creates a default ThreadPool, with default values : * - minimum pool size is 0 * - keepAlive set to 30 seconds * - A default ThreadFactory * - All events are accepted * * @param maximumPoolSize The maximum pool size */ public OrderedThreadPoolExecutor(int maximumPoolSize) { this(DEFAULT_INITIAL_THREAD_POOL_SIZE, maximumPoolSize, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, Executors .defaultThreadFactory(), null); } /** * Creates a default ThreadPool, with default values : * - keepAlive set to 30 seconds * - A default ThreadFactory * - All events are accepted * * @param corePoolSize The initial pool sizePoolSize * @param maximumPoolSize The maximum pool size */ public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize) { this(corePoolSize, maximumPoolSize, DEFAULT_KEEP_ALIVE, TimeUnit.SECONDS, Executors.defaultThreadFactory(), null); } /** * Creates a default ThreadPool, with default values : * - A default ThreadFactory * - All events are accepted * * @param corePoolSize The initial pool sizePoolSize * @param maximumPoolSize The maximum pool size * @param keepAliveTime Default duration for a thread * @param unit Time unit used for the keepAlive value */ public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), null); } /** * Creates a default ThreadPool, with default values : * - A default ThreadFactory * * @param corePoolSize The initial pool sizePoolSize * @param maximumPoolSize The maximum pool size * @param keepAliveTime Default duration for a thread * @param unit Time unit used for the keepAlive value * @param eventQueueHandler The queue used to store events */ public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, IoEventQueueHandler eventQueueHandler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, Executors.defaultThreadFactory(), eventQueueHandler); } /** * Creates a default ThreadPool, with default values : * - A default ThreadFactory * * @param corePoolSize The initial pool sizePoolSize * @param maximumPoolSize The maximum pool size * @param keepAliveTime Default duration for a thread * @param unit Time unit used for the keepAlive value * @param threadFactory The factory used to create threads */ public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, threadFactory, null); } /** * Creates a new instance of a OrderedThreadPoolExecutor. * * @param corePoolSize The initial pool sizePoolSize * @param maximumPoolSize The maximum pool size * @param keepAliveTime Default duration for a thread * @param unit Time unit used for the keepAlive value * @param threadFactory The factory used to create threads * @param eventQueueHandler The queue used to store events */ public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory, IoEventQueueHandler eventQueueHandler) { // We have to initialize the pool with default values (0 and 1) in order to // handle the exception in a better way. We can't add a try {} catch() {} // around the super() call. super(DEFAULT_INITIAL_THREAD_POOL_SIZE, 1, keepAliveTime, unit, new SynchronousQueue(), threadFactory, new AbortPolicy()); if (corePoolSize < DEFAULT_INITIAL_THREAD_POOL_SIZE) { throw new IllegalArgumentException("corePoolSize: " + corePoolSize); } if ((maximumPoolSize <= 0) || (maximumPoolSize < corePoolSize)) { throw new IllegalArgumentException("maximumPoolSize: " + maximumPoolSize); } // Now, we can setup the pool sizes super.setMaximumPoolSize(maximumPoolSize); super.setCorePoolSize(corePoolSize); // The queueHandler might be null. if (eventQueueHandler == null) { this.eventQueueHandler = IoEventQueueHandler.NOOP; } else { this.eventQueueHandler = eventQueueHandler; } } /** * Get the session's tasks queue. */ private SessionTasksQueue getSessionTasksQueue(IoSession session) { SessionTasksQueue queue = (SessionTasksQueue) session.getAttribute(TASKS_QUEUE); if (queue == null) { queue = new SessionTasksQueue(); SessionTasksQueue oldQueue = (SessionTasksQueue) session.setAttributeIfAbsent(TASKS_QUEUE, queue); if (oldQueue != null) { queue = oldQueue; } } return queue; } /** * @return The associated queue handler. */ public IoEventQueueHandler getQueueHandler() { return eventQueueHandler; } /** * {@inheritDoc} */ @Override public void setRejectedExecutionHandler(RejectedExecutionHandler handler) { // Ignore the request. It must always be AbortPolicy. } /** * Add a new thread to execute a task, if needed and possible. * It depends on the current pool size. If it's full, we do nothing. */ private void addWorker() { synchronized (workers) { if (workers.size() >= super.getMaximumPoolSize()) { return; } // Create a new worker, and add it to the thread pool Worker worker = new Worker(); Thread thread = getThreadFactory().newThread(worker); // As we have added a new thread, it's considered as idle. idleWorkers.incrementAndGet(); // Now, we can start it. thread.start(); workers.add(worker); if (workers.size() > largestPoolSize) { largestPoolSize = workers.size(); } } } /** * Add a new Worker only if there are no idle worker. */ private void addWorkerIfNecessary() { if (idleWorkers.get() == 0) { synchronized (workers) { if (workers.isEmpty() || (idleWorkers.get() == 0)) { addWorker(); } } } } private void removeWorker() { synchronized (workers) { if (workers.size() <= super.getCorePoolSize()) { return; } waitingSessions.offer(EXIT_SIGNAL); } } /** * {@inheritDoc} */ @Override public void setMaximumPoolSize(int maximumPoolSize) { if ((maximumPoolSize <= 0) || (maximumPoolSize < super.getCorePoolSize())) { throw new IllegalArgumentException("maximumPoolSize: " + maximumPoolSize); } synchronized (workers) { super.setMaximumPoolSize(maximumPoolSize); int difference = workers.size() - maximumPoolSize; while (difference > 0) { removeWorker(); --difference; } } } /** * {@inheritDoc} */ @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long deadline = System.currentTimeMillis() + unit.toMillis(timeout); synchronized (workers) { while (!isTerminated()) { long waitTime = deadline - System.currentTimeMillis(); if (waitTime <= 0) { break; } workers.wait(waitTime); } } return isTerminated(); } /** * {@inheritDoc} */ @Override public boolean isShutdown() { return shutdown; } /** * {@inheritDoc} */ @Override public boolean isTerminated() { if (!shutdown) { return false; } synchronized (workers) { return workers.isEmpty(); } } /** * {@inheritDoc} */ @Override public void shutdown() { if (shutdown) { return; } shutdown = true; synchronized (workers) { for (int i = workers.size(); i > 0; i--) { waitingSessions.offer(EXIT_SIGNAL); } } } /** * {@inheritDoc} */ @Override public List shutdownNow() { shutdown(); List answer = new ArrayList<>(); IoSession session; while ((session = waitingSessions.poll()) != null) { if (session == EXIT_SIGNAL) { waitingSessions.offer(EXIT_SIGNAL); Thread.yield(); // Let others take the signal. continue; } SessionTasksQueue sessionTasksQueue = (SessionTasksQueue) session.getAttribute(TASKS_QUEUE); synchronized (sessionTasksQueue.tasksQueue) { for (Runnable task : sessionTasksQueue.tasksQueue) { getQueueHandler().polled(this, (IoEvent) task); answer.add(task); } sessionTasksQueue.tasksQueue.clear(); } } return answer; } /** * A Helper class used to print the list of events being queued. */ private void print(Queue queue, IoEvent event) { StringBuilder sb = new StringBuilder(); sb.append("Adding event ").append(event.getType()).append(" to session ").append(event.getSession().getId()); boolean first = true; sb.append("\nQueue : ["); for (Runnable elem : queue) { if (first) { first = false; } else { sb.append(", "); } sb.append(((IoEvent) elem).getType()).append(", "); } sb.append("]\n"); if (LOGGER.isDebugEnabled()) { LOGGER.debug(sb.toString()); } } /** * {@inheritDoc} */ @Override public void execute(Runnable task) { if (shutdown) { rejectTask(task); } // Check that it's a IoEvent task checkTaskType(task); IoEvent event = (IoEvent) task; // Get the associated session IoSession session = event.getSession(); // Get the session's queue of events SessionTasksQueue sessionTasksQueue = getSessionTasksQueue(session); Queue tasksQueue = sessionTasksQueue.tasksQueue; boolean offerSession; // propose the new event to the event queue handler. If we // use a throttle queue handler, the message may be rejected // if the maximum size has been reached. boolean offerEvent = eventQueueHandler.accept(this, event); if (offerEvent) { // Ok, the message has been accepted synchronized (tasksQueue) { // Inject the event into the executor taskQueue tasksQueue.offer(event); if (sessionTasksQueue.processingCompleted) { sessionTasksQueue.processingCompleted = false; offerSession = true; } else { offerSession = false; } if (LOGGER.isDebugEnabled()) { print(tasksQueue, event); } } } else { offerSession = false; } if (offerSession) { // As the tasksQueue was empty, the task has been executed // immediately, so we can move the session to the queue // of sessions waiting for completion. waitingSessions.offer(session); } addWorkerIfNecessary(); if (offerEvent) { eventQueueHandler.offered(this, event); } } private void rejectTask(Runnable task) { getRejectedExecutionHandler().rejectedExecution(task, this); } private void checkTaskType(Runnable task) { if (!(task instanceof IoEvent)) { throw new IllegalArgumentException("task must be an IoEvent or its subclass."); } } /** * {@inheritDoc} */ @Override public int getActiveCount() { synchronized (workers) { return workers.size() - idleWorkers.get(); } } /** * {@inheritDoc} */ @Override public long getCompletedTaskCount() { synchronized (workers) { long answer = completedTaskCount; for (Worker w : workers) { answer += w.completedTaskCount.get(); } return answer; } } /** * {@inheritDoc} */ @Override public int getLargestPoolSize() { return largestPoolSize; } /** * {@inheritDoc} */ @Override public int getPoolSize() { synchronized (workers) { return workers.size(); } } /** * {@inheritDoc} */ @Override public long getTaskCount() { return getCompletedTaskCount(); } /** * {@inheritDoc} */ @Override public boolean isTerminating() { synchronized (workers) { return isShutdown() && !isTerminated(); } } /** * {@inheritDoc} */ @Override public int prestartAllCoreThreads() { int answer = 0; synchronized (workers) { for (int i = super.getCorePoolSize() - workers.size(); i > 0; i--) { addWorker(); answer++; } } return answer; } /** * {@inheritDoc} */ @Override public boolean prestartCoreThread() { synchronized (workers) { if (workers.size() < super.getCorePoolSize()) { addWorker(); return true; } else { return false; } } } /** * {@inheritDoc} */ @Override public BlockingQueue getQueue() { throw new UnsupportedOperationException(); } /** * {@inheritDoc} */ @Override public void purge() { // Nothing to purge in this implementation. } /** * {@inheritDoc} */ @Override public boolean remove(Runnable task) { checkTaskType(task); IoEvent event = (IoEvent) task; IoSession session = event.getSession(); SessionTasksQueue sessionTasksQueue = (SessionTasksQueue) session.getAttribute(TASKS_QUEUE); if (sessionTasksQueue == null) { return false; } boolean removed; Queue tasksQueue = sessionTasksQueue.tasksQueue; synchronized (tasksQueue) { removed = tasksQueue.remove(task); } if (removed) { getQueueHandler().polled(this, event); } return removed; } /** * {@inheritDoc} */ @Override public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0) { throw new IllegalArgumentException("corePoolSize: " + corePoolSize); } if (corePoolSize > super.getMaximumPoolSize()) { throw new IllegalArgumentException("corePoolSize exceeds maximumPoolSize"); } synchronized (workers) { if (super.getCorePoolSize() > corePoolSize) { for (int i = super.getCorePoolSize() - corePoolSize; i > 0; i--) { removeWorker(); } } super.setCorePoolSize(corePoolSize); } } private class Worker implements Runnable { private AtomicLong completedTaskCount = new AtomicLong(0); private Thread thread; /** * @inheritedDoc */ @Override public void run() { thread = Thread.currentThread(); try { for (;;) { IoSession session = fetchSession(); idleWorkers.decrementAndGet(); if (session == null) { synchronized (workers) { if (workers.size() > getCorePoolSize()) { // Remove now to prevent duplicate exit. workers.remove(this); break; } } } if (session == EXIT_SIGNAL) { break; } try { if (session != null) { runTasks(getSessionTasksQueue(session)); } } finally { idleWorkers.incrementAndGet(); } } } finally { synchronized (workers) { workers.remove(this); OrderedThreadPoolExecutor.this.completedTaskCount += completedTaskCount.get(); workers.notifyAll(); } } } private IoSession fetchSession() { IoSession session = null; long currentTime = System.currentTimeMillis(); long deadline = currentTime + getKeepAliveTime(TimeUnit.MILLISECONDS); for (;;) { try { long waitTime = deadline - currentTime; if (waitTime <= 0) { break; } try { session = waitingSessions.poll(waitTime, TimeUnit.MILLISECONDS); break; } finally { if (session != null) { currentTime = System.currentTimeMillis(); } } } catch (InterruptedException e) { // Ignore. continue; } } return session; } private void runTasks(SessionTasksQueue sessionTasksQueue) { for (;;) { Runnable task; Queue tasksQueue = sessionTasksQueue.tasksQueue; synchronized (tasksQueue) { task = tasksQueue.poll(); if (task == null) { sessionTasksQueue.processingCompleted = true; break; } } eventQueueHandler.polled(OrderedThreadPoolExecutor.this, (IoEvent) task); runTask(task); } } private void runTask(Runnable task) { beforeExecute(thread, task); boolean ran = false; try { task.run(); ran = true; afterExecute(task, null); completedTaskCount.incrementAndGet(); } catch (RuntimeException e) { if (!ran) { afterExecute(task, e); } throw e; } } } /** * A class used to store the ordered list of events to be processed by the * session, and the current task state. */ private class SessionTasksQueue { /** A queue of ordered event waiting to be processed */ private final Queue tasksQueue = new ConcurrentLinkedQueue<>(); /** The current task state */ private boolean processingCompleted = true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy