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