/* * ThreadUtilities.java - Utilities for threading * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2010 Matthieu Casanova * Portions Copyright (C) 2010 Marcelo Vanzin * * 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; either version 2 * of the License, or any later version. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.util; //{{{ Imports import java.awt.EventQueue; import java.lang.reflect.InvocationTargetException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; //}}} /** * The threadpool of jEdit. * It uses a ExecutorService from the java.util.concurrent package. * You can run {@link Task} or {@link Runnable} in it, Runnables will be * encapsulated in Task and displayed in the Task Monitor. * * @author Matthieu Casanova * @author Marcelo Vanzin * @since jEdit 4.4pre1 */ public class ThreadUtilities { //{{{ runInDispatchThread() method /** * Run the runnable in EventDispatch Thread. * If the current thread is EventDispatch, it will run * immediately otherwise it will be queued in the DispatchThread * The difference with VFSManager.runInAWTThread() method is that * this one will not wait for IO Request before being executed * * @param runnable the runnable to run - it should return something meaningful from * toString() so that we can display it in the Task Monitor. */ public static void runInDispatchThread(Runnable runnable) { if (EventQueue.isDispatchThread()) runnable.run(); else EventQueue.invokeLater(runnable); } //}}} //{{{ runInDispatchThreadAndWait() method /** Runs the runnable in EDT through <code>invokeLater</code>, * but returns only after the runnable is executed. * This method is uninterruptible. * <p>Note the difference from <code>invokeAndWait</code>. * If current thread is not EDT and there are runnables * queued in EDT: * <ul><li>this method runs the runnable after them</li> * <li><code>invokeAndWait</code> runs the runnable before them * </li></ul> * @param runnable the runnable to run - it should return something meaningful from * toString() so that we can display it in the Task Monitor. */ public static void runInDispatchThreadAndWait(Runnable runnable) { boolean interrupted = false; CountDownLatchRunnable run = new CountDownLatchRunnable(runnable); runInDispatchThread(run); while (run.done.getCount() > 0) { try { run.done.await(); } catch (InterruptedException e) { interrupted = true; } } if (interrupted) Thread.currentThread().interrupt(); } //}}} //{{{ runInDispatchThreadNow() method /** * Runs the runnable in EDT through <code>invokeAndWait</code>. * Even if the thread gets interrupted, the call does not return * until the runnable finishes (uninterruptible method). * <p> * This method uses <code>EventQueue.invokeAndWait</code>, so * the following remark applies: * <p>If you use invokeAndWait(), make sure that the thread that calls * invokeAndWait() does not hold any locks that other threads might * need while the call is occurring. * From the article: * <a href="http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html#event_dispatching"> * Threads and Swing</a> * @param runnable the runnable to run - it should return something meaningful from * toString() so that we can display it in the Task Monitor. */ public static void runInDispatchThreadNow(Runnable runnable) { boolean interrupted = false; CountDownLatchRunnable run = new CountDownLatchRunnable(runnable); try { EventQueue.invokeAndWait(run); } catch (InterruptedException e) { interrupted = true; } catch (InvocationTargetException ite) { Throwable cause = ite.getCause(); if (cause instanceof RuntimeException) throw (RuntimeException)cause; else { Log.log(Log.ERROR, ThreadUtilities.class, "Invocation Target Exception:"); Log.log(Log.ERROR, runnable.getClass(), cause); } } while (run.done.getCount() > 0) { try { run.done.await(); } catch (InterruptedException e) { interrupted = true; } } if (interrupted) Thread.currentThread().interrupt(); } //}}} //{{{ runInBackground() method /** * Run the runnable in the threadpool. * The runnable will be encapsulated in a {@link Task} * @see #runInBackground(Task) * * @param runnable the runnable to run - it should return something meaningful from toString() so that we can display it in the Task Monitor. */ public static void runInBackground(Runnable runnable) { Task task; if (runnable instanceof Task) { task = (Task) runnable; } else { task = TaskManager.decorate(runnable); } TaskManager.instance.fireWaiting(task); threadPool.execute(task); } /** * Run the task in the threadpool. * The runnable will be encapsulated in a {@link Task} * * @param task the task to run */ public static void runInBackground(Task task) { TaskManager.instance.fireWaiting(task); threadPool.execute(task); } //}}} private ThreadUtilities() { } //{{{ JEditThreadFactory class private static class JEditThreadFactory implements ThreadFactory { private JEditThreadFactory() { threadIDs = new AtomicInteger(0); threadGroup = new ThreadGroup("jEdit Workers"); } @Override public Thread newThread(Runnable r) { Thread t = new Thread(threadGroup, r); t.setName("jEdit Worker #" + threadIDs.getAndIncrement()); return t; } private final AtomicInteger threadIDs; private final ThreadGroup threadGroup; } //}}} private static final ExecutorService threadPool; private static final int CORE_POOL_SIZE = 4; static { threadPool = Executors.newCachedThreadPool(new JEditThreadFactory()); ((ThreadPoolExecutor) threadPool).setCorePoolSize(CORE_POOL_SIZE); } //{{{ MyRunnable class private static class CountDownLatchRunnable implements Runnable { private final Runnable runnable; private CountDownLatch done = new CountDownLatch(1); private CountDownLatchRunnable(Runnable runnable) { this.runnable = runnable; } @Override public void run() { runnable.run(); done.countDown(); } } //}}} }