001    // Copyright (c) 2001 Hursh Jain (http://www.mollypages.org) 
002    // The Molly framework is freely distributable under the terms of an
003    // MIT-style license. For details, see the molly pages web site at:
004    // http://www.mollypages.org/. Use, modify, have fun !
005    
006    package fc.util;
007    
008    /**
009    Allows measuring the start and stop time and (hence the elapsed time) of
010    some event. ("event" meaning something of interest, for example, a method
011    call). Can be started/stopped repeatedly. (cumulative time across all such
012    start/stops are available via the {@link #cumulativeTime} method).
013    <p>
014    All times are in <i>milliseconds</i>. 
015    <p>
016    Thread Safety: This class is <b>not</b> threadsafe and it's method
017    do <b>not</b> acquire any locks to reduce any time skew due to
018    lock acquisition. Multiple threads should use separate Watch
019    objects or alternatively, higher level synchronization.
020    <p>
021    Note: This class <i>used</i> to be called "Timer" but changed to Watch to 
022    avoid an annoying name conflict with <code>java.util.Timer</code>
023    
024    @author   hursh jain
025    @version  1.0, 10/19/2001
026    @see    NanoWatch A higher nano-second precision watch.
027    */
028    public class Watch
029    {
030          String    myname;
031    volatile  boolean   running = false;
032          long    startTime = -1;
033          long    stopTime;
034          long    cumulativeTime;
035    
036    public Watch(String name) {
037      this.myname = name;
038      }
039    
040    public Watch() {
041      this("DefaultWatch/" + Thread.currentThread().getName());
042      }
043    
044    /** 
045    Start measuring time. Call this method just before calling the
046    method/code to be instrumented. This method does <b>not</b> reset the
047    Watch, so the reset method should be called before calling this method
048    <b>again</b>. Returns <tt>this</tt> so we can say:
049    <blockquote>
050    <tt>Watch w = new Watch().start()</tt>
051    </blockquote>
052    */
053    public Watch start() {
054      startTime = System.currentTimeMillis(); 
055      running = true;
056      return this;
057      }
058      
059    /** Stop measuring the time */
060    public void stop() {
061      stopTime = System.currentTimeMillis();
062      cumulativeTime += (stopTime - startTime);
063      running = false;
064      }
065    
066    /** 
067    Returns the time elapsed since the Watch was started. If the watch was
068    started and stopped, returns the time (in milliseconds) between the 
069    start/stop interval.
070    
071    @throws RuntimeException  if the watch was never started before calling
072                  this method.
073    */
074    public long time() 
075      {
076      if (startTime == -1)
077        throw new RuntimeException("You need to start the watch at least once before calling this method");
078        
079      if (running)
080        return System.currentTimeMillis() - startTime;
081      
082      return  stopTime - startTime;
083      }
084    
085    /** 
086    This method is an alias for {@link #time} method.
087    */
088    public long getTime() 
089      {
090      return time();
091      }
092    
093    /** 
094    Useful for showing the elapsed time in seconds. Intervals between,
095    (<code>0 - 499</code> milliseconds are rounded down and
096    <code>500 - 999</code> milliseconds are rounded up).
097    */
098    public long timeInSeconds() 
099      {
100      final long time = getTime();
101      
102      if (time < 500)  return 0;
103      if (time <= 1000) return 1;
104      
105      long quotient  = time / 1000;
106      long remainder = time % 1000;
107      return quotient + ((remainder < 500) ? 0 : 1);
108      }
109    
110    /** 
111    This method is an alias for {@link #timeInSeconds} method.
112    */
113    public long getTimeInSeconds() 
114      {
115      return timeInSeconds();
116      }
117    
118    /**
119    Useful in NanoWatch and other subclasses. Not really useful here, returns
120    the same value as {@link #getTime}/{@link #time} functions instead. 
121    <p>
122    This method is defined here so all watches have a common interface and
123    any instance can be bound to a variable of type <code>Watch</code>.
124    */
125    public double getTimeInMillis()
126      {
127      return getTime() * 1.0D;
128      }
129    
130    /** 
131    Returns the total time recorded by this Watch (across several starts/stops)
132    */
133    public long cumulativeTime() 
134      {
135      if (! running) 
136        return cumulativeTime;
137      
138      return cumulativeTime + (System.currentTimeMillis() - startTime);
139      }
140    
141    /** 
142    Reset all values to zero. This method should be called before this object is
143    used <b>again</b>. Does not restart the timer, call start again when start the
144    timer.
145    */
146    public void reset() 
147      {
148      startTime = stopTime =  0;
149      }
150    
151    
152    /** 
153    Reset all values to zero and restarts the timer.
154    */
155    public void restart() 
156      {
157      reset();
158      start();
159      }
160    
161    /** Is the Watch currently running ? */
162    public boolean isRunning() {
163      return running;
164      }
165    
166    /** Get the start time (in milliseconds since Jan 1, 1970)*/
167    protected long getStart() {
168      return startTime;
169      }
170    
171    /** Get the stop time, (in milliseconds since Jan 1, 1970) */
172    protected long getStop() {
173      return stopTime;
174      }
175    
176    /** 
177    Describes the current state of this watch. The exact details of said
178    description are unspecified and subject to change.
179    */  
180    public String toString() 
181      {
182      String str = myname;
183    
184      str += ": Cum.Time=[" + cumulativeTime() + " ms]" + 
185          "; Start=[" + startTime + "]";
186      
187      if (! running) 
188        str +=  "; Stop=[" + stopTime + "]";
189      else 
190        str += "; Elapsed=[" + time() + " ms]";
191        
192      return str;
193      }
194    
195    public static void main(String[] args)
196      {
197      fc.util.Watch t1 = new fc.util.Watch("Watch 1");
198      t1.start();
199      
200      new Thread(new Runnable() { 
201        public void run() {
202          try { 
203            Watch t2 = new Watch();
204            t2.start(); Thread.currentThread().sleep(20); t2.stop(); 
205            System.out.println("t2.toString():" + t2);
206            } 
207          catch (Exception e) { e.printStackTrace(); }
208          } 
209        }).start();
210      
211      //following should return -1
212      System.out.println("Watch 1, total time taken:" + t1.time());
213    
214      System.out.println("Watch 1, time=" + t1.time());
215      System.out.println("Watch 1, before-being-stopped, toString():" + t1);
216      t1.stop();
217      System.out.println("Watch 1, is running ? " + t1.isRunning() );
218      System.out.println("Watch 1, after-being-stopped, toString():" + t1);
219      System.out.println("Watch 1, elapsed time:" + t1.time());
220      System.out.println("Watch 1, cumulative time taken:" + t1.cumulativeTime());
221      System.out.println("Watch 1, elapsed time:" + t1.time());
222    
223      new Thread(new Runnable() { 
224        public void run() {
225          try { 
226            Watch t2 = new Watch();
227            t2.start(); 
228            Thread.currentThread().sleep(250); 
229            t2.stop(); 
230            System.out.println("After sleeping 250ms: time in seconds:  " + t2.getTimeInSeconds());
231            t2.start();
232            Thread.currentThread().sleep(500); 
233            System.out.println("After sleeping 750ms: time in seconds:  " + t2.getTimeInSeconds());
234            Thread.currentThread().sleep(500); 
235            System.out.println("After sleeping 1250ms: time in seconds: " + t2.getTimeInSeconds());
236            Thread.currentThread().sleep(500); 
237            System.out.println("After sleeping 1750ms: time in seconds: " + t2.getTimeInSeconds());
238            Thread.currentThread().sleep(1000); 
239            System.out.println("After sleeping 2750ms: time in seconds: " + t2.getTimeInSeconds());
240            } 
241          catch (Exception e) { e.printStackTrace(); }
242          } 
243        }).start();
244      }
245    
246    } //~class Watch