/* * Copyright (c) 2008-2012 Eike Stepper (Berlin, Germany) and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Eike Stepper - initial API and implementation */ package org.eclipse.net4j.util.concurrent; import org.eclipse.net4j.internal.util.bundle.OM; import org.eclipse.net4j.util.WrappedException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * @author Eike Stepper * @since 2.0 */ public abstract class MonitoredThread extends Thread { private MonitoredThread.ThreadMonitor monitor; private long timeStamp; private boolean shutdown; public MonitoredThread(String name, MonitoredThread.ThreadMonitor monitor) { super(name); this.monitor = monitor; } public long getTimeStamp() { return timeStamp; } public boolean isIdleTimeoutExpired(long idleTimeOut) { if (timeStamp != 0L) // Skip in first loop { long idle = System.currentTimeMillis() - timeStamp; return idle > idleTimeOut; } return false; } public void heartBeat() { if (shutdown) { throw new ShutdownException(); } timeStamp = System.currentTimeMillis(); } public void shutdown() { shutdown = true; } @Override public void run() { monitor.handleStarting(this); try { doRun(); } catch (MonitoredThread.ShutdownException ex) { return; } catch (Exception ex) { OM.LOG.error(ex); throw WrappedException.wrap(ex); } finally { monitor.handleFinished(this); } } protected abstract void doRun() throws Exception; /** * @author Eike Stepper */ private static final class ShutdownException extends RuntimeException { private static final long serialVersionUID = 1L; } /** * @author Eike Stepper */ public static interface ThreadMonitor { public void handleStarting(MonitoredThread thread); public void handleFinished(MonitoredThread thread); } /** * @author Eike Stepper */ public static class MultiThreadMonitor implements MonitoredThread.ThreadMonitor, Runnable { public static final long SYNCED_START = -1; private long idleTimeOut; private long startOffset; private CountDownLatch startLatch; private List<MonitoredThread> threads = new ArrayList<MonitoredThread>(); /** * @param idleTimeOut * The number of milli seconds one of the threads may be idle (i.e. not having called * {@link MonitoredThread#heartBeat()}) before {@link #handleTimeoutExpiration(MonitoredThread)} is called. * @param startOffset * The number of milli seconds to sleep between threads are started. Zero means not to sleep and * {@link #SYNCED_START} means that all threads start at the same time by waiting on a shared latch. */ public MultiThreadMonitor(long idleTimeOut, long startOffset) { this.idleTimeOut = idleTimeOut; this.startOffset = startOffset; if (startOffset == SYNCED_START) { startLatch = new CountDownLatch(1); } } /** * Same as calling <tt>MonitoredThread(idleTimeOut, SYNCED_START)</tt>. */ public MultiThreadMonitor(long timeOut) { this(timeOut, SYNCED_START); } public long getIdleTimeOut() { return idleTimeOut; } public void addThread(MonitoredThread thread) { synchronized (threads) { threads.add(thread); } } public void handleStarting(MonitoredThread thread) { if (startLatch != null) { try { startLatch.await(); } catch (InterruptedException ex) { throw WrappedException.wrap(ex); } } else if (startOffset > 0L) { ConcurrencyUtil.sleep(startOffset); } } public void handleFinished(MonitoredThread thread) { synchronized (threads) { threads.remove(thread); } } public void run() { startupThreads(); for (;;) { List<MonitoredThread> idleThreads = new ArrayList<MonitoredThread>(); synchronized (threads) { if (threads.isEmpty()) { break; } for (MonitoredThread thread : threads) { if (thread.isIdleTimeoutExpired(idleTimeOut)) { idleThreads.add(thread); } } } for (MonitoredThread thread : idleThreads) { handleTimeoutExpiration(thread); } } ConcurrencyUtil.sleep(10); } protected void handleTimeoutExpiration(MonitoredThread thread) { synchronized (threads) { threads.remove(thread); } shutdownThreads(); throw new RuntimeException("Idle timeout expired: " + thread.getName()); //$NON-NLS-1$ } private void startupThreads() { for (MonitoredThread thread : threads) { thread.start(); } if (startLatch != null) { startLatch.countDown(); } } private void shutdownThreads() { synchronized (threads) { for (MonitoredThread t : threads) { t.shutdown(); } } } } }