/* * Created on Nov 20, 2006 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu * (jactr.org) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. This library is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. You should have * received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.core.concurrent; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.commonreality.executor.GeneralThreadFactory; public class ExecutorServices { /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(ExecutorServices.class); static final public String INLINE = "Inline"; static final public String BACKGROUND = "Background"; static final public String PERIODIC = "Periodic"; static final public ExecutorService INLINE_EXECUTOR = new InlineExecutor(); static private Map<String, ExecutorService> _executors = new TreeMap<String, ExecutorService>(); static private boolean _isShuttingDown = false; static protected void initialize() { addExecutor(INLINE, INLINE_EXECUTOR); addExecutor(BACKGROUND, Executors .newSingleThreadExecutor(new GeneralThreadFactory("jACTR-Background"))); addExecutor(PERIODIC, Executors .newSingleThreadScheduledExecutor(new GeneralThreadFactory( "jACT-R Periodic"))); } static public void removeExecutor(String name) { synchronized (_executors) { ExecutorService service = _executors.remove(name); if (LOGGER.isDebugEnabled()) LOGGER.debug("Removed " + name + " : " + service); } } static public void addExecutor(String name, ExecutorService service) { synchronized (_executors) { if (_executors.containsKey(name) && !_executors.get(name).isShutdown()) throw new RuntimeException("Cannot overwrite active executors [" + name + "]"); if (LOGGER.isDebugEnabled()) LOGGER.debug("Adding executor " + name + " : " + service); _executors.put(name, service); } } static public ExecutorService getExecutor(String name) { synchronized (_executors) { if (!_isShuttingDown && !_executors.containsKey(BACKGROUND)) initialize(); return _executors.get(name); } } static private void getExecutorNames(Collection<String> container) { synchronized (_executors) { container.addAll(_executors.keySet()); } } /** * shutdown and wait for the shutdown of all the executors that are currently * installed. if millisecondsToWait is 0, it will wait indefinitely */ static public void shutdown(long millisecondsToWait) { synchronized (_executors) { _isShuttingDown = true; } Collection<String> executorNames = new ArrayList<String>(); getExecutorNames(executorNames); /* * issue shutdown */ for (String name : executorNames) { ExecutorService executor = getExecutor(name); if (executor != null && !executor.isShutdown()) executor.shutdown(); } /* * and wait */ long interval = 500; long abortAt = System.currentTimeMillis() + millisecondsToWait; if (millisecondsToWait == 0) abortAt = Long.MAX_VALUE; while (abortAt > System.currentTimeMillis() && executorNames.size() != 0) { for (String name : executorNames) { ExecutorService executor = getExecutor(name); if (executor != null) try { if (executor.awaitTermination(interval, TimeUnit.MILLISECONDS)) removeExecutor(name); else if (LOGGER.isDebugEnabled()) LOGGER .debug(name + " did not terminate after " + interval + "ms"); } catch (Exception e) { if (LOGGER.isWarnEnabled()) LOGGER.warn("Failed to terminate " + name, e); removeExecutor(name); } } /* * get the current names again.. */ executorNames.clear(); getExecutorNames(executorNames); } if (executorNames.size() != 0) { if (LOGGER.isWarnEnabled()) LOGGER.warn("Forcing unresponsive executors to terminate " + executorNames + " after " + millisecondsToWait + "ms"); for (String name : executorNames) { ExecutorService executor = getExecutor(name); if (executor != null) executor.shutdownNow(); } } synchronized (_executors) { _executors.clear(); _isShuttingDown = false; } } static private class InlineExecutor extends AbstractExecutorService { public boolean awaitTermination(long arg0, TimeUnit arg1) throws InterruptedException { return true; } public boolean isShutdown() { return false; } public boolean isTerminated() { return false; } public void shutdown() { } @SuppressWarnings("unchecked") public List<Runnable> shutdownNow() { return Collections.EMPTY_LIST; } public void execute(Runnable arg0) { arg0.run(); } } }