/**
* Helios, OpenSource Monitoring
* Brought to you by the Helios Development Group
*
* Copyright 2007, Helios Development Group and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
*/
package org.helios.apmrouter.ref;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.management.ManagementFactory;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.helios.apmrouter.jmx.JMXHelper;
/**
* <p>Title: RunnableReferenceQueue</p>
* <p>Description: Singleton to manage the generic weak reference enqueue callback service.</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.ref.RunnableReferenceQueue</code></p>
*/
public class RunnableReferenceQueue extends Thread implements RunnableReferenceQueueMBean, ThreadFactory, RejectedExecutionHandler, UncaughtExceptionHandler {
/** The singleton reference */
private static volatile RunnableReferenceQueue instance = null;
/** The singleton ctor lock */
private static final Object lock = new Object();
/** The reference queue that weak references are enqueued into */
private final ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
/** The thread pool work queue */
private final BlockingQueue<Runnable> blockinqQueue = new LinkedBlockingQueue<Runnable>();
/** A thread pool executor to run the actual callbacks */
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(1, CORES, 60, TimeUnit.SECONDS, blockinqQueue, this, this);
/** A counter of rejected executions */
private final AtomicLong rejectedExecutions = new AtomicLong(0);
/** A counter of uncaught exceptions */
private final AtomicLong failedExecutions = new AtomicLong(0);
/** A counter of cleared callbacks */
private final AtomicLong completedCallbacks = new AtomicLong(0);
/** A counter of submitted callbacks */
private final AtomicLong submittedCallbacks = new AtomicLong(0);
/** A serial number factory for worker threads */
private final AtomicInteger serial = new AtomicInteger(0);
/** The number of processors available to the JVM */
public static final int CORES = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
/**
* Acquires the RunnableReferenceQueue singleton instance
* @return the RunnableReferenceQueue singleton instance
*/
public static RunnableReferenceQueue getInstance() {
if(instance==null) {
synchronized(lock) {
if(instance==null) {
instance = new RunnableReferenceQueue();
}
}
}
return instance;
}
/**
* Creates a new RunnableReferenceQueue
*/
private RunnableReferenceQueue() {
this.setDaemon(true);
this.setName("RunnableReferenceQueueThread");
this.start();
JMXHelper.registerMBean(JMXHelper.objectName(getClass().getPackage().getName(), "service", getClass().getSimpleName()), this);
}
public static void main(String[] args) {
log("RunnableReferenceQueue Test");
final Map<Long, WeakReference<byte[]>> m = new ConcurrentHashMap<Long, WeakReference<byte[]>>();
Random r = new Random();
int loopCount = 10000;
while(true) {
for(int i = 0; i < loopCount; i++) {
byte[] bytes = new byte[1000];
final long key = r.nextLong();
r.nextBytes(bytes);
m.put(key, RunnableReferenceQueue.getInstance().buildWeakReference(bytes, new Runnable(){
@Override
public void run() {
m.remove(key);
}
}));
}
log("M size:" + m.size());
try { Thread.currentThread().join(1000); } catch (Exception x) {}
}
}
public static void log(Object msg) {
System.out.println(msg);
}
/**
* {@inheritDoc}
* @see java.lang.Thread#run()
*/
@Override
public void run() {
while(true) {
try {
Reference<?> ref = refQueue.remove();
if(ref instanceof RunnableWeakReference) {
executor.submit((RunnableWeakReference<?>)ref);
submittedCallbacks.incrementAndGet();
}
} catch (Throwable t) {}
}
}
/**
* Returns the cleaner work queue depth
* @return the cleaner work queue depth
*/
@Override
public int getWorkQueueDepth() {
return blockinqQueue.size();
}
/**
* Returns the cleaner work queue remaining capacity
* @return the cleaner work queue remaining capacity
*/
@Override
public int getWorkQueueCapacity() {
return blockinqQueue.remainingCapacity();
}
/**
* Returns the worker completed task count
* @return the worker completed task count
*/
@Override
public long getCompletedTaskCount() {
return executor.getCompletedTaskCount();
}
/**
* Returns the number of active worker threads
* @return the number of active worker threads
*/
@Override
public int getActiveWorkers() {
return executor.getActiveCount();
}
/**
* Returns the current number of worker threads
* @return the current number of worker threads
*/
@Override
public int getWorkerCount() {
return executor.getPoolSize();
}
/**
* Returns the highwater number of worker threads
* @return the highwater number of worker threads
*/
@Override
public int getHighwaterWorkerCount() {
return executor.getLargestPoolSize();
}
/**
// * Returns the number of rejected executions
* @return the number of rejected executions
*/
@Override
public long getRejectedExecutions() {
return rejectedExecutions.get();
}
/**
* Returns the number of failed executions
* @return the number of failed executions
*/
@Override
public long getFailedExecutions() {
return failedExecutions.get();
}
/**
* Returns the number of completed callbacks
* @return the number of completed callbacks
*/
@Override
public long getCompletedCallbacks() {
return completedCallbacks.get();
}
/**
* Returns the number of submitted callbacks
* @return the number of submitted callbacks
*/
@Override
public long getSubmittedCallbacks() {
return submittedCallbacks.get();
}
/**
* {@inheritDoc}
* @see org.helios.apmrouter.ref.RunnableReferenceQueueMBean#getPendingCallbacks()
*/
@Override
public long getPendingCallbacks() {
return submittedCallbacks.get() - completedCallbacks.get();
}
/**
* {@inheritDoc}
* @see java.util.concurrent.RejectedExecutionHandler#rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)
*/
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.err.println("[RunnableReferenceQueue] Rejected execution for enqueue callback [" + r + "]");
rejectedExecutions.incrementAndGet();
}
/**
* {@inheritDoc}
* @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable)
*/
@Override
public void uncaughtException(Thread t, Throwable e) {
failedExecutions.incrementAndGet();
System.err.println("[RunnableReferenceQueue] Failed callback execution. Stack trace follows:");
if(e!=null) e.printStackTrace(System.err);
}
/**
* {@inheritDoc}
* @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
*/
@Override
public Thread newThread(final Runnable r) {
Thread t = new Thread(r, "RunnableReferenceQueueWorkerThread#" + serial.incrementAndGet());
t.setDaemon(true);
return t;
}
/**
* Creates a new WeakReference for the passed referent and runnable callback
* @param t The reference
* @param r The on enqueue runnable callback
* @return a weak reference
*/
public <T> WeakReference<T> buildWeakReference(T t, Runnable r) {
return new RunnableWeakReference<T>(t, r);
}
/**
* <p>Title: RunnableWeakReference</p>
* <p>Description: A {@link WeakReference} extension that holds an enqueue runnable</p>
* <p>Company: Helios Development Group LLC</p>
* @author Whitehead (nwhitehead AT heliosdev DOT org)
* <p><code>org.helios.apmrouter.ref.RunnableReferenceQueue.RunnableWeakReference</code></p>
*/
private class RunnableWeakReference<T> extends WeakReference<T> implements Runnable {
/** The callback runnable */
private final Runnable runnable;
/**
* Creates a new RunnableWeakReference
* @param referent The referent
* @param runnable The callback runnable
*/
public RunnableWeakReference(T referent, Runnable runnable) {
super(referent, refQueue);
this.runnable = runnable;
}
/**
* {@inheritDoc}
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try {
if(runnable!=null) {
runnable.run();
}
} finally {
completedCallbacks.incrementAndGet();
}
}
}
}