1 /*
2  * Copyright (C) 1997-2005, R3vis Corporation.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
18  *
19  * Original Contributor:
20  *   Wes Bethel, R3vis Corporation, Marin County, California
21  * Additional Contributor(s):
22  *
23  * The OpenRM project is located at http://openrm.sourceforge.net/.
24  */
25 /*
26  * $Id: rmtime.c,v 1.15 2005/09/12 04:01:37 wes Exp $
27  * Version: $Name: OpenRM-1-6-0-2-RC2 $
28  * $Revision: 1.15 $
29  * $Log: rmtime.c,v $
30  * Revision 1.15  2005/09/12 04:01:37  wes
31  * Modified end-of-nap test in unix code to be <= rather than <.
32  *
33  * Revision 1.14  2005/09/10 19:07:41  wes
34  * Fix for Windows in time conversion. Fixes a long-standing bug in the
35  * constant-rate rendering code on Windows.
36  *
37  * Revision 1.13  2005/06/06 02:04:29  wes
38  * Lots of small additions to clean up compiler warnings.
39  *
40  * Revision 1.12  2005/03/19 17:17:19  wes
41  * Win32 clock init stuff, and deinit upon rmFinish (in response to report
42  * of 8-byte memory leak).
43  *
44  * Revision 1.11  2005/02/19 16:22:50  wes
45  * Distro sync and consolidation.
46  *
47  * Revision 1.10  2005/01/23 17:08:42  wes
48  * Minor tweaks to improve accuracy.
49  *
50  * Revision 1.9  2004/03/18 15:49:56  wes
51  * Fixed buglet in private_rmTimeSpinDelay that resulted in inaccurate
52  * spins for time values greater than, say, tens of ms.
53  *
54  * Revision 1.8  2004/02/23 03:04:01  wes
55  * *** empty log message ***
56  *
57  * Revision 1.7  2004/01/16 16:51:33  wes
58  * (1) Updated copyright line to 2004; (2) changed name of routine from
59  * rmTimeDifferenceMilliseconds() to rmTimeDifferenceMS(); (3) rewrote
60  * the spinlock routine, tested on Win32 & Unix.
61  *
62  * Revision 1.6  2003/12/06 03:26:06  wes
63  * Documentation tweaks to RMtime routines, updates to RMtime routines for
64  * Windows.
65  *
66  * Revision 1.5  2003/12/01 02:13:05  wes
67  * Additions to support constant frame-rate rendering on both Unix and Win32.
68  *
69  * Revision 1.4  2003/11/21 17:07:52  wes
70  * For RM_X platforms, use a spinlock to implement high-resolution sleeps.
71  *
72  * Revision 1.3  2003/11/16 16:20:06  wes
73  * Added routine rmTimeSleep.
74  *
75  * Revision 1.2  2003/11/05 15:24:29  wes
76  * Minor updates.
77  *
78  */
79 
80 #include <rm/rm.h>
81 #include "rmprivat.h"
82 
83 #ifdef RM_X
84 #include <sys/time.h>
85 /* unix-side uses gettimeofday() */
86 #endif
87 
88 #ifdef RM_WIN
89 static LARGE_INTEGER *static_clockFrequency=NULL;
90 static double static_usecsPerTick=0.0;
91 #endif
92 
93 
94 static void private_rmTimeSpinDelay(const RMtime *w, RMtime *prev);
95 
96 /*
97  * ----------------------------------------------------
98  * @Name rmTimeCurrent
99  @pstart
100  RMenum rmTimeCurrent (RMtime *result)
101  @pend
102 
103  @astart
104  RMtime *result - a handle to a caller-supplied RMtime struct.
105  @aend
106 
107  @dstart
108 
109  Time measurement in RM is performed by querying the time with rmTimeCurrent(),
110  by computing time difference with rmTimeDifference(), or computing the
111  elapsed time in milliseconds with rmTimeDifferenceMS().
112 
113  rmTimeCurrent() will initialize an RMtime object, which is essentially
114  the same as a "struct timeval" on Unix systems, with the current time. The
115  caller supplies a handle to an RMtime object, and it will be set to contain
116  the  current time. Time zones are not relevant, as the purpose of RM's timing
117  facilities is limited to taking millisecond-resolution measurements of time.
118  RM_CHILL is returned upon success, or RM_WHACKED upon failure.
119 
120  rmTimeDifference() takes two RMtimeVal objects that have been set with
121  rmTimeCurrent(), computes the difference in time between the two, and places
122  the difference into a third RMtime object.  RM_CHILL is returned upon
123  success, or RM_WHACKED upon failure.
124 
125  rmTimeDifferenceMS() takes two RMtime objects that have been set
126  with rmTimeCurrent(), and returns the difference in milliseconds between
127  the two.
128 
129  rmTimeSet() allows you to set the secs/usecs fields of the RMtime object
130  to specific values. rmTimeGet() will tell you what those values are.
131 
132  rmTimeSleep() implements a spinlock high-resolution delay function that
133  will block the caller for the amount of time (secs, usecs) specified in
134  the input RMtime object.
135 
136  rmTimeDecodeMS() will return a double value representing the number of
137  milliseconds represented by the values in the input RMtime object.
138  rmTimeEncodeMS() will accept a double value interpreted as milliseconds
139  and encode that information into an RMtime object.
140 
141  rmTimeNew() and rmTimeDelete() are used to create and destroy RMtime
142  objects, respectively.
143 
144  @dend
145  * ----------------------------------------------------
146  */
147 RMenum
rmTimeCurrent(RMtime * r)148 rmTimeCurrent(RMtime *r)
149 {
150 #ifdef RM_X
151     struct timeval t1;
152 
153     if (RM_ASSERT(r,"rmTimeCurrent error: the input RMtime object is NULL") == RM_WHACKED)
154 	return RM_WHACKED;
155 
156     gettimeofday(&t1, NULL);
157     r->sec = t1.tv_sec;
158     r->usec = t1.tv_usec;
159 
160 #else /* assume RM_WIN */
161 
162     LARGE_INTEGER ticks;
163     LARGE_INTEGER fracSecs;
164     /*
165      * in order to be completely thread safe, we need to protect the following
166      * if block with a mutex to prevent simultaneous access by multiple threads
167      */
168     if (RM_ASSERT(r,"rmTimeCurrent error: the input RMtime object is NULL") == RM_WHACKED)
169 	return RM_WHACKED;
170 
171     if (static_clockFrequency == NULL)
172     {
173 	static_clockFrequency = (LARGE_INTEGER *)malloc(sizeof(LARGE_INTEGER));
174 	if (QueryPerformanceFrequency(static_clockFrequency) == 0)
175 	{
176 	    rmWarning(" The Win32 implementation you are using does not provide a high resolution clock. This means that it is not possible to use any of the rmTime*() routines on your implementation of Windows.");
177 	    return RM_WHACKED;
178 	}
179     }
180 
181     if (QueryPerformanceCounter(&ticks) == 0)
182     {
183 	rmWarning(" The Win32 implementation you are using does not provide a high resolution clock. This means that it is not possible to use any of the rmTime*() routines on your implementation of Windows.");
184 	return RM_WHACKED;
185     }
186 
187     r->sec = (long)(ticks.QuadPart / static_clockFrequency->QuadPart); /* secs */
188     fracSecs.QuadPart = ticks.QuadPart % static_clockFrequency->QuadPart; /* ticks */
189     r->usec = (long)((double)fracSecs.QuadPart * static_usecsPerTick);
190 
191 #endif
192 
193     return RM_CHILL;
194 }
195 
196 /*
197  * ----------------------------------------------------
198  * @Name rmTimeDifference
199  @pstart
200  RMenum rmTimeDifference (const RMtime *start, const RMtime *end, RMtime *result)
201  @pend
202 
203  @astart
204  const RMtime *start, *end - handles to caller-supplied RMtime structs
205  that contain valid time values.
206  RMtime *result - a handle to a caler-supplied RMtime struct. This
207  value will be computed as the difference between "start" and "end."
208  @aend
209 
210  @dstart
211 
212  Time measurement in RM is performed by querying the time with rmTimeCurrent(),
213  by computing time difference with rmTimeDifference(), or computing the
214  elapsed time in milliseconds with rmTimeDifferenceMS().
215 
216  rmTimeDifference() takes as input two RMtime objects, start and end,
217  and computes the difference between them. The result is placed into the
218  "result" RMtime object.
219 
220  Limitations: it is assumed that time in "end" is later than the value
221  in "start." If this assumption doesn't hold, then the computed difference
222  may not be accurate.
223 
224  @dend
225  * ----------------------------------------------------
226  */
227 RMenum
rmTimeDifference(const RMtime * s,const RMtime * e,RMtime * d)228 rmTimeDifference(const RMtime *s,
229 		 const RMtime *e,
230 		 RMtime *d)
231 {
232     /* compute d = e - s */
233     long secs, usecs;
234 
235     if ((RM_ASSERT(s,"rmTimeDifference() error: the start RMtime is NULL")==RM_WHACKED) ||
236 	(RM_ASSERT(e,"rmTimeDifference() error: the end RMtimeVal is NULL")==RM_WHACKED) ||
237 	(RM_ASSERT(d,"rmTimeDifference() error: the result RMtimeVal is NULL") == RM_WHACKED))
238     {
239 	return RM_WHACKED;
240     }
241 
242     secs = e->sec - s->sec;
243     usecs = e->usec - s->usec;
244 
245     if (usecs < 0)
246     {
247 	usecs += 1000000;
248 	secs -= 1;
249     }
250 
251     d->sec = secs;
252     d->usec = usecs;
253     return RM_CHILL;
254 }
255 
256 /*
257  * ----------------------------------------------------
258  * @Name rmTimeDifferenceMS
259  @pstart
260  double rmTimeDifferenceMS (const RMtime *start, const RMtime *end)
261  @pend
262 
263  @astart
264  const RMtime *start, *end - handles to caller-supplied RMtime structs
265  that contain valid time values.
266  @aend
267 
268  @dstart
269 
270  Time measurement in RM is performed by querying the time with rmTimeCurrent(),
271  by computing time differences with rmTimeDifference(), and by reporting the
272  time difference in milliseconds with rmTimeDifferenceMS().
273 
274  rmTimeDifferenceMS() takes two RMtimeVal objects that have been
275  set with rmTimeCurrent(), computes the difference in time between the two,
276  and returns a double precision value indicating the number of milliseconds
277  difference between the two RMtime objects.
278 
279  It is assumed that the time value in "end" is greater than or equal to the
280  time value in "start." If this assumption does not hold, the computed
281  and returned difference may be inaccurate.
282 
283  Upon success, a non-negative integer is returned. Upon failure, -1 is
284  returned. If the time value in "end" is less than the time value in "start",
285  the returned value is not guaranteed to be accurate. It is the application's
286  responsibility to ensure the timevalue in "end" is greater than or equal
287  to the time value in "start".
288 
289  @dend
290  * ----------------------------------------------------
291  */
292 double
rmTimeDifferenceMS(const RMtime * s,const RMtime * e)293 rmTimeDifferenceMS(const RMtime *s,
294 		   const RMtime *e)
295 {
296     /* return d = e - s where d is msec */
297     long secs, usecs;
298     double msecs;
299 
300     if ((RM_ASSERT(s,"rmTimeDifferenceMS() error: the start RMtimeVal is NULL") == RM_WHACKED) ||
301 	(RM_ASSERT(e, "rmTimeDifferenceMS() error: the end RMtimeVal is NULL") == RM_WHACKED))
302 	return -1;
303 
304     secs = e->sec - s->sec;
305     usecs = e->usec - s->usec;
306 
307     if (usecs < 0)
308     {
309 	usecs += 1000000;
310 	secs -= 1;
311     }
312 
313     msecs = (double)(secs)*1000.0;
314     msecs += ((double)(usecs) * 0.001);
315 
316     return msecs;
317 }
318 
319 
320 /*
321  * ----------------------------------------------------
322  * @Name rmTimeNew
323  @pstart
324  RMtime * rmTimeNew(void)
325  @pend
326 
327  @astart
328  No arguments.
329  @aend
330 
331  @dstart
332  rmTimeNew() will create an RMtime object, and return the handle of the
333  new RMtime object to the caller upon success. Upon failure, NULL is
334  returned.
335 
336  No special processing is performed inside rmTimeNew(). It is safe for
337  applications to allocate RMtime objects off the stack using normal
338  variable declaration if so desired.
339 
340  @dend
341  * ----------------------------------------------------
342  */
343 RMtime *
rmTimeNew(void)344 rmTimeNew(void)
345 {
346     RMtime *r = (RMtime *)malloc(sizeof(RMtime));
347     if (r == NULL)
348     {
349 	rmError("rmTimeNew() malloc failure.");
350 	return NULL;
351     }
352 
353     rmTimeSet(r, (long)0, (long)0);
354     return r;
355 }
356 
357 /*
358  * ----------------------------------------------------
359  * @Name rmTimeDelete
360  @pstart
361  RMenum rmTimeDelete(RMtime *toDelete)
362  @pend
363 
364  @astart
365  RMtime *toDelete - handle to an RMtime object that will be deleted.
366  @aend
367 
368  @dstart
369  This routine will free resources associated with the input RMtime object
370  "toDelete." Upon success, RM_CHILL is returned. Upon failure, RM_WHACKED
371  is returned.
372  @dend
373  * ----------------------------------------------------
374  */
375 RMenum
rmTimeDelete(RMtime * d)376 rmTimeDelete(RMtime *d)
377 {
378     if (RM_ASSERT(d,"rmTimeDelete() error: the input RMtime is NULL."))
379 	return RM_WHACKED;
380 
381     free((void *)d);
382     return RM_CHILL;
383 }
384 
385 /*
386  * ----------------------------------------------------
387  * @Name rmTimeSet
388  @pstart
389  RMenum rmTimeSet(RMtime *toModify, long secs, long usecs)
390  @pend
391 
392  @astart
393  long secs, usecs: input arguments specifing number of seconds and
394  microseconds, respectively.
395  @aend
396 
397  @dstart
398  rmTimeSet is used to assign known values to the RMtime object
399  "toModify." The RMtime object contains two fields - seconds and
400  microseconds. When set by rmTimeCurrent(), those values represent
401  the amount of time elapsed  since the current epoch.
402 
403  (Note that since we use longs, OpenRM is Y2038 safe!!)
404 
405  Upon success, RM_CHILL is returned. Upon failure, RM_WHACKED is returned.
406 
407  Limitations: OpenRM doesn't perform any sanity checking on the input
408  values for secs/usecs. It is possible to assign garbage to an RMtime
409  object.
410 
411  @dend
412  * ----------------------------------------------------
413  */
414 RMenum
rmTimeSet(RMtime * r,long secs,long usecs)415 rmTimeSet(RMtime *r,
416 	  long secs,
417 	  long usecs)
418 {
419     if (RM_ASSERT(r,"rmTimeSet() error: the input RMtime is NULL.") == RM_WHACKED)
420 	return RM_WHACKED;
421 
422     r->sec = secs;
423     r->usec = usecs;
424 
425     return RM_CHILL;
426 }
427 
428 /*
429  * ----------------------------------------------------
430  * @Name rmTimeGet
431  @pstart
432  RMenum rmTimeGet(const RMtime *toQuery, long *returnSecs, long *returnUSecs)
433  @pend
434 
435  @astart
436  const RMtime *toQuery - an input RMtime object.
437  long *returnSecs, long *returnUSecs - pointers to longs.
438  @aend
439 
440  @dstart
441  rmTimeGet() will copy the seconds/microseconds fields from the input RMtime
442  object "toQuery" into the memory pointed to by "returnSecs" and
443  "returnUSecs."
444 
445  If you just want to know the number of milliseconds contained in an RMtime
446  representation, use the routine rmTimeDecodsMS().
447 
448  RM_CHILL is returned upon success, or RM_WHACKED upon failure.
449 
450  @dend
451  * ----------------------------------------------------
452  */
453 RMenum
rmTimeGet(const RMtime * r,long * returnSecs,long * returnUSecs)454 rmTimeGet(const RMtime *r,
455 	  long *returnSecs,
456 	  long *returnUSecs)
457 {
458     if (RM_ASSERT(r,"rmTimeGet() error: the input RMtime object is NULL.") == RM_WHACKED)
459 	return RM_WHACKED;
460 
461     if (returnSecs != NULL)
462 	*returnSecs = r->sec;
463     if (returnUSecs != NULL)
464 	*returnUSecs = r->usec;
465 
466     return RM_CHILL;
467 }
468 
469 
470 /* this is routine that was used internally for testing. it permits the
471    caller to know how much error there was from a sleep operation. it works
472    OK, but isn't really needed for our purposes. We might add it back into
473    the public API at some point if someone asks for it. */
474 RMenum
rmTimeSleepDrift(const RMtime * tSleep,RMtime * drift)475 rmTimeSleepDrift(const RMtime *tSleep,
476 		 RMtime *drift)
477 {
478     private_rmTimeSpinDelay(tSleep, drift);
479     return RM_CHILL;
480 }
481 
482 
483 /*
484  * ----------------------------------------------------
485  * @Name rmTimeSleep
486  @pstart
487  RMenum rmTimeSleep(const RMtime *toSleep)
488  @pend
489 
490  @astart
491  RMtime *toSleep - input RMtime object that specifies the length of the
492  sleep period.
493 
494  @aend
495 
496  @dstart
497  rmTimeSleep() will block execution of the caller for the amount of time
498  specified in the toSleep argument. This routine will block execution for
499  a period of time that is at least as long as the time specified in the
500  toSleep object. In our preliminary testing, we have found the error to
501  be quite small - less than a microsecond on current hardware.
502 
503  Internally, a high-resolution spinlock is used to implement precision naps.
504  There is a large body of knowledge on the subject of problems with precision
505  sleeps. The brute-force solution is to use a spinlock, which is what we do
506  here. The two primary advantages of the spinlock are: (1) they are of
507  a very high resolution, and (2) the time required to wake up from a nap
508  is less likely to be interruped or influenced by the OS doing something
509  else, like checking to see if the printer is awake.
510 
511  Since we use a spinlock, your CPU meter will be pegged at 100% during
512  rmTimeSleep naps.
513 
514  This routine returns RM_CHILL upon success, or RM_WHACKED upon failure.
515 
516  @dend
517  * ----------------------------------------------------
518  */
519 RMenum
rmTimeSleep(const RMtime * tSleep)520 rmTimeSleep(const RMtime *tSleep)
521 {
522     if (RM_ASSERT(tSleep,"rmTimeSleep() error: the input RMtime object is NULL.") == RM_WHACKED)
523 	return RM_WHACKED;
524 
525     private_rmTimeSpinDelay(tSleep, NULL);
526     return RM_CHILL;
527 }
528 
529 
530 /*
531  * ----------------------------------------------------
532  * @Name rmTimeEncodeMS
533  @pstart
534  RMenum rmTimeEncodeMS(RMtime *toModify, double ms)
535  @pend
536 
537  @astart
538  RMtime *toModify - input RMtime object to be modified.
539  double ms - double precision value interpreted as milliseconds.
540  @aend
541 
542  @dstart
543  rmTimeEncodeMS() will take an input double precision value that is
544  interpreted as milliseconds, and encode the number of milliseconds
545  (that may be fractional) into the RMtime object.
546 
547  RM_CHILL is returned upon success, and RM_WHACKED is returned upon
548  failure.
549 
550  @dend
551  * ----------------------------------------------------
552  */
553 RMenum
rmTimeEncodeMS(RMtime * t,double ms)554 rmTimeEncodeMS(RMtime *t,
555 	       double ms)
556 {
557     long sec, usec;
558 
559     if (RM_ASSERT(t,"rmTimeEncodeMS() error: the input RMtime object is NULL.") == RM_WHACKED)
560 	return RM_WHACKED;
561 
562     sec = (long)(ms) / 1000;
563     usec = (long)(ms * 1000.0F) % 1000000;
564 
565     rmTimeSet(t, sec, usec);
566 
567     return RM_CHILL;
568 }
569 
570 /*
571  * ----------------------------------------------------
572  * @Name rmTimeDecodeMS
573  @pstart
574  RMenum rmTimeDecodeMS(const RMtime *toQuery, double *resultMS)
575  @pend
576 
577  @astart
578  const RMtime *toQuery - an input RMtime object.
579  double *resultMS - a pointer to a double precision value; the results of
580    this routine will be returned in this caller-supplied memory.
581  @aend
582 
583  @dstart
584  rmTimeDecodeMS will compute the number of milliseconds represented by
585  the contents of the input RMtime object toQuery, and return the results
586  into caller supplied memory.
587 
588  RM_CHILL is returned upon success, and RM_WHACKED is returned upon
589  failure.
590 
591  @dend
592  * ----------------------------------------------------
593  */
594 RMenum
rmTimeDecodeMS(const RMtime * src,double * resultMS)595 rmTimeDecodeMS(const RMtime *src, double *resultMS)
596 {
597     double r;
598     long sec, usec;
599 
600     if (RM_ASSERT(src,"rmDecodeMS() error: the input RMtime object is NULL.") == RM_WHACKED)
601 	return RM_WHACKED;
602 
603     rmTimeGet(src, &sec, &usec);
604 
605     r = (double)(sec * 1000);
606     r += (double)(usec / 1000);
607 
608     *resultMS = r;
609 
610     return RM_CHILL;
611 }
612 
613 /* internal/private - this is the spindelay loop. */
614 static void
private_rmTimeSpinDelay(const RMtime * w,RMtime * drift)615 private_rmTimeSpinDelay(const RMtime *w,
616 			RMtime *drift)
617 {
618     /*
619      * the X (non-Windoze) version of the timer interfaces directly
620      * to gettimeofday(), and uses only integer arithmetic. the
621      * Win32 version uses floating point math.
622      *
623      * Early testing showed that the code in the Win32 section, which is
624      * painfully correct and easy to read, runs more slowly in some
625      * circumstances than the RM_X code that uses gettimeofday() and
626      * integer math. Oh well.
627      *
628      * Testing of the Win32 code shows that the Win32 clock is of
629      * higher resolution and accuracy than that affored by gettimeofday().
630      * Sad, but true.
631      *
632      * Future work:
633      * 1. strive for even better accuracy
634      * 2. Update the windoze code to use only integer math.
635      */
636 #ifdef RM_X
637     struct timeval start,now,d;
638     gettimeofday(&start,0);
639     do
640     {
641 	gettimeofday(&now,0);
642 	d.tv_sec = now.tv_sec - start.tv_sec;
643 	d.tv_usec = now.tv_usec - start.tv_usec;
644 
645 	if (d.tv_usec < 0)
646 	{
647 	    d.tv_usec += 1000000;
648 	    d.tv_sec--;
649 	}
650 
651     }
652     while((d.tv_sec<w->sec) || ((d.tv_sec==w->sec && d.tv_usec<=w->usec)));
653 
654     if (drift != NULL)
655     {
656 	long usec = d.tv_usec - w->usec;
657 
658 	if (usec < 0)
659 	    rmWarning("private_rmTimeSpinDelay() : usec < 0 \n");
660 	/*	usec++; */ /* fudge factor */
661 	usec--;
662 	rmTimeSet(drift, 0, usec);
663     }
664 #else
665 
666     RMtime start, now, d;
667     double waitMS;
668     double startMS, nowMS, deltaMS;
669 
670     rmTimeDecodeMS(w,&waitMS);
671     rmTimeCurrent(&start);
672     rmTimeDecodeMS(&start, &startMS);
673 
674     rmTimeCurrent(&now);
675     rmTimeDecodeMS(&now, &nowMS);
676     deltaMS = nowMS - startMS;
677 
678     for (; deltaMS < waitMS ; )
679     {
680 	rmTimeCurrent(&now);
681 	rmTimeDecodeMS(&now, &nowMS);
682 	deltaMS = nowMS - startMS;
683     }
684 
685     /*
686      * how late are we?
687      * d = now-start, which is the amount of elapsed time
688      * so d-w would the the amount we are over budget. We assume
689      * that d >= w, and we also assume that the seconds component of
690      * d and w are equal.
691      */
692     if (drift != NULL)
693     {
694 	double driftMS = deltaMS - waitMS;
695 	rmTimeEncodeMS(drift, driftMS);
696 
697     }
698 #endif
699 
700 }
701 
702 RMenum
private_initTimer(void)703 private_initTimer(void)
704 {
705 #ifdef RM_WIN
706     if (static_clockFrequency == NULL)
707     {
708 	static_clockFrequency = (LARGE_INTEGER *)malloc(sizeof(LARGE_INTEGER));
709 	if (QueryPerformanceFrequency(static_clockFrequency) == 0)
710 	{
711 	    rmWarning(" The Win32 implementation you are using does not provide a high resolution clock. This means that it is not possible to use any of the rmTime*() routines on your implementation of Windows.");
712 	    return RM_WHACKED;
713 	}
714 	static_usecsPerTick = 1000000.0/(double)((static_clockFrequency->QuadPart));
715 
716     }
717 #endif
718     /* only windows needs a timer init */
719     return RM_CHILL;
720 }
721 
722 void
private_deInitTimer(void)723 private_deInitTimer(void)
724 {
725 #ifdef RM_WIN
726     if (static_clockFrequency != NULL)
727 	free((void *)static_clockFrequency);
728 #endif
729 }
730 
731 /* EOF */
732