/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.visualvm.core.scheduler;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Scheduling pipe maintains a list of {@linkplain ScheduledTask} instances
* for a certain scheduling interval
* <p>It allows addition and removal of the tasks and takes care of creating
* and releasing appropriate scheduled executors</p>
*
* @author Jaroslav Bachorik <jaroslav.bachorik@sun.com>
*/
final class SchedulingPipe {
private static final Logger LOGGER = Logger.getLogger(SchedulingPipe.class.getName());
final private Object pipeLock = new Object();
final private ReadWriteLock tasksLock = new ReentrantReadWriteLock();
// @GuardedBy pipeLock
private ScheduledFuture pipeFuture = null;
final private static ScheduledExecutorService schedulerService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
final private static ExecutorService dispatcher = Executors.newCachedThreadPool();
// @GuardedBy tasksLock
final private Set<WeakReference<DefaultScheduledTask>> tasks = new HashSet<WeakReference<DefaultScheduledTask>>();
private Quantum interval;
SchedulingPipe(Quantum interval) {
this.interval = interval;
}
void addTask(DefaultScheduledTask task) {
try {
tasksLock.writeLock().lock();
if (tasks.isEmpty()) {
startPipe();
}
tasks.add(new WeakReference<DefaultScheduledTask>(task));
} finally {
tasksLock.writeLock().unlock();
}
}
private void startPipe() {
synchronized (pipeLock) {
pipeFuture = schedulerService.scheduleAtFixedRate(new Runnable() {
public void run() {
try {
tasksLock.writeLock().lock();
final long timeStamp = System.currentTimeMillis();
for (Iterator<WeakReference<DefaultScheduledTask>> iter = tasks.iterator(); iter.hasNext();) {
WeakReference<DefaultScheduledTask> ref = iter.next();
final DefaultScheduledTask t = ref.get();
if (t != null) {
dispatcher.submit(new Runnable() {
public void run() {
try {
t.onSchedule(timeStamp);
} catch (Throwable e) {
LOGGER.log(Level.SEVERE, null, e);
}
}
});
} else {
iter.remove();
}
}
if (tasks.isEmpty()) {
synchronized (pipeLock) {
pipeFuture.cancel(false);
pipeFuture = null;
}
}
} finally {
tasksLock.writeLock().unlock();
}
}
}, interval.interval, interval.interval, interval.unit);
}
}
void removeTask(DefaultScheduledTask task) {
try {
tasksLock.writeLock().lock();
for(Iterator<WeakReference<DefaultScheduledTask>> iter = tasks.iterator();iter.hasNext();) {
WeakReference<DefaultScheduledTask> ref = iter.next();
DefaultScheduledTask t = ref.get();
if (t == null || t.equals(task)) {
iter.remove();
}
}
} finally {
tasksLock.writeLock().unlock();
}
if (tasks.isEmpty()) {
synchronized(pipeLock) {
pipeFuture.cancel(false);
pipeFuture = null;
}
}
}
}