/* * Copyright (c) 2012-2014, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are dual-licensed under * either the terms of the Eclipse Public License v1.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) * * under the terms of the GNU Lesser General Public License version 3.0 * as published by the Free Software Foundation. */ package co.paralleluniverse.common.concurrent; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Executor; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Adapted from Netty's org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor * * @author pron */ public abstract class OrderedThreadPoolExecutor extends ThreadPoolExecutor { private static final Logger LOG = LoggerFactory.getLogger(OrderedThreadPoolExecutor.class); protected final ConcurrentMap<Object, Executor> childExecutors = new ConcurrentHashMap<Object, Executor>(); private final int maxQueueSize; public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, int maxQueueSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new SynchronousQueue<Runnable>(), threadFactory, handler); this.maxQueueSize = maxQueueSize; } public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, int maxQueueSize, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new SynchronousQueue<Runnable>(), handler); this.maxQueueSize = maxQueueSize; } public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, int maxQueueSize, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new SynchronousQueue<Runnable>(), threadFactory); this.maxQueueSize = maxQueueSize; } public OrderedThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, int maxQueueSize) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new SynchronousQueue<Runnable>()); this.maxQueueSize = maxQueueSize; } @Override public void execute(Runnable task) { assert !(task instanceof ChildExecutor); getChildExecutor(task).execute(task); } protected abstract Object getChildExecutorKey(Runnable task); protected boolean removeChildExecutor(Object key) { // FIXME: Succeed only when there is no task in the ChildExecutor's queue. // Note that it will need locking which might slow down task submission. return childExecutors.remove(key) != null; } protected Executor getChildExecutor(Runnable task) { Object key = getChildExecutorKey(task); Executor executor = childExecutors.get(key); if (executor == null) { executor = new ChildExecutor(); Executor oldExecutor = childExecutors.putIfAbsent(key, executor); if (oldExecutor != null) { executor = oldExecutor; } } return executor; } private final class ChildExecutor implements Executor, Runnable { private final Queue<Runnable> tasks = QueueFactory.getInstance(maxQueueSize); private boolean running; @Override public void execute(Runnable command) { boolean start = false; synchronized (this) { try { tasks.add(command); // TODO: What todo if the add return false ? } catch (IllegalStateException ex) { LOG.error("my queue full", ex); throw ex; } if (!running) { running = true; start = true; } } if (start) { try { OrderedThreadPoolExecutor.super.execute(this); } catch (Exception e) { LOG.error("exexution failed. poolsize {}. activeCount {}", OrderedThreadPoolExecutor.super.getPoolSize(), OrderedThreadPoolExecutor.super.getActiveCount()); throw e; } } } @Override public void run() { Thread thread = Thread.currentThread(); for (;;) { final Runnable task; synchronized (this) { task = tasks.poll(); if (task == null) { running = false; break; } } beforeExecute(thread, task); try { task.run(); afterExecute(task, null); } catch (RuntimeException e) { afterExecute(task, e); LOG.error("Error while executing task " + task, e); } } } } }