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
006package fc.util;
007
008/**
009Allows measuring the start and stop time and (hence the elapsed time) of
010some event. ("event" meaning something of interest, for example, a method
011call). Can be started/stopped repeatedly. (cumulative time across all such
012start/stops are available via the {@link #cumulativeTime} method).
013<p>
014All times are in <i>milliseconds</i>. 
015<p>
016Thread Safety: This class is <b>not</b> threadsafe and it's method
017do <b>not</b> acquire any locks to reduce any time skew due to
018lock acquisition. Multiple threads should use separate Watch
019objects or alternatively, higher level synchronization.
020<p>
021Note: This class <i>used</i> to be called "Timer" but changed to Watch to 
022avoid 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*/
028public class Watch
029{
030      String    myname;
031volatile  boolean   running = false;
032      long    startTime = -1;
033      long    stopTime;
034      long    cumulativeTime;
035
036public Watch(String name) {
037  this.myname = name;
038  }
039
040public Watch() {
041  this("DefaultWatch/" + Thread.currentThread().getName());
042  }
043
044/** 
045Start measuring time. Call this method just before calling the
046method/code to be instrumented. This method does <b>not</b> reset the
047Watch, 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*/
053public Watch start() {
054  startTime = System.currentTimeMillis(); 
055  running = true;
056  return this;
057  }
058  
059/** Stop measuring the time */
060public void stop() {
061  stopTime = System.currentTimeMillis();
062  cumulativeTime += (stopTime - startTime);
063  running = false;
064  }
065
066/** 
067Returns the time elapsed since the Watch was started. If the watch was
068started and stopped, returns the time (in milliseconds) between the 
069start/stop interval.
070
071@throws RuntimeException  if the watch was never started before calling
072              this method.
073*/
074public 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/** 
086This method is an alias for {@link #time} method.
087*/
088public long getTime() 
089  {
090  return time();
091  }
092
093/** 
094Useful 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*/
098public 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/** 
111This method is an alias for {@link #timeInSeconds} method.
112*/
113public long getTimeInSeconds() 
114  {
115  return timeInSeconds();
116  }
117
118/** 
119This method is an alias for {@link #timeInSeconds} method.
120*/
121public long seconds() 
122  {
123  return timeInSeconds();
124  }
125
126/** 
127This method is an alias for {@link #timeInSeconds} method.
128*/
129public long elapsed() 
130  {
131  return timeInSeconds();
132  }
133
134
135/** 
136This method is an alias for {@link #timeInSeconds} method.
137*/
138public long timeSeconds() 
139  {
140  return timeInSeconds();
141  }
142
143
144/** 
145This method is an alias for {@link #getTimeInMillis} method.
146*/
147public double timeMillis() 
148  {
149  return getTimeInMillis();
150  }
151
152
153/**
154Useful in NanoWatch and other subclasses. Not really useful here, returns
155the same value as {@link #getTime}/{@link #time} functions instead. 
156<p>
157This method is defined here so all watches have a common interface and
158any instance can be bound to a variable of type <code>Watch</code>.
159*/
160public double getTimeInMillis()
161  {
162  return getTime() * 1.0D;
163  }
164
165/** 
166Returns the total time recorded by this Watch (across several starts/stops)
167*/
168public long cumulativeTime() 
169  {
170  if (! running) 
171    return cumulativeTime;
172  
173  return cumulativeTime + (System.currentTimeMillis() - startTime);
174  }
175
176/** 
177Reset all values to zero. This method should be called before this object is
178used <b>again</b>. Does not restart the timer, call start again when start the
179timer.
180*/
181public void reset() 
182  {
183  startTime = stopTime =  0;
184  }
185
186
187/** 
188Reset all values to zero and restarts the timer.
189*/
190public void restart() 
191  {
192  reset();
193  start();
194  }
195
196/** Is the Watch currently running ? */
197public boolean isRunning() {
198  return running;
199  }
200
201/** Get the start time (in milliseconds since Jan 1, 1970)*/
202protected long getStart() {
203  return startTime;
204  }
205
206/** Get the stop time, (in milliseconds since Jan 1, 1970) */
207protected long getStop() {
208  return stopTime;
209  }
210
211/** 
212Describes the current state of this watch. The exact details of said
213description are unspecified and subject to change.
214*/  
215public String toString() 
216  {
217  String str = myname;
218
219  str += ": Cum.Time=[" + cumulativeTime() + " ms]" + 
220      "; Start=[" + startTime + "]";
221  
222  if (! running) 
223    str +=  "; Stop=[" + stopTime + "]";
224  else 
225    str += "; Elapsed=[" + time() + " ms]";
226    
227  return str;
228  }
229
230public static void main(String[] args)
231  {
232  fc.util.Watch t1 = new fc.util.Watch("Watch 1");
233  t1.start();
234  
235  new Thread(new Runnable() { 
236    public void run() {
237      try { 
238        Watch t2 = new Watch();
239        t2.start(); Thread.currentThread().sleep(20); t2.stop(); 
240        System.out.println("t2.toString():" + t2);
241        } 
242      catch (Exception e) { e.printStackTrace(); }
243      } 
244    }).start();
245  
246  //following should return -1
247  System.out.println("Watch 1, total time taken:" + t1.time());
248
249  System.out.println("Watch 1, time=" + t1.time());
250  System.out.println("Watch 1, before-being-stopped, toString():" + t1);
251  t1.stop();
252  System.out.println("Watch 1, is running ? " + t1.isRunning() );
253  System.out.println("Watch 1, after-being-stopped, toString():" + t1);
254  System.out.println("Watch 1, elapsed time:" + t1.time());
255  System.out.println("Watch 1, cumulative time taken:" + t1.cumulativeTime());
256  System.out.println("Watch 1, elapsed time:" + t1.time());
257
258  new Thread(new Runnable() { 
259    public void run() {
260      try { 
261        Watch t2 = new Watch();
262        t2.start(); 
263        Thread.currentThread().sleep(250); 
264        t2.stop(); 
265        System.out.println("After sleeping 250ms: time in seconds:  " + t2.getTimeInSeconds());
266        t2.start();
267        Thread.currentThread().sleep(500); 
268        System.out.println("After sleeping 750ms: time in seconds:  " + t2.getTimeInSeconds());
269        Thread.currentThread().sleep(500); 
270        System.out.println("After sleeping 1250ms: time in seconds: " + t2.getTimeInSeconds());
271        Thread.currentThread().sleep(500); 
272        System.out.println("After sleeping 1750ms: time in seconds: " + t2.getTimeInSeconds());
273        Thread.currentThread().sleep(1000); 
274        System.out.println("After sleeping 2750ms: time in seconds: " + t2.getTimeInSeconds());
275        } 
276      catch (Exception e) { e.printStackTrace(); }
277      } 
278    }).start();
279  }
280
281} //~class Watch