/* * PS3 Media Server, for streaming any medias to your PS3. * Copyright (C) 2011 G.Zsombor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License only. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.pms.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Background task executor and scheduler with a dynamic thread pool, where the threads are daemons. * * @author zsombor * */ public class TaskRunner { final static Logger logger = LoggerFactory.getLogger(TaskRunner.class); private static TaskRunner instance; public static synchronized TaskRunner getInstance() { if (instance == null) { instance = new TaskRunner(); } return instance; } private final ExecutorService executors = Executors.newCachedThreadPool(new ThreadFactory() { int counter = 0; @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, "background-task-" + (counter++)); t.setDaemon(true); return t; } }); private final Map<String, Integer> counters = new HashMap<String, Integer>(); private final Map<String, Lock> uniquenessLock = new HashMap<String, Lock> (); public void submit(Runnable runnable) { executors.execute(runnable); } public <X> Future<X> submit(Callable<X> call) { return executors.submit(call); } /** * Submit a named task for later execution. * * @param name * @param runnable */ public void submitNamed(final String name, final Runnable runnable) { submitNamed(name, false, runnable); } /** * Submit a named task for later execution. If singletonTask is set to true, checked that tasks with the same name is not concurrently running. * @param name * @param runnable * @param singletonTask */ public void submitNamed(final String name, final boolean singletonTask, final Runnable runnable) { submit(new Runnable() { @Override public void run() { String prevName = Thread.currentThread().getName(); boolean locked = false; try { if (singletonTask) { if (getLock(name).tryLock()) { locked = true; logger.debug("singleton task " + name + " started"); } else { locked = false; logger.debug("singleton task '" + name + "' already running, exiting"); return; } } Thread.currentThread().setName(prevName + '-' + name + '(' + getAndIncr(name) + ')'); logger.debug("task started"); runnable.run(); logger.debug("task ended"); } finally { if (locked) { getLock(name).unlock(); } Thread.currentThread().setName(prevName); } } }); } protected Lock getLock(String name) { synchronized(uniquenessLock) { Lock lk = uniquenessLock.get(name); if (lk == null) { lk = new ReentrantLock(); uniquenessLock.put(name, lk); } return lk; } } protected int getAndIncr(String name) { synchronized(counters) { Integer val = counters.get(name); int newVal = (val == null) ? 0 : val.intValue() + 1; counters.put(name, newVal); return newVal; } } public void shutdown() { executors.shutdown(); } /** * @return True if all tasks have completed following shutdown. * @see java.util.concurrent.ExecutorService#isTerminated() */ public boolean isTerminated() { return executors.isTerminated(); } /** * @param timeout * @param unit * @return true if this executor terminated and false if the timeout elapsed before termination. * @throws InterruptedException * @see java.util.concurrent.ExecutorService#awaitTermination(long, java.util.concurrent.TimeUnit) */ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return executors.awaitTermination(timeout, unit); } }