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