Rework Time::HiRes not to need HAS_NANOSLEEP from Configure.
[p5sagit/p5-mst-13.2.git] / ext / Time / HiRes / HiRes.xs
index 3bb1aa4..91249f0 100644 (file)
@@ -9,49 +9,106 @@ extern "C" {
 #else
 #include <sys/time.h>
 #endif
+#ifdef HAS_SELECT
+# ifdef I_SYS_SELECT
+#  include <sys/select.h>
+# endif
+#endif
 #ifdef __cplusplus
 }
 #endif
 
-static IV
-constant(char *name, int arg)
-{
-    errno = 0;
-    switch (*name) {
-    case 'I':
-      if (strEQ(name, "ITIMER_REAL"))
-#ifdef ITIMER_REAL
-       return ITIMER_REAL;
-#else
-       goto not_there;
+#ifndef aTHX_
+#    define aTHX_
+#    define pTHX_
+#endif         
+
+#ifndef NVTYPE
+#   if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE)
+#       define NVTYPE long double
+#   else
+#       define NVTYPE double
+#   endif
+typedef NVTYPE NV;
 #endif
-      if (strEQ(name, "ITIMER_REALPROF"))
-#ifdef ITIMER_REALPROF
-       return ITIMER_REALPROF;
-#else
-       goto not_there;
+
+#ifndef IVdf
+#  ifdef IVSIZE
+#      if IVSIZE == LONGSIZE
+#           define     IVdf            "ld"
+#           define     UVuf            "lu"
+#       else
+#           if IVSIZE == INTSIZE
+#               define IVdf    "d"
+#               define UVuf    "u"
+#           endif
+#       endif
+#   else
+#       define IVdf    "ld"
+#       define UVuf    "lu"
+#   endif
 #endif
-      if (strEQ(name, "ITIMER_VIRTUAL"))
-#ifdef ITIMER_VIRTUAL
-       return ITIMER_VIRTUAL;
-#else
-       goto not_there;
+
+#ifndef NVef
+#   if defined(USE_LONG_DOUBLE) && defined(HAS_LONG_DOUBLE) && \
+       defined(PERL_PRIgldbl) /* Not very likely, but let's try anyway. */ 
+#       define NVgf            PERL_PRIgldbl
+#   else
+#       define NVgf            "g"
+#   endif
 #endif
-      if (strEQ(name, "ITIMER_PROF"))
-#ifdef ITIMER_PROF
-       return ITIMER_PROF;
+
+#ifndef INT2PTR
+
+#if (IVSIZE == PTRSIZE) && (UVSIZE == PTRSIZE)
+#  define PTRV                  UV
+#  define INT2PTR(any,d)        (any)(d)
 #else
-       goto not_there;
+#  if PTRSIZE == LONGSIZE
+#    define PTRV                unsigned long
+#  else
+#    define PTRV                unsigned
+#  endif
+#  define INT2PTR(any,d)        (any)(PTRV)(d)
 #endif
-      break;
-    }
-    errno = EINVAL;
-    return 0;
+#define PTR2IV(p)       INT2PTR(IV,p)
 
-not_there:
-    errno = ENOENT;
-    return 0;
+#endif /* !INT2PTR */
+
+#ifndef SvPV_nolen
+static char *
+sv_2pv_nolen(pTHX_ register SV *sv)
+{
+    STRLEN n_a;
+    return sv_2pv(sv, &n_a);
 }
+#   define SvPV_nolen(sv) \
+        ((SvFLAGS(sv) & (SVf_POK)) == SVf_POK \
+         ? SvPVX(sv) : sv_2pv_nolen(sv))
+#endif
+
+#ifndef PerlProc_pause
+#   define PerlProc_pause() Pause()
+#endif
+
+/* Though the cpp define ITIMER_VIRTUAL is available the functionality
+ * is not supported in Cygwin as of August 2002, ditto for Win32.
+ * Neither are ITIMER_PROF or ITIMER_REALPROF implemented.  --jhi
+ */
+#if defined(__CYGWIN__) || defined(WIN32)
+#   undef ITIMER_VIRTUAL
+#   undef ITIMER_PROF
+#   undef ITIMER_REALPROF
+#endif
+
+/* 5.004 doesn't define PL_sv_undef */
+#ifndef ATLEASTFIVEOHOHFIVE
+#ifndef PL_sv_undef
+#define PL_sv_undef sv_undef
+#endif
+#endif
+
+#include "const-c.inc"
 
 #if !defined(HAS_GETTIMEOFDAY) && defined(WIN32)
 #define HAS_GETTIMEOFDAY
@@ -69,7 +126,12 @@ typedef union {
 } FT_t;
 
 /* Number of 100 nanosecond units from 1/1/1601 to 1/1/1970 */
-#define EPOCH_BIAS  116444736000000000i64
+#ifdef __GNUC__
+#define Const64(x) x##LL
+#else
+#define Const64(x) x##i64
+#endif
+#define EPOCH_BIAS  Const64(116444736000000000)
 
 /* NOTE: This does not compute the timezone info (doing so can be expensive,
  * and appears to be unsupported even by glibc) */
@@ -82,10 +144,10 @@ gettimeofday (struct timeval *tp, void *not_used)
     GetSystemTimeAsFileTime(&ft.ft_val);
 
     /* seconds since epoch */
-    tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / 10000000i64);
+    tp->tv_sec = (long)((ft.ft_i64 - EPOCH_BIAS) / Const64(10000000));
 
     /* microseconds remaining */
