package org.apache.blur.concurrent; /** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.blur.log.Log; import org.apache.blur.log.LogFactory; public class ThreadWatcher { private static final Log LOG = LogFactory.getLog(ThreadWatcher.class); private static ThreadWatcher _instance; static class Watch { public Watch(Thread thread) { _thread = thread; } Thread _thread; final long _start = System.currentTimeMillis(); String _task; float _complete; public void status(String task, float complete) { _task = task; _complete = complete; } public void resetStatus() { _complete = 0; _task = null; } } private final ConcurrentMap<Thread, Watch> _threads = new ConcurrentHashMap<Thread, Watch>(); private Timer _timer; private ThreadWatcher() { } public void watch(Thread thread) { _threads.put(thread, new Watch(thread)); } public void release(Thread thread) { _threads.remove(thread); } public ExecutorService watch(ExecutorService executorService) { return new ThreadWatcherExecutorService(executorService, this); } public void init() { _timer = new Timer("Thread-Watcher", true); _timer.schedule(new TimerTask() { @Override public void run() { try { processRunningThreads(); } catch (Throwable t) { LOG.error("Unknown error", t); } } }, TimeUnit.SECONDS.toMillis(5), TimeUnit.SECONDS.toMillis(5)); } private void processRunningThreads() { for (Entry<Thread, Watch> entry : _threads.entrySet()) { processWatch(entry.getValue()); } } private void processWatch(Watch watch) { if (hasBeenExecutingLongerThan(TimeUnit.SECONDS.toMillis(5), watch)) { long now = System.currentTimeMillis(); String task = watch._task; float complete = watch._complete; if (task == null) { LOG.info("Thread [{0}] has been executing for [{1} ms]", watch._thread, now - watch._start); } else { LOG.info("Thread [{0}] has been executing task [{1}] is [{2}] complete for [{4} ms]", watch._thread, task, complete, now - watch._start); } } } private boolean hasBeenExecutingLongerThan(long period, Watch watch) { if (watch._start + period < System.currentTimeMillis()) { return true; } return false; } public void close() { _timer.cancel(); _timer.purge(); _threads.clear(); } public static class ThreadWatcherExecutorService implements ExecutorService { private ExecutorService _executorService; private ThreadWatcher _threadWatcher; public ThreadWatcherExecutorService(ExecutorService executorService, ThreadWatcher threadWatcher) { _executorService = executorService; _threadWatcher = threadWatcher; } private Runnable wrap(final Runnable runnable) { return new Runnable() { @Override public void run() { Thread thread = Thread.currentThread(); _threadWatcher.watch(thread); try { runnable.run(); } finally { _threadWatcher.release(thread); } } }; } private <T> Collection<? extends Callable<T>> wrapCallableCollection(Collection<? extends Callable<T>> tasks) { List<Callable<T>> result = new ArrayList<Callable<T>>(tasks.size()); for (Callable<T> callable : tasks) { result.add(wrapCallable(callable)); } return result; } private <T> Callable<T> wrapCallable(final Callable<T> task) { return new Callable<T>() { @Override public T call() throws Exception { Thread thread = Thread.currentThread(); _threadWatcher.watch(thread); try { return task.call(); } finally { _threadWatcher.release(thread); } } }; } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return _executorService.awaitTermination(timeout, unit); } public void execute(Runnable command) { _executorService.execute(wrap(command)); } public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { return _executorService.invokeAll(wrapCallableCollection(tasks), timeout, unit); } public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { return _executorService.invokeAll(wrapCallableCollection(tasks)); } public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return _executorService.invokeAny(wrapCallableCollection(tasks), timeout, unit); } public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return _executorService.invokeAny(wrapCallableCollection(tasks)); } public boolean isShutdown() { return _executorService.isShutdown(); } public boolean isTerminated() { return _executorService.isTerminated(); } public void shutdown() { _executorService.shutdown(); } public List<Runnable> shutdownNow() { return _executorService.shutdownNow(); } public <T> Future<T> submit(Callable<T> task) { return _executorService.submit(wrapCallable(task)); } public <T> Future<T> submit(Runnable task, T result) { return _executorService.submit(wrap(task), result); } public Future<?> submit(Runnable task) { return _executorService.submit(wrap(task)); } } public synchronized static ThreadWatcher instance() { if (_instance == null) { _instance = new ThreadWatcher(); _instance.init(); } return _instance; } public static void status(String task, float complete) { if (_instance == null) { LOG.warn("Call to resetStatus on thread no being watched."); return; } Watch watch = _instance._threads.get(Thread.currentThread()); if (watch == null) { return; } watch.status(task, complete); } public static void resetStatus() { if (_instance == null) { LOG.warn("Call to resetStatus on thread no being watched."); return; } Watch watch = _instance._threads.get(Thread.currentThread()); if (watch == null) { return; } watch.resetStatus(); } }