001 /* 002 Copyright (c) 2008 Evan Jones, Yang Zhang 003 004 Permission is hereby granted, free of charge, to any person obtaining a copy 005 of this software and associated documentation files (the "Software"), to deal 006 in the Software without restriction, including without limitation the rights 007 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 008 copies of the Software, and to permit persons to whom the Software is 009 furnished to do so, subject to the following conditions: 010 011 The above copyright notice and this permission notice shall be included in 012 all copies or substantial portions of the Software. 013 014 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 015 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 016 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 017 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 018 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 019 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 020 THE SOFTWARE. 021 */ 022 023 package util; 024 025 import java.util.Random; 026 import java.util.concurrent.atomic.AtomicBoolean; 027 import java.util.concurrent.locks.Condition; 028 import java.util.concurrent.locks.ReentrantLock; 029 030 /** 031 * Abstract class for simple sampling daemons. 032 * 033 * 034 */ 035 public abstract class Sampler implements Runnable { 036 private final int intervalMillis; 037 private final AtomicBoolean doStop; 038 private boolean isDone; 039 private final ReentrantLock lock; 040 private final Condition done; 041 private final Random random; 042 043 /** 044 * Creates a sampler that executes periodically. The sampler uses randomization 045 * so the interval is probabilistic with uniform distribution. 046 * 047 * @param intervalMillis the expected interval period in milliseconds. If 048 * <code>intervalMillis</code> is zero, the sampler 049 * will run with an infinite interval (i.e., no-op). 050 */ 051 protected Sampler(int intervalMillis) { 052 this.intervalMillis = intervalMillis; 053 054 doStop = new AtomicBoolean(false); 055 lock = new ReentrantLock(); 056 done = lock.newCondition(); 057 random = new Random(); 058 } 059 060 /** 061 * Takes a sample at the interval. 062 */ 063 protected abstract void sample(); 064 065 /** 066 * Returns the statistics associated with this sampler. 067 * 068 * @return statistics 069 */ 070 protected abstract Statistics dumpSamples(); 071 072 @Override 073 public void run() { 074 while (!doStop.get()) { 075 try { 076 int sleepTime = intervalMillis - (intervalMillis / 2) + random.nextInt(intervalMillis); 077 Thread.sleep(sleepTime); 078 } catch (InterruptedException e) { 079 throw new RuntimeException(e); 080 } 081 sample(); 082 } 083 084 lock.lock(); 085 try { 086 isDone = true; 087 done.signal(); 088 } finally { 089 lock.unlock(); 090 } 091 } 092 093 /** 094 * Stops the sampler and returns the accumulated statistics. 095 * 096 * @return the accumulated statistics or null if the interval is infinite 097 */ 098 public Statistics stop() { 099 if (intervalMillis > 0) { 100 doStop.set(true); 101 lock.lock(); 102 try { 103 while (!isDone) { 104 done.await(); 105 } 106 return dumpSamples(); 107 } catch (InterruptedException e) { 108 throw new Error(e); 109 } finally { 110 lock.unlock(); 111 } 112 } 113 114 return null; 115 } 116 117 /** 118 * Starts sampling thread. Subclasses should use this method to start 119 * sampling. 120 * 121 * @return a sampler 122 */ 123 protected final static Sampler start(Sampler sampler) { 124 if (sampler.intervalMillis > 0) { 125 Thread t = new Thread(sampler, "Sampler"); 126 t.setDaemon(true); 127 t.start(); 128 } 129 return sampler; 130 } 131 }