View Javadoc

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 }