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
143    before this object is used <b>again</b>.
144    */
145    public void reset() 
146      {
147      startTime = stopTime =  0;
148      }
149    
150    /** Is the Watch currently running ? */
151    public boolean isRunning() {
152      return running;
153      }
154    
155    /** Get the start time (in milliseconds since Jan 1, 1970)*/
156    protected long getStart() {
157      return startTime;
158      }
159    
160    /** Get the stop time, (in milliseconds since Jan 1, 1970) */
161    protected long getStop() {
162      return stopTime;
163      }
164    
165    /** 
166    Describes the current state of this watch. The exact details of said
167    description are unspecified and subject to change.
168    */  
169    public String toString() 
170      {
171      String str = myname;
172    
173      str += ": Cum.Time=[" + cumulativeTime() + " ms]" + 
174          "; Start=[" + startTime + "]";
175      
176      if (! running) 
177        str +=  "; Stop=[" + stopTime + "]";
178      else 
179        str += "; Elapsed=[" + time() + " ms]";
180        
181      return str;
182      }
183    
184    public static void main(String[] args)
185      {
186      fc.util.Watch t1 = new fc.util.Watch("Watch 1");
187      t1.start();
188      
189      new Thread(new Runnable() { 
190        public void run() {
191          try { 
192            Watch t2 = new Watch();
193            t2.start(); Thread.currentThread().sleep(20); t2.stop(); 
194            System.out.println("t2.toString():" + t2);
195            } 
196          catch (Exception e) { e.printStackTrace(); }
197          } 
198        }).start();
199      
200      //following should return -1
201      System.out.println("Watch 1, total time taken:" + t1.time());
202    
203      System.out.println("Watch 1, time=" + t1.time());
204      System.out.println("Watch 1, before-being-stopped, toString():" + t1);
205      t1.stop();
206      System.out.println("Watch 1, is running ? " + t1.isRunning() );
207      System.out.println("Watch 1, after-being-stopped, toString():" + t1);
208      System.out.println("Watch 1, elapsed time:" + t1.time());
209      System.out.println("Watch 1, cumulative time taken:" + t1.cumulativeTime());
210      System.out.println("Watch 1, elapsed time:" + t1.time());
211    
212      new Thread(new Runnable() { 
213        public void run() {
214          try { 
215            Watch t2 = new Watch();
216            t2.start(); 
217            Thread.currentThread().sleep(250); 
218            t2.stop(); 
219            System.out.println("After sleeping 250ms: time in seconds:  " + t2.getTimeInSeconds());
220            t2.start();
221            Thread.currentThread().sleep(500); 
222            System.out.println("After sleeping 750ms: time in seconds:  " + t2.getTimeInSeconds());
223            Thread.currentThread().sleep(500); 
224            System.out.println("After sleeping 1250ms: time in seconds: " + t2.getTimeInSeconds());
225            Thread.currentThread().sleep(500); 
226            System.out.println("After sleeping 1750ms: time in seconds: " + t2.getTimeInSeconds());
227            Thread.currentThread().sleep(1000); 
228            System.out.println("After sleeping 2750ms: time in seconds: " + t2.getTimeInSeconds());
229            } 
230          catch (Exception e) { e.printStackTrace(); }
231          } 
232        }).start();
233      }
234    
235    } //~class Watch