-    tp->tv_usec = (long)((ft.ft_i64 / 10i64) % 1000000i64);
+    tp->tv_usec = (long)((ft.ft_i64 / Const64(10)) % Const64(1000000));
 
     return 0;
 }
@@ -277,6 +339,25 @@ gettimeofday (struct timeval *tp, void *tpz)
 }
 #endif
 
+
+ /* Do not use H A S _ N A N O S L E E P
+  * so that Perl Configure doesn't scan for it.
+  * The TIME_HIRES_NANOSLEEP is set by Makefile.PL. */
+#if !defined(HAS_USLEEP) && defined(TIME_HIRES_NANOSLEEP)
+#define HAS_USLEEP
+#define usleep hrt_nanosleep  /* could conflict with ncurses for static build */
+
+void
+hrt_nanosleep(unsigned long usec)
+{
+    struct timespec res;
+    res.tv_sec = usec/1000/1000;
+    res.tv_nsec = ( usec - res.tv_sec*1000*1000 ) * 1000;
+    nanosleep(&res, NULL);
+}
+#endif
+
+
 #if !defined(HAS_USLEEP) && defined(HAS_SELECT)
 #ifndef SELECT_IS_BROKEN
 #define HAS_USLEEP
@@ -324,6 +405,203 @@ hrt_ualarm(int usec, int interval)
 }
 #endif
 
