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