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 }