+#if !defined(HAS_UALARM) && defined(VMS)
+#define HAS_UALARM
+#define ualarm vms_ualarm 
+
+#include <lib$routines.h>
+#include <ssdef.h>
+#include <starlet.h>
+#include <descrip.h>
+#include <signal.h>
+#include <jpidef.h>
+#include <psldef.h>
+
+#define VMSERR(s)   (!((s)&1))
+
+static void
+us_to_VMS(useconds_t mseconds, unsigned long v[])
+{
+    int iss;
+    unsigned long qq[2];
+
+    qq[0] = mseconds;
+    qq[1] = 0;
+    v[0] = v[1] = 0;
+
+    iss = lib$addx(qq,qq,qq);
+    if (VMSERR(iss)) lib$signal(iss);
+    iss = lib$subx(v,qq,v);
+    if (VMSERR(iss)) lib$signal(iss);
+    iss = lib$addx(qq,qq,qq);
+    if (VMSERR(iss)) lib$signal(iss);
+    iss = lib$subx(v,qq,v);
+    if (VMSERR(iss)) lib$signal(iss);
+    iss = lib$subx(v,qq,v);
+    if (VMSERR(iss)) lib$signal(iss);
+}
+
+static int
+VMS_to_us(unsigned long v[])
+{
+    int iss;
+    unsigned long div=10,quot, rem;
+
+    iss = lib$ediv(&div,v,&quot,&rem);
+    if (VMSERR(iss)) lib$signal(iss);
+
+    return quot;
+}
+
+typedef unsigned short word;
+typedef struct _ualarm {
+    int function;
+    int repeat;
+    unsigned long delay[2];
+    unsigned long interval[2];
+    unsigned long remain[2];
+} Alarm;
+
+
+static int alarm_ef;
+static Alarm *a0, alarm_base;
+#define UAL_NULL   0
+#define UAL_SET    1
+#define UAL_CLEAR  2
+#define UAL_ACTIVE 4
+static void ualarm_AST(Alarm *a);
+
+static int 
+vms_ualarm(int mseconds, int interval)
+{
+    Alarm *a, abase;
+    struct item_list3 {
+        word length;
+        word code;
+        void *bufaddr;
+        void *retlenaddr;
+    } ;
+    static struct item_list3 itmlst[2];
+    static int first = 1;
+    unsigned long asten;
+    int iss, enabled;
+
+    if (first) {
+        first = 0;
+        itmlst[0].code       = JPI$_ASTEN;
+        itmlst[0].length     = sizeof(asten);
+        itmlst[0].retlenaddr = NULL;
+        itmlst[1].code       = 0;
+        itmlst[1].length     = 0;
+        itmlst[1].bufaddr    = NULL;
+        itmlst[1].retlenaddr = NULL;
+
+        iss = lib$get_ef(&alarm_ef);
+        if (VMSERR(iss)) lib$signal(iss);
+
+        a0 = &alarm_base;
+        a0->function = UAL_NULL;
+    }
+    itmlst[0].bufaddr    = &asten;
+    
+    iss = sys$getjpiw(0,0,0,itmlst,0,0,0);
+    if (VMSERR(iss)) lib$signal(iss);
+    if (!(asten&0x08)) return -1;
+
+    a = &abase;
+    if (mseconds) {
+        a->function = UAL_SET;
+    } else {
+        a->function = UAL_CLEAR;
+    }
+
+    us_to_VMS(mseconds, a->delay);
+    if (interval) {
+        us_to_VMS(interval, a->interval);
+        a->repeat = 1;
+    } else 
+        a->repeat = 0;
+
+    iss = sys$clref(alarm_ef);
+    if (VMSERR(iss)) lib$signal(iss);
+
+    iss = sys$dclast(ualarm_AST,a,0);
+    if (VMSERR(iss)) lib$signal(iss);
+
+    iss = sys$waitfr(alarm_ef);
+    if (VMSERR(iss)) lib$signal(iss);
+
+    if (a->function == UAL_ACTIVE) 
+        return VMS_to_us(a->remain);
+    else
+        return 0;
+}
+
+
+
+static void
+ualarm_AST(Alarm *a)
+{
+    int iss;
+    unsigned long now[2];
+
+    iss = sys$gettim(now);
+    if (VMSERR(iss)) lib$signal(iss);
+
+    if (a->function == UAL_SET || a->function == UAL_CLEAR) {
+        if (a0->function == UAL_ACTIVE) {
+            iss = sys$cantim(a0,PSL$C_USER);
+            if (VMSERR(iss)) lib$signal(iss);
+
+            iss = lib$subx(a0->remain, now, a->remain);
+            if (VMSERR(iss)) lib$signal(iss);
+
+            if (a->remain[1] & 0x80000000) 
+                a->remain[0] = a->remain[1] = 0;
+        }
+
+        if (a->function == UAL_SET) {
+            a->function = a0->function;
+            a0->function = UAL_ACTIVE;
+            a0->repeat = a->repeat;
+            if (a0->repeat) {
+                a0->interval[0] = a->interval[0];
+                a0->interval[1] = a->interval[1];
+            }
+            a0->delay[0] = a->delay[0];
+            a0->delay[1] = a->delay[1];
+
+            iss = lib$subx(now, a0->delay, a0->remain);
+            if (VMSERR(iss)) lib$signal(iss);
+
+            iss = sys$setimr(0,a0->delay,ualarm_AST,a0);
+            if (VMSERR(iss)) lib$signal(iss);
+        } else {
+            a->function = a0->function;
+            a0->function = UAL_NULL;
+        }
+        iss = sys$setef(alarm_ef);
+        if (VMSERR(iss)) lib$signal(iss);
+    } else if (a->function == UAL_ACTIVE) {
+        if (a->repeat) {
+            iss = lib$subx(now, a->interval, a->remain);
+            if (VMSERR(iss)) lib$signal(iss);
+
+            iss = sys$setimr(0,a->interval,ualarm_AST,a);
+            if (VMSERR(iss)) lib$signal(iss);
+        } else {
+            a->function = UAL_NULL;
+        }
+        iss = sys$wake(0,0);
+        if (VMSERR(iss)) lib$signal(iss);
+        lib$signal(SS$_ASTFLT);
+    } else {
+        lib$signal(SS$_BADPARAM);
+    }
+}
+
+#endif /* !HAS_UALARM && VMS */
+
 #ifdef HAS_GETTIMEOFDAY
 
 static int
