1 /* 2 ~ Copyright 2006-2007 Nicolas De Loof. 3 ~ 4 ~ Licensed under the Apache License, Version 2.0 (the "License"); 5 ~ you may not use this file except in compliance with the License. 6 ~ You may obtain a copy of the License at 7 ~ 8 ~ http://www.apache.org/licenses/LICENSE-2.0 9 ~ 10 ~ Unless required by applicable law or agreed to in writing, software 11 ~ distributed under the License is distributed on an "AS IS" BASIS, 12 ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 ~ See the License for the specific language governing permissions and 14 ~ limitations under the License. 15 */ 16 package org.jmonit; 17 18 import static org.jmonit.events.ExecutionEvent.CANCELED; 19 import static org.jmonit.events.ExecutionEvent.STARTED; 20 import static org.jmonit.events.ExecutionEvent.STOPPED; 21 22 import org.jmonit.events.DataMonitoredEvent; 23 import org.jmonit.events.ExecutionEvent; 24 import org.jmonit.events.MonitoringEvent; 25 import org.jmonit.log.Log; 26 27 /** 28 * a <b>Stopwatch</b> is used to evaluate the time consumed by a thread to 29 * execute some process. There is two way to use a Stopwatch : 30 * <ul> 31 * <li>By using the {@link #start(Monitor)} method to create a new instance. 32 * The returned object is not thread-safe and must be created on every thread 33 * that execute the code to be monitored.</li> 34 * <li> </li> 35 * By getting a Stopwatch usin the monitor features API using 36 * <code>monitor.getFeature(Stopwatch.class)</code>. In such case, the 37 * Stopwatch object can be reused, and can also be obtained from another block 38 * of code. It internally uses a threadLocale to associate the running Stopwatch 39 * with the current active thread. 40 * </ul> 41 * <p> 42 * In both case, the monitored application MUST allways {@link #stop} or 43 * {@link #cancel} even when an error occurs. For this reason, a 44 * <code>try/catch</code> is highly recommended. 45 * <p> 46 * If not followed, this requirement will introduce zombie threads in 47 * {@link org.jmonit.features.Concurrency} and invalid performance statistics. 48 * You will also get warning in logs for zombie Stopwatches. 49 * 50 * @author <a href="mailto:nicolas.deloof@gmail.com">Nicolas De Loof</a> 51 */ 52 public class Stopwatch 53 extends Probe 54 { 55 /** Logger */ 56 private static Log log = Log.getLog( Stopwatch.class ); 57 58 /** 59 * Start a new Stopwatch to evaluate time consumed to execute some 60 * processing. The created object is not thread-safe and must be garbaged 61 * after use. 62 * 63 * @param monitor the monitor used to gather the computed execution time 64 * @return a new started Stopwatch 65 */ 66 public static Stopwatch start( Monitor monitor ) 67 { 68 Stopwatch stopwatch = new Stopwatch( monitor ); 69 stopwatch.start(); 70 return stopwatch; 71 } 72 73 /** Time the probe was started */ 74 private long startedAt; 75 76 /** Time the probe was stopped */ 77 private long stopedAt; 78 79 /** Time the probe was paused */ 80 private long pauseDelay; 81 82 /** flag for running probe */ 83 private boolean started; 84 85 /** flag for stopped probe */ 86 private boolean stoped; 87 88 /** flag for paused probe */ 89 private boolean paused; 90 91 /** Collector that is notified of probe result */ 92 private Monitor monitor; 93 94 /** 95 * Constructor 96 * 97 * @param monitor monitor that gets the elapsed time to be monitored 98 */ 99 public Stopwatch( Monitor monitor ) 100 { 101 super(); 102 monitor.tag( "perf" ); 103 this.monitor = monitor; 104 } 105 106 /** 107 * Default constructor. Can be used to evalutate execution time without 108 * monitoring. 109 */ 110 public Stopwatch() 111 { 112 super(); 113 } 114 115 /** 116 * Cancel monitoring. Elapsed time will not be computed and will not be 117 * published to the monitor. 118 * <p> 119 * In some circumstances you want to monitor time elapsed from early stage 120 * of computation, and discover latter if the computed data is relevant. For 121 * example, monitoring a messaging system, but beeing interested only by 122 * some types of messages. In such case, a Stopwatch can be started early 123 * and canceled when the application is able to determine it's relevancy. 124 * <p> 125 * In any way, the probe will still report thread concurrency even if 126 * canceled. 127 */ 128 public void cancel() 129 { 130 if ( !stoped && started ) 131 { 132 stoped = true; 133 fireMonitoringEvent( new ExecutionEvent( this, CANCELED ) ); 134 } 135 } 136 137 /** 138 * @return Elapsed time (in nanoseconds) for the monitored process 139 */ 140 public long getElapsedTime() 141 { 142 long delay; 143 if ( stoped || paused ) 144 { 145 delay = stopedAt - startedAt - pauseDelay; 146 } 147 else 148 { 149 // Still running ! 150 delay = nanotime() - startedAt - pauseDelay; 151 } 152 return delay; 153 } 154 155 /** 156 * Temporary stop the Stopwatch. Elapsed time calculation will not include 157 * time spent in paused mode. 158 */ 159 public void pause() 160 { 161 if ( started && !paused && !stoped ) 162 { 163 stopedAt = nanotime(); 164 paused = true; 165 } 166 } 167 168 /** 169 * Resume the Stopwatch after a pause. 170 */ 171 public void resume() 172 { 173 if ( paused && !stoped ) 174 { 175 pauseDelay = nanotime() - stopedAt; 176 paused = false; 177 stopedAt = 0; 178 } 179 } 180 181 /** 182 * Start monitoring the process. 183 */ 184 public void start() 185 { 186 if ( !started ) 187 { 188 startedAt = nanotime(); 189 fireMonitoringEvent( new ExecutionEvent( this, STARTED ) ); 190 started = true; 191 } 192 } 193 194 protected void fireMonitoringEvent( MonitoringEvent event ) 195 { 196 if ( monitor != null ) 197 { 198 monitor.fireMonitoringEvent( event ); 199 } 200 } 201 202 /** 203 * Convenience method to stop or cancel a Stopwatch depending on success of 204 * monitored operation 205 * 206 * @param canceled 207 * @return time elapsed since the probe has been started 208 */ 209 public long stop( boolean canceled ) 210 { 211 if ( canceled ) 212 { 213 cancel(); 214 return getElapsedTime(); 215 } 216 return stop(); 217 } 218 219 /** 220 * Stop monitoring the process. A Stopwatch created with 221 * {@link #start(Monitor)} cannot be re-used after stopped has been called. 222 */ 223 public long stop() 224 { 225 if ( started && !stoped ) 226 { 227 long t = nanotime(); 228 if ( paused ) 229 { 230 pauseDelay = t - stopedAt; 231 } 232 stopedAt = t; 233 stoped = true; 234 fireMonitoringEvent( new ExecutionEvent( this, STOPPED ) ); 235 fireMonitoringEvent( new DataMonitoredEvent( getElapsedTime() ) ); 236 } 237 return stopedAt; 238 } 239 240 /** 241 * {@inheritDoc} 242 * <p> 243 * Monitored application should use a <code>try/finally</code> block to 244 * ensure on of {@link #stop()} or {@link #cancel()} method is invoked, even 245 * when an exception occurs. To avoid Stopwatches to keep running if the 246 * application didn't follow this recommandation, the finalizer is used to 247 * cancel the Stopwatch and will log a educational warning. 248 * 249 * @see java.lang.Object#finalize() 250 */ 251 protected void finalize() 252 { 253 // This probe is reclaimed by garbage-collector and still running, 254 // the monitored code "forgot" to stop/cancel it properly. 255 if ( started && !stoped ) 256 { 257 log.warn( "Execution for " + monitor.getName() + " was not stoped properly. " 258 + "This can result in wrong concurrency monitoring. " 259 + "Use try/finally blocks to avoid this warning" ); 260 cancel(); 261 } 262 } 263 264 /** 265 * Returns the current value of the most precise available system timer, in 266 * nanoseconds. The real precision depends on the JVM and the underlying 267 * system. On JRE before java5, backport-util-concurrent provides some 268 * limited support for equivalent timer. 269 * 270 * @see System#nanoTime() 271 * @return time in nanosecond 272 */ 273 protected long nanotime() 274 { 275 return System.nanoTime(); 276 } 277 278 /** 279 * @return <code>true</code> if the Stopwatch has been started 280 */ 281 public boolean isStarted() 282 { 283 return started; 284 } 285 286 /** 287 * @return <code>true</code> if the Stopwatch has been stopped 288 */ 289 public boolean isStoped() 290 { 291 return stoped; 292 } 293 294 /** 295 * @return <code>true</code> if the Stopwatch has been paused 296 */ 297 public boolean isPaused() 298 { 299 return paused; 300 } 301 302 }