Upgrade to Time::HiRes 1.63.
[p5sagit/p5-mst-13.2.git] / ext / Time / HiRes / HiRes.xs
1 #ifdef __cplusplus
2 extern "C" {
3 #endif
4 #define PERL_NO_GET_CONTEXT
5 #include "EXTERN.h"
6 #include "perl.h"
7 #include "XSUB.h"
8 #include "ppport.h"
9 #if defined(__CYGWIN__) && defined(HAS_W32API_WINDOWS_H)
10 # include <w32api/windows.h>
11 # define CYGWIN_WITH_W32API
12 #endif
13 #ifdef WIN32
14 # include <time.h>
15 #else
16 # include <sys/time.h>
17 #endif
18 #ifdef HAS_SELECT
19 # ifdef I_SYS_SELECT
20 #  include <sys/select.h>
21 # endif
22 #endif
23 #ifdef __cplusplus
24 }
25 #endif
26
27 #ifdef START_MY_CXT
28 #  ifndef MY_CXT_CLONE
29 #    define MY_CXT_CLONE                                                \
30         dMY_CXT_SV;                                                     \
31         my_cxt_t *my_cxtp = (my_cxt_t*)SvPVX(newSV(sizeof(my_cxt_t)-1));\
32         Copy(INT2PTR(my_cxt_t*, SvUV(my_cxt_sv)), my_cxtp, 1, my_cxt_t); \
33         sv_setuv(my_cxt_sv, PTR2UV(my_cxtp))
34 #  endif
35 #else
36 #    define MY_CXT_CLONE NOOP
37 #endif
38
39 #ifndef PerlProc_pause
40 #   define PerlProc_pause() Pause()
41 #endif
42
43 #ifdef HAS_PAUSE
44 #   define Pause   pause
45 #else
46 #   define Pause() sleep(~0)
47 #endif
48
49 /* Though the cpp define ITIMER_VIRTUAL is available the functionality
50  * is not supported in Cygwin as of August 2004, ditto for Win32.
51  * Neither are ITIMER_PROF or ITIMER_REALPROF implemented.  --jhi
52  */
53 #if defined(__CYGWIN__) || defined(WIN32)
54 #   undef ITIMER_VIRTUAL
55 #   undef ITIMER_PROF
56 #   undef ITIMER_REALPROF
57 #endif
58
59 /* 5.004 doesn't define PL_sv_undef */
60 #ifndef ATLEASTFIVEOHOHFIVE
61 # ifndef PL_sv_undef
62 #  define PL_sv_undef sv_undef
63 # endif
64 #endif
65
66 #include "const-c.inc"
67
68 #if defined(WIN32) || defined(CYGWIN_WITH_W32API)
69
70 #ifndef HAS_GETTIMEOFDAY
71 #   define HAS_GETTIMEOFDAY
72 #endif
73
74 /* shows up in winsock.h?
75 struct timeval {
76  long tv_sec;
77  long tv_usec;
78 }
79 */
80
81 typedef union {
82     unsigned __int64    ft_i64;
83     FILETIME            ft_val;
84 } FT_t;
85
86 #define MY_CXT_KEY "Time::HiRes_" XS_VERSION
87
88 typedef struct {
89     unsigned long run_count;
90     unsigned __int64 base_ticks;
91     unsigned __int64 tick_frequency;
92     FT_t base_systime_as_filetime;
93     unsigned __int64 reset_time;
94 } my_cxt_t;
95
96 START_MY_CXT
97
98 /* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 */
99 #ifdef __GNUC__
100 # define Const64(x) x##LL
101 #else
102 # define Const64(x) x##i64
103 #endif
104 #define EPOCH_BIAS  Const64(116444736000000000)
105
106 /* NOTE: This does not compute the timezone info (doing so can be expensive,
107  * and appears to be unsupported even by glibc) */
108
109 /* dMY_CXT needs a Perl context and we don't want to call PERL_GET_CONTEXT
110    for performance reasons */
111
112 #undef gettimeofday
113 #define gettimeofday(tp, not_used) _gettimeofday(aTHX_ tp, not_used)
114
115 /* If the performance counter delta drifts more than 0.5 seconds from the
116  * system time then we recalibrate to the system time.  This means we may
117  * move *backwards* in time! */
118 #define MAX_PERF_COUNTER_SKEW Const64(5000000) /* 0.5 seconds */
119
120 /* Reset reading from the performance counter every five minutes.
121  * Many PC clocks just seem to be so bad. */
122 #define MAX_PERF_COUNTER_TICKS Const64(300000000) /* 300 seconds */
123
124 static int
125 _gettimeofday(pTHX_ struct timeval *tp, void *not_used)
126 {
127     dMY_CXT;
128
129     unsigned __int64 ticks;
130     FT_t ft;
131
132     if (MY_CXT.run_count++ == 0 ||
133         MY_CXT.base_systime_as_filetime.ft_i64 > MY_CXT.reset_time) {
134         QueryPerformanceFrequency((LARGE_INTEGER*)&MY_CXT.tick_frequency);
135         QueryPerformanceCounter((LARGE_INTEGER*)&MY_CXT.base_ticks);
136         GetSystemTimeAsFileTime(&MY_CXT.base_systime_as_filetime.ft_val);
137         ft.ft_i64 = MY_CXT.base_systime_as_filetime.ft_i64;
138         MY_CXT.reset_time = ft.ft_i64 + MAX_PERF_COUNTER_TICKS;
139     }
140     else {
141         __int64 diff;
142         QueryPerformanceCounter((LARGE_INTEGER*)&ticks);
143         ticks -= MY_CXT.base_ticks;
144         ft.ft_i64 = MY_CXT.base_systime_as_filetime.ft_i64
145                     + Const64(10000000) * (ticks / MY_CXT.tick_frequency)
146                     +(Const64(10000000) * (ticks % MY_CXT.tick_frequency)) / MY_CXT.tick_frequency;
147         diff = ft.ft_i64 - MY_CXT.base_systime_as_filetime.ft_i64;
148         if (diff < -MAX_PERF_COUNTER_SKEW || diff > MAX_PERF_COUNTER_SKEW) {
149             MY_CXT.base_ticks += ticks;
150             GetSystemTimeAsFileTime(&MY_CXT.base_systime_as_filetime.ft_val);
151             ft.ft_i64 = MY_CXT.base_systime_as_filetime.ft_i64;
152         }
153     }
154
155     /* seconds since epoch */
156     tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / Const64(10000000));
157
158     /* microseconds remaining */
159     tp->tv_usec = (long)((ft.ft_i64 / Const64(10)) % Const64(1000000));
160
161     return 0;
162 }
163 #endif
164
165 #if defined(WIN32) && !defined(ATLEASTFIVEOHOHFIVE)
166 static unsigned int
167 sleep(unsigned int t)
168 {
169     Sleep(t*1000);
170     return 0;
171 }
172 #endif
173
174 #if !defined(HAS_GETTIMEOFDAY) && defined(VMS)
175 #define HAS_GETTIMEOFDAY
176
177 #include <lnmdef.h>
178 #include <time.h> /* gettimeofday */
179 #include <stdlib.h> /* qdiv */
180 #include <starlet.h> /* sys$gettim */
181 #include <descrip.h>
182 #ifdef __VAX
183 #include <lib$routines.h> /* lib$ediv() */
184 #endif
185
186 /*
187         VMS binary time is expressed in 100 nano-seconds since
188         system base time which is 17-NOV-1858 00:00:00.00
189 */
190
191 #define DIV_100NS_TO_SECS  10000000L
192 #define DIV_100NS_TO_USECS 10L
193
194 /* 
195         gettimeofday is supposed to return times since the epoch
196         so need to determine this in terms of VMS base time
197 */
198 static $DESCRIPTOR(dscepoch,"01-JAN-1970 00:00:00.00");
199
200 #ifdef __VAX
201 static long base_adjust[2]={0L,0L};
202 #else
203 static __int64 base_adjust=0;
204 #endif
205
206 /* 
207
208    If we don't have gettimeofday, then likely we are on a VMS machine that
209    operates on local time rather than UTC...so we have to zone-adjust.
210    This code gleefully swiped from VMS.C 
211
212 */
213 /* method used to handle UTC conversions:
214  *   1 == CRTL gmtime();  2 == SYS$TIMEZONE_DIFFERENTIAL;  3 == no correction
215  */
216 static int gmtime_emulation_type;
217 /* number of secs to add to UTC POSIX-style time to get local time */
218 static long int utc_offset_secs;
219 static struct dsc$descriptor_s fildevdsc = 
220   { 12, DSC$K_DTYPE_T, DSC$K_CLASS_S, "LNM$FILE_DEV" };
221 static struct dsc$descriptor_s *fildev[] = { &fildevdsc, NULL };
222
223 static time_t toutc_dst(time_t loc) {
224   struct tm *rsltmp;
225
226   if ((rsltmp = localtime(&loc)) == NULL) return -1;
227   loc -= utc_offset_secs;
228   if (rsltmp->tm_isdst) loc -= 3600;
229   return loc;
230 }
231
232 static time_t toloc_dst(time_t utc) {
233   struct tm *rsltmp;
234
235   utc += utc_offset_secs;
236   if ((rsltmp = localtime(&utc)) == NULL) return -1;
237   if (rsltmp->tm_isdst) utc += 3600;
238   return utc;
239 }
240
241 #define _toutc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
242        ((gmtime_emulation_type || timezone_setup()), \
243        (gmtime_emulation_type == 1 ? toutc_dst(secs) : \
244        ((secs) - utc_offset_secs))))
245
246 #define _toloc(secs)  ((secs) == (time_t) -1 ? (time_t) -1 : \
247        ((gmtime_emulation_type || timezone_setup()), \
248        (gmtime_emulation_type == 1 ? toloc_dst(secs) : \
249        ((secs) + utc_offset_secs))))
250
251 static int
252 timezone_setup(void) 
253 {
254   struct tm *tm_p;
255
256   if (gmtime_emulation_type == 0) {
257     int dstnow;
258     time_t base = 15 * 86400; /* 15jan71; to avoid month/year ends between    */
259                               /* results of calls to gmtime() and localtime() */
260                               /* for same &base */
261
262     gmtime_emulation_type++;
263     if ((tm_p = gmtime(&base)) == NULL) { /* CRTL gmtime() is a fake */
264       char off[LNM$C_NAMLENGTH+1];;
265
266       gmtime_emulation_type++;
267       if (!Perl_vmstrnenv("SYS$TIMEZONE_DIFFERENTIAL",off,0,fildev,0)) {
268         gmtime_emulation_type++;
269         utc_offset_secs = 0;
270         Perl_warn(aTHX_ "no UTC offset information; assuming local time is UTC");
271       }
272       else { utc_offset_secs = atol(off); }
273     }
274     else { /* We've got a working gmtime() */
275       struct tm gmt, local;
276
277       gmt = *tm_p;
278       tm_p = localtime(&base);
279       local = *tm_p;
280       utc_offset_secs  = (local.tm_mday - gmt.tm_mday) * 86400;
281       utc_offset_secs += (local.tm_hour - gmt.tm_hour) * 3600;
282       utc_offset_secs += (local.tm_min  - gmt.tm_min)  * 60;
283       utc_offset_secs += (local.tm_sec  - gmt.tm_sec);
284     }
285   }
286   return 1;
287 }
288
289
290 int
291 gettimeofday (struct timeval *tp, void *tpz)
292 {
293  long ret;
294 #ifdef __VAX
295  long quad[2];
296  long quad1[2];
297  long div_100ns_to_secs;
298  long div_100ns_to_usecs;
299  long quo,rem;
300  long quo1,rem1;
301 #else
302  __int64 quad;
303  __qdiv_t ans1,ans2;
304 #endif
305 /*
306         In case of error, tv_usec = 0 and tv_sec = VMS condition code.
307         The return from function is also set to -1.
308         This is not exactly as per the manual page.
309 */
310
311  tp->tv_usec = 0;
312
313 #ifdef __VAX
314  if (base_adjust[0]==0 && base_adjust[1]==0) {
315 #else
316  if (base_adjust==0) { /* Need to determine epoch adjustment */
317 #endif
318         ret=sys$bintim(&dscepoch,&base_adjust);
319         if (1 != (ret &&1)) {
320                 tp->tv_sec = ret;
321                 return -1;
322         }
323  }
324
325  ret=sys$gettim(&quad); /* Get VMS system time */
326  if ((1 && ret) == 1) {
327 #ifdef __VAX
328         quad[0] -= base_adjust[0]; /* convert to epoch offset */
329         quad[1] -= base_adjust[1]; /* convert 2nd half of quadword */
330         div_100ns_to_secs = DIV_100NS_TO_SECS;
331         div_100ns_to_usecs = DIV_100NS_TO_USECS;
332         lib$ediv(&div_100ns_to_secs,&quad,&quo,&rem);
333         quad1[0] = rem;
334         quad1[1] = 0L;
335         lib$ediv(&div_100ns_to_usecs,&quad1,&quo1,&rem1);
336         tp->tv_sec = quo; /* Whole seconds */
337         tp->tv_usec = quo1; /* Micro-seconds */
338 #else
339         quad -= base_adjust; /* convert to epoch offset */
340         ans1=qdiv(quad,DIV_100NS_TO_SECS);
341         ans2=qdiv(ans1.rem,DIV_100NS_TO_USECS);
342         tp->tv_sec = ans1.quot; /* Whole seconds */
343         tp->tv_usec = ans2.quot; /* Micro-seconds */
344 #endif
345  } else {
346         tp->tv_sec = ret;
347         return -1;
348  }
349 # ifdef VMSISH_TIME
350 # ifdef RTL_USES_UTC
351   if (VMSISH_TIME) tp->tv_sec = _toloc(tp->tv_sec);
352 # else
353   if (!VMSISH_TIME) tp->tv_sec = _toutc(tp->tv_sec);
354 # endif
355 # endif
356  return 0;
357 }
358 #endif
359
360
361  /* Do not use H A S _ N A N O S L E E P
362   * so that Perl Configure doesn't scan for it.
363   * The TIME_HIRES_NANOSLEEP is set by Makefile.PL. */
364 #if !defined(HAS_USLEEP) && defined(TIME_HIRES_NANOSLEEP)
365 #define HAS_USLEEP
366 #define usleep hrt_nanosleep  /* could conflict with ncurses for static build */
367
368 void
369 hrt_nanosleep(unsigned long usec)
370 {
371     struct timespec res;
372     res.tv_sec = usec/1000/1000;
373     res.tv_nsec = ( usec - res.tv_sec*1000*1000 ) * 1000;
374     nanosleep(&res, NULL);
375 }
376 #endif
377
378
379 #if !defined(HAS_USLEEP) && defined(HAS_SELECT)
380 #ifndef SELECT_IS_BROKEN
381 #define HAS_USLEEP
382 #define usleep hrt_usleep  /* could conflict with ncurses for static build */
383
384 void
385 hrt_usleep(unsigned long usec)
386 {
387     struct timeval tv;
388     tv.tv_sec = 0;
389     tv.tv_usec = usec;
390     select(0, (Select_fd_set_t)NULL, (Select_fd_set_t)NULL,
391                 (Select_fd_set_t)NULL, &tv);
392 }
393 #endif
394 #endif
395
396 #if !defined(HAS_USLEEP) && defined(WIN32)
397 #define HAS_USLEEP
398 #define usleep hrt_usleep  /* could conflict with ncurses for static build */
399
400 void
401 hrt_usleep(unsigned long usec)
402 {
403     long msec;
404     msec = usec / 1000;
405     Sleep (msec);
406 }
407 #endif
408
409
410 #if !defined(HAS_UALARM) && defined(HAS_SETITIMER)
411 #define HAS_UALARM
412 #define ualarm hrt_ualarm  /* could conflict with ncurses for static build */
413
414 int
415 hrt_ualarm(int usec, int interval)
416 {
417    struct itimerval itv;
418    itv.it_value.tv_sec = usec / 1000000;
419    itv.it_value.tv_usec = usec % 1000000;
420    itv.it_interval.tv_sec = interval / 1000000;
421    itv.it_interval.tv_usec = interval % 1000000;
422    return setitimer(ITIMER_REAL, &itv, 0);
423 }
424 #endif
425
426 #if !defined(HAS_UALARM) && defined(VMS)
427 #define HAS_UALARM
428 #define ualarm vms_ualarm 
429
430 #include <lib$routines.h>
431 #include <ssdef.h>
432 #include <starlet.h>
433 #include <descrip.h>
434 #include <signal.h>
435 #include <jpidef.h>
436 #include <psldef.h>
437
438 #define VMSERR(s)   (!((s)&1))
439
440 static void
441 us_to_VMS(useconds_t mseconds, unsigned long v[])
442 {
443     int iss;
444     unsigned long qq[2];
445
446     qq[0] = mseconds;
447     qq[1] = 0;
448     v[0] = v[1] = 0;
449
450     iss = lib$addx(qq,qq,qq);
451     if (VMSERR(iss)) lib$signal(iss);
452     iss = lib$subx(v,qq,v);
453     if (VMSERR(iss)) lib$signal(iss);
454     iss = lib$addx(qq,qq,qq);
455     if (VMSERR(iss)) lib$signal(iss);
456     iss = lib$subx(v,qq,v);
457     if (VMSERR(iss)) lib$signal(iss);
458     iss = lib$subx(v,qq,v);
459     if (VMSERR(iss)) lib$signal(iss);
460 }
461
462 static int
463 VMS_to_us(unsigned long v[])
464 {
465     int iss;
466     unsigned long div=10,quot, rem;
467
468     iss = lib$ediv(&div,v,&quot,&rem);
469     if (VMSERR(iss)) lib$signal(iss);
470
471     return quot;
472 }
473
474 typedef unsigned short word;
475 typedef struct _ualarm {
476     int function;
477     int repeat;
478     unsigned long delay[2];
479     unsigned long interval[2];
480     unsigned long remain[2];
481 } Alarm;
482
483
484 static int alarm_ef;
485 static Alarm *a0, alarm_base;
486 #define UAL_NULL   0
487 #define UAL_SET    1
488 #define UAL_CLEAR  2
489 #define UAL_ACTIVE 4
490 static void ualarm_AST(Alarm *a);
491
492 static int 
493 vms_ualarm(int mseconds, int interval)
494 {
495     Alarm *a, abase;
496     struct item_list3 {
497         word length;
498         word code;
499         void *bufaddr;
500         void *retlenaddr;
501     } ;
502     static struct item_list3 itmlst[2];
503     static int first = 1;
504     unsigned long asten;
505     int iss, enabled;
506
507     if (first) {
508         first = 0;
509         itmlst[0].code       = JPI$_ASTEN;
510         itmlst[0].length     = sizeof(asten);
511         itmlst[0].retlenaddr = NULL;
512         itmlst[1].code       = 0;
513         itmlst[1].length     = 0;
514         itmlst[1].bufaddr    = NULL;
515         itmlst[1].retlenaddr = NULL;
516
517         iss = lib$get_ef(&alarm_ef);
518         if (VMSERR(iss)) lib$signal(iss);
519
520         a0 = &alarm_base;
521         a0->function = UAL_NULL;
522     }
523     itmlst[0].bufaddr    = &asten;
524     
525     iss = sys$getjpiw(0,0,0,itmlst,0,0,0);
526     if (VMSERR(iss)) lib$signal(iss);
527     if (!(asten&0x08)) return -1;
528
529     a = &abase;
530     if (mseconds) {
531         a->function = UAL_SET;
532     } else {
533         a->function = UAL_CLEAR;
534     }
535
536     us_to_VMS(mseconds, a->delay);
537     if (interval) {
538         us_to_VMS(interval, a->interval);
539         a->repeat = 1;
540     } else 
541         a->repeat = 0;
542
543     iss = sys$clref(alarm_ef);
544     if (VMSERR(iss)) lib$signal(iss);
545
546     iss = sys$dclast(ualarm_AST,a,0);
547     if (VMSERR(iss)) lib$signal(iss);
548
549     iss = sys$waitfr(alarm_ef);
550     if (VMSERR(iss)) lib$signal(iss);
551
552     if (a->function == UAL_ACTIVE) 
553         return VMS_to_us(a->remain);
554     else
555         return 0;
556 }
557
558
559
560 static void
561 ualarm_AST(Alarm *a)
562 {
563     int iss;
564     unsigned long now[2];
565
566     iss = sys$gettim(now);
567     if (VMSERR(iss)) lib$signal(iss);
568
569     if (a->function == UAL_SET || a->function == UAL_CLEAR) {
570         if (a0->function == UAL_ACTIVE) {
571             iss = sys$cantim(a0,PSL$C_USER);
572             if (VMSERR(iss)) lib$signal(iss);
573
574             iss = lib$subx(a0->remain, now, a->remain);
575             if (VMSERR(iss)) lib$signal(iss);
576
577             if (a->remain[1] & 0x80000000) 
578                 a->remain[0] = a->remain[1] = 0;
579         }
580
581         if (a->function == UAL_SET) {
582             a->function = a0->function;
583             a0->function = UAL_ACTIVE;
584             a0->repeat = a->repeat;
585             if (a0->repeat) {
586                 a0->interval[0] = a->interval[0];
587                 a0->interval[1] = a->interval[1];
588             }
589             a0->delay[0] = a->delay[0];
590             a0->delay[1] = a->delay[1];
591
592             iss = lib$subx(now, a0->delay, a0->remain);
593             if (VMSERR(iss)) lib$signal(iss);
594
595             iss = sys$setimr(0,a0->delay,ualarm_AST,a0);
596             if (VMSERR(iss)) lib$signal(iss);
597         } else {
598             a->function = a0->function;
599             a0->function = UAL_NULL;
600         }
601         iss = sys$setef(alarm_ef);
602         if (VMSERR(iss)) lib$signal(iss);
603     } else if (a->function == UAL_ACTIVE) {
604         if (a->repeat) {
605             iss = lib$subx(now, a->interval, a->remain);
606             if (VMSERR(iss)) lib$signal(iss);
607
608             iss = sys$setimr(0,a->interval,ualarm_AST,a);
609             if (VMSERR(iss)) lib$signal(iss);
610         } else {
611             a->function = UAL_NULL;
612         }
613         iss = sys$wake(0,0);
614         if (VMSERR(iss)) lib$signal(iss);
615         lib$signal(SS$_ASTFLT);
616     } else {
617         lib$signal(SS$_BADPARAM);
618     }
619 }
620
621 #endif /* !HAS_UALARM && VMS */
622
623 #ifdef HAS_GETTIMEOFDAY
624
625 static int
626 myU2time(pTHX_ UV *ret)
627 {
628   struct timeval Tp;
629   int status;
630   status = gettimeofday (&Tp, NULL);
631   ret[0] = Tp.tv_sec;
632   ret[1] = Tp.tv_usec;
633   return status;
634 }
635
636 static NV
637 myNVtime()
638 {
639 #ifdef WIN32
640   dTHX;
641 #endif
642   struct timeval Tp;
643   int status;
644   status = gettimeofday (&Tp, NULL);
645   return status == 0 ? Tp.tv_sec + (Tp.tv_usec / 1000000.) : -1.0;
646 }
647
648 #endif
649
650 MODULE = Time::HiRes            PACKAGE = Time::HiRes
651
652 PROTOTYPES: ENABLE
653
654 BOOT:
655 {
656 #ifdef MY_CXT_KEY
657   MY_CXT_INIT;
658 #endif
659 #ifdef ATLEASTFIVEOHOHFIVE
660 #ifdef HAS_GETTIMEOFDAY
661   {
662     UV auv[2];
663     hv_store(PL_modglobal, "Time::NVtime", 12, newSViv(PTR2IV(myNVtime)), 0);
664     if (myU2time(aTHX_ auv) == 0)
665       hv_store(PL_modglobal, "Time::U2time", 12, newSViv((IV) auv[0]), 0);
666   }
667 #endif
668 #endif
669 }
670
671 #if defined(USE_ITHREADS) && defined(MY_CXT_KEY)
672
673 void
674 CLONE(...)
675     CODE:
676     MY_CXT_CLONE;
677
678 #endif
679
680 INCLUDE: const-xs.inc
681
682 #if defined(HAS_USLEEP) && defined(HAS_GETTIMEOFDAY)
683
684 NV
685 usleep(useconds)
686         NV useconds
687         PREINIT:
688         struct timeval Ta, Tb;
689         CODE:
690         gettimeofday(&Ta, NULL);
691         if (items > 0) {
692             if (useconds > 1E6) {
693                 IV seconds = (IV) (useconds / 1E6);
694                 /* If usleep() has been implemented using setitimer()
695                  * then this contortion is unnecessary-- but usleep()
696                  * may be implemented in some other way, so let's contort. */
697                 if (seconds) {
698                     sleep(seconds);
699                     useconds -= 1E6 * seconds;
700                 }
701             } else if (useconds < 0.0)
702                 croak("Time::HiRes::usleep(%"NVgf"): negative time not invented yet", useconds);
703             usleep((U32)useconds);
704         } else
705             PerlProc_pause();
706         gettimeofday(&Tb, NULL);
707 #if 0
708         printf("[%ld %ld] [%ld %ld]\n", Tb.tv_sec, Tb.tv_usec, Ta.tv_sec, Ta.tv_usec);
709 #endif
710         RETVAL = 1E6*(Tb.tv_sec-Ta.tv_sec)+(NV)((IV)Tb.tv_usec-(IV)Ta.tv_usec);
711
712         OUTPUT:
713         RETVAL
714
715 NV
716 sleep(...)
717         PREINIT:
718         struct timeval Ta, Tb;
719         CODE:
720         gettimeofday(&Ta, NULL);
721         if (items > 0) {
722             NV seconds  = SvNV(ST(0));
723             if (seconds >= 0.0) {
724                  UV useconds = (UV)(1E6 * (seconds - (UV)seconds));
725                  if (seconds >= 1.0)
726                      sleep((U32)seconds);
727                  if ((IV)useconds < 0) {
728 #if defined(__sparc64__) && defined(__GNUC__)
729                    /* Sparc64 gcc 2.95.3 (e.g. on NetBSD) has a bug
730                     * where (0.5 - (UV)(0.5)) will under certain
731                     * circumstances (if the double is cast to UV more
732                     * than once?) evaluate to -0.5, instead of 0.5. */
733                    useconds = -(IV)useconds;
734 #endif
735                    if ((IV)useconds < 0)
736                      croak("Time::HiRes::sleep(%"NVgf"): internal error: useconds < 0 (unsigned %"UVuf" signed %"IVdf")", seconds, useconds, (IV)useconds);
737                  }
738                  usleep(useconds);
739             } else
740                 croak("Time::HiRes::sleep(%"NVgf"): negative time not invented yet", seconds);
741         } else
742             PerlProc_pause();
743         gettimeofday(&Tb, NULL);
744 #if 0
745         printf("[%ld %ld] [%ld %ld]\n", Tb.tv_sec, Tb.tv_usec, Ta.tv_sec, Ta.tv_usec);
746 #endif
747         RETVAL = (NV)(Tb.tv_sec-Ta.tv_sec)+0.000001*(NV)(Tb.tv_usec-Ta.tv_usec);
748
749         OUTPUT:
750         RETVAL
751
752 #endif
753
754 #ifdef HAS_UALARM
755
756 int
757 ualarm(useconds,interval=0)
758         int useconds
759         int interval
760         CODE:
761         if (useconds < 0 || interval < 0)
762             croak("Time::HiRes::ualarm(%d, %d): negative time not invented yet", useconds, interval);
763         RETVAL = ualarm(useconds, interval);
764
765         OUTPUT:
766         RETVAL
767
768 NV
769 alarm(seconds,interval=0)
770         NV seconds
771         NV interval
772         CODE:
773         if (seconds < 0.0 || interval < 0.0)
774             croak("Time::HiRes::alarm(%"NVgf", %"NVgf"): negative time not invented yet", seconds, interval);
775         RETVAL = (NV)ualarm(seconds  * 1000000,
776                             interval * 1000000) / 1E6;
777
778         OUTPUT:
779         RETVAL
780
781 #endif
782
783 #ifdef HAS_GETTIMEOFDAY
784 #    ifdef MACOS_TRADITIONAL    /* fix epoch TZ and use unsigned time_t */
785 void
786 gettimeofday()
787         PREINIT:
788         struct timeval Tp;
789         struct timezone Tz;
790         PPCODE:
791         int status;
792         status = gettimeofday (&Tp, &Tz);
793         Tp.tv_sec += Tz.tz_minuteswest * 60;    /* adjust for TZ */
794
795         if (GIMME == G_ARRAY) {
796              EXTEND(sp, 2);
797              /* Mac OS (Classic) has unsigned time_t */
798              PUSHs(sv_2mortal(newSVuv(Tp.tv_sec)));
799              PUSHs(sv_2mortal(newSViv(Tp.tv_usec)));
800         } else {
801              EXTEND(sp, 1);
802              PUSHs(sv_2mortal(newSVnv(Tp.tv_sec + (Tp.tv_usec / 1000000.0))));
803         }
804
805 NV
806 time()
807         PREINIT:
808         struct timeval Tp;
809         struct timezone Tz;
810         CODE:
811         int status;
812         status = gettimeofday (&Tp, &Tz);
813         Tp.tv_sec += Tz.tz_minuteswest * 60;    /* adjust for TZ */
814         RETVAL = Tp.tv_sec + (Tp.tv_usec / 1000000.0);
815         OUTPUT:
816         RETVAL
817
818 #    else       /* MACOS_TRADITIONAL */
819 void
820 gettimeofday()
821         PREINIT:
822         struct timeval Tp;
823         PPCODE:
824         int status;
825         status = gettimeofday (&Tp, NULL);
826         if (GIMME == G_ARRAY) {
827              EXTEND(sp, 2);
828              PUSHs(sv_2mortal(newSViv(Tp.tv_sec)));
829              PUSHs(sv_2mortal(newSViv(Tp.tv_usec)));
830         } else {
831              EXTEND(sp, 1);
832              PUSHs(sv_2mortal(newSVnv(Tp.tv_sec + (Tp.tv_usec / 1000000.0))));
833         }
834
835 NV
836 time()
837         PREINIT:
838         struct timeval Tp;
839         CODE:
840         int status;
841         status = gettimeofday (&Tp, NULL);
842         RETVAL = Tp.tv_sec + (Tp.tv_usec / 1000000.);
843         OUTPUT:
844         RETVAL
845
846 #    endif      /* MACOS_TRADITIONAL */
847 #endif
848
849 #if defined(HAS_GETITIMER) && defined(HAS_SETITIMER)
850
851 #define TV2NV(tv) ((NV)((tv).tv_sec) + 0.000001 * (NV)((tv).tv_usec))
852
853 void
854 setitimer(which, seconds, interval = 0)
855         int which
856         NV seconds
857         NV interval
858     PREINIT:
859         struct itimerval newit;
860         struct itimerval oldit;
861     PPCODE:
862         if (seconds < 0.0 || interval < 0.0)
863             croak("Time::HiRes::setitimer(%"IVdf", %"NVgf", %"NVgf"): negative time not invented yet", (IV)which, seconds, interval);
864         newit.it_value.tv_sec  = seconds;
865         newit.it_value.tv_usec =
866           (seconds  - (NV)newit.it_value.tv_sec)    * 1000000.0;
867         newit.it_interval.tv_sec  = interval;
868         newit.it_interval.tv_usec =
869           (interval - (NV)newit.it_interval.tv_sec) * 1000000.0;
870         if (setitimer(which, &newit, &oldit) == 0) {
871           EXTEND(sp, 1);
872           PUSHs(sv_2mortal(newSVnv(TV2NV(oldit.it_value))));
873           if (GIMME == G_ARRAY) {
874             EXTEND(sp, 1);
875             PUSHs(sv_2mortal(newSVnv(TV2NV(oldit.it_interval))));
876           }
877         }
878
879 void
880 getitimer(which)
881         int which
882     PREINIT:
883         struct itimerval nowit;
884     PPCODE:
885         if (getitimer(which, &nowit) == 0) {
886           EXTEND(sp, 1);
887           PUSHs(sv_2mortal(newSVnv(TV2NV(nowit.it_value))));
888           if (GIMME == G_ARRAY) {
889             EXTEND(sp, 1);
890             PUSHs(sv_2mortal(newSVnv(TV2NV(nowit.it_interval))));
891           }
892         }
893
894 #endif
895