@@ -353,19 +631,18 @@ MODULE = Time::HiRes            PACKAGE = Time::HiRes
 PROTOTYPES: ENABLE
 
 BOOT:
+#ifdef ATLEASTFIVEOHOHFIVE
 #ifdef HAS_GETTIMEOFDAY
 {
   UV auv[2];
-  hv_store(PL_modglobal, "Time::NVtime", 12, newSViv((IV) myNVtime()), 0);
+  hv_store(PL_modglobal, "Time::NVtime", 12, newSViv(PTR2IV(myNVtime)), 0);
   if (myU2time(auv) == 0)
     hv_store(PL_modglobal, "Time::U2time", 12, newSViv((IV) auv[0]), 0);
 }
 #endif
+#endif
 
-IV
-constant(name, arg)
-       char *          name
-       int             arg
+INCLUDE: const-xs.inc
 
 #if defined(HAS_USLEEP) && defined(HAS_GETTIMEOFDAY)
 
@@ -379,10 +656,16 @@ usleep(useconds)
        if (items > 0) {
            if (useconds > 1E6) {
                IV seconds = (IV) (useconds / 1E6);
-               sleep(seconds);
-               useconds -= 1E6 * seconds;
-           }
-           usleep((UV)useconds);
+               /* If usleep() has been implemented using setitimer()
+                * then this contortion is unnecessary-- but usleep()
+                * may be implemented in some other way, so let's contort. */
+               if (seconds) {
+                   sleep(seconds);
+                   useconds -= 1E6 * seconds;
+               }
+           } else if (useconds < 0.0)
+               croak("Time::HiRes::usleep(%"NVgf"): negative time not invented yet", useconds);
+           usleep((U32)useconds);
        } else
            PerlProc_pause();
        gettimeofday(&Tb, NULL);
@@ -402,9 +685,24 @@ sleep(...)
        gettimeofday(&Ta, NULL);
        if (items > 0) {
            NV seconds  = SvNV(ST(0));
-           IV useconds = 1E6 * (seconds - (IV)seconds);
-           sleep(seconds);
-           usleep(useconds);
+           if (seconds >= 0.0) {
+                UV useconds = (UV)(1E6 * (seconds - (UV)seconds));
+                if (seconds >= 1.0)
+                    sleep((U32)seconds);
+                if ((IV)useconds < 0) {
+#if defined(__sparc64__) && defined(__GNUC__)
+                  /* Sparc64 gcc 2.95.3 (e.g. on NetBSD) has a bug
+                   * where (0.5 - (UV)(0.5)) will under certain
+                   * circumstances (if the double is cast to UV more
+                   * than once?) evaluate to -0.5, instead of 0.5. */
+                  useconds = -(IV)useconds;
+#endif
+                  if ((IV)useconds < 0)
+                    croak("Time::HiRes::sleep(%"NVgf"): internal error: useconds < 0 (unsigned %"UVuf" signed %"IVdf")", seconds, useconds, (IV)useconds);
+                }
+                usleep(useconds);
+           } else
+               croak("Time::HiRes::sleep(%"NVgf"): negative time not invented yet", seconds);
        } else
            PerlProc_pause();
        gettimeofday(&Tb, NULL);
@@ -424,17 +722,23 @@ int
 ualarm(useconds,interval=0)
        int useconds
        int interval
+       CODE:
+       if (useconds < 0 || interval < 0)
+           croak("Time::HiRes::ualarm(%d, %d): negative time not invented yet", useconds, interval);
+       RETVAL = ualarm(useconds, interval);
 
-int
-alarm(fseconds,finterval=0)
-       NV fseconds
-       NV finterval
-       PREINIT:
-       int useconds, uinterval;
+       OUTPUT:
+       RETVAL
+
+NV
+alarm(seconds,interval=0)
+       NV seconds
+       NV interval
        CODE:
-       useconds = fseconds * 1000000;
-       uinterval = finterval * 1000000;
-       RETVAL = ualarm (useconds, uinterval);
+       if (seconds < 0.0 || interval < 0.0)
+           croak("Time::HiRes::alarm(%"NVgf", %"NVgf"): negative time not invented yet", seconds, interval);
+       RETVAL = (NV)ualarm(seconds  * 1000000,
+                           interval * 1000000) / 1E6;
 
        OUTPUT:
        RETVAL
@@ -442,7 +746,41 @@ alarm(fseconds,finterval=0)
 #endif
 
 #ifdef HAS_GETTIMEOFDAY
+#    ifdef MACOS_TRADITIONAL   /* fix epoch TZ and use unsigned time_t */
+void
+gettimeofday()
+        PREINIT:
+        struct timeval Tp;
+        struct timezone Tz;
+        PPCODE:
+        int status;
+        status = gettimeofday (&Tp, &Tz);
+        Tp.tv_sec += Tz.tz_minuteswest * 60;   /* adjust for TZ */
+
+        if (GIMME == G_ARRAY) {
+             EXTEND(sp, 2);
+             /* Mac OS (Classic) has unsigned time_t */
+             PUSHs(sv_2mortal(newSVuv(Tp.tv_sec)));
+             PUSHs(sv_2mortal(newSViv(Tp.tv_usec)));
+        } else {
+             EXTEND(sp, 1);
+             PUSHs(sv_2mortal(newSVnv(Tp.tv_sec + (Tp.tv_usec / 1000000.0))));
+        }
+
+NV
+time()
+        PREINIT:
+        struct timeval Tp;
+        struct timezone Tz;
+        CODE:
+        int status;
+        status = gettimeofday (&Tp, &Tz);
+        Tp.tv_sec += Tz.tz_minuteswest * 60;   /* adjust for TZ */
+        RETVAL = Tp.tv_sec + (Tp.tv_usec / 1000000.0);
+       OUTPUT:
+       RETVAL
 
+#    else      /* MACOS_TRADITIONAL */
 void
 gettimeofday()
         PREINIT:
@@ -470,6 +808,7 @@ time()
        OUTPUT:
        RETVAL
 
+#    endif     /* MACOS_TRADITIONAL */
 #endif
 
 #if defined(HAS_GETITIMER) && defined(HAS_SETITIMER)
@@ -485,6 +824,8 @@ setitimer(which, seconds, interval = 0)
        struct itimerval newit;
        struct itimerval oldit;
     PPCODE:
+       if (seconds < 0.0 || interval < 0.0)
+           croak("Time::HiRes::setitimer(%"IVdf", %"NVgf", %"NVgf"): negative time not invented yet", (IV)which, seconds, interval);
        newit.it_value.tv_sec  = seconds;
        newit.it_value.tv_usec =
          (seconds  - (NV)newit.it_value.tv_sec)    * 1000000.0;
@@ -517,41 +858,3 @@ getitimer(which)
 
 #endif
 
-# $Id: HiRes.xs,v 1.11 1999/03/16 02:27:38 wegscd Exp wegscd $
-
-# $Log: HiRes.xs,v $
-# Revision 1.11  1999/03/16 02:27:38  wegscd
-# Add U2time, NVtime. Fix symbols for static link.
-#
-# Revision 1.10  1998/09/30 02:36:25  wegscd
-# Add VMS changes.
-#
-# Revision 1.9  1998/07/07 02:42:06  wegscd
-# Win32 usleep()
-#
-# Revision 1.8  1998/07/02 01:47:26  wegscd
-# Add Win32 code for gettimeofday.
-#
-# Revision 1.7  1997/11/13 02:08:12  wegscd
-# Add missing EXTEND in gettimeofday() scalar code.
-#
-# Revision 1.6  1997/11/11 02:32:35  wegscd
-# Do something useful when calling gettimeofday() in a scalar context.
-# The patch is courtesy of Gisle Aas.
-#
-# Revision 1.5  1997/11/06 03:10:47  wegscd
-# Fake ualarm() if we have setitimer.
-#
-# Revision 1.4  1997/11/05 05:41:23  wegscd
-# Turn prototypes ON (suggested by Gisle Aas)
-#
-# Revision 1.3  1997/10/13 20:56:15  wegscd
-# Add PROTOTYPES: DISABLE
-#
-# Revision 1.2  1997/05/23 01:01:38  wegscd
-# Conditional compilation, depending on what the OS gives us.
-#
-# Revision 1.1  1996/09/03 18:26:35  wegscd
-# Initial revision
-#
-#