From 636b1637b40dbd56487822c54145693cd16c6d41 Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Tue, 13 Jun 2017 12:25:20 -0600 Subject: [PATCH] rktio: add date & time --- racket/src/rktio/Makefile.in | 4 + racket/src/rktio/demo.c | 47 ++- racket/src/rktio/rktio.h | 26 +- racket/src/rktio/rktio_config.h.in | 2 +- racket/src/rktio/rktio_error.c | 6 +- racket/src/rktio/rktio_fd.c | 2 +- racket/src/rktio/rktio_flock.c | 10 +- racket/src/rktio/rktio_fs.c | 4 - racket/src/rktio/rktio_network.c | 21 -- racket/src/rktio/rktio_platform.h | 373 ++++++++++++++++++++++ racket/src/rktio/rktio_private.h | 25 +- racket/src/rktio/rktio_process.c | 6 +- racket/src/rktio/rktio_time.c | 484 +++++++++++++++++++++++++++++ 13 files changed, 965 insertions(+), 45 deletions(-) create mode 100644 racket/src/rktio/rktio_platform.h create mode 100644 racket/src/rktio/rktio_time.c diff --git a/racket/src/rktio/Makefile.in b/racket/src/rktio/Makefile.in index a3ce9e2403..c6c4bef643 100644 --- a/racket/src/rktio/Makefile.in +++ b/racket/src/rktio/Makefile.in @@ -24,6 +24,7 @@ OBJS = rktio_fs.@LTO@ \ rktio_envvars.@LTO@ \ rktio_fs_change.@LTO@ \ rktio_flock.@LTO@ \ + rktio_time.@LTO@ \ rktio_error.@LTO@ \ rktio_hash.@LTO@ \ rktio_wide.@LTO@ \ @@ -72,6 +73,9 @@ rktio_fs_change.@LTO@: $(srcdir)/rktio_fs_change.c $(RKTIO_HEADERS) rktio_flock.@LTO@: $(srcdir)/rktio_flock.c $(RKTIO_HEADERS) $(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_flock.@LTO@ -c $(srcdir)/rktio_flock.c +rktio_time.@LTO@: $(srcdir)/rktio_time.c $(RKTIO_HEADERS) + $(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_time.@LTO@ -c $(srcdir)/rktio_time.c + rktio_error.@LTO@: $(srcdir)/rktio_error.c $(RKTIO_HEADERS) $(CC) $(CFLAGS) -I$(srcdir) -I. -o rktio_error.@LTO@ -c $(srcdir)/rktio_error.c diff --git a/racket/src/rktio/demo.c b/racket/src/rktio/demo.c index baa800f6db..2e3937ffad 100644 --- a/racket/src/rktio/demo.c +++ b/racket/src/rktio/demo.c @@ -435,6 +435,20 @@ static rktio_fd_t *connect_loop(rktio_t *rktio, rktio_addrinfo_t *addr, rktio_ad return fd; } +static char *month_name(rktio_t *rktio, int month) +{ + static char *months[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "NOV", "DEC"}; + check_valid((month >= 1) && (month <= 12)); + return months[month-1]; +} + +static char *week_day_name(rktio_t *rktio, int dow) +{ + static char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + check_valid((dow >= 0) && (dow <= 6)); + return days[dow-1]; +} + int main(int argc, char **argv) { rktio_t *rktio; @@ -897,6 +911,7 @@ int main(int argc, char **argv) char *path = "test1"; rktio_fs_change_t *fc; rktio_poll_set_t *ps; + double start; if (verbose) printf("fs change\n"); @@ -910,9 +925,10 @@ int main(int argc, char **argv) check_valid(ps); rktio_poll_add_fs_change(rktio, fc, ps); + start = rktio_get_inexact_milliseconds(); rktio_sleep(rktio, 0.1, ps, NULL); rktio_poll_set_close(rktio, ps); - /* FIXME: check that at least 0.1 seconds have passed */ + check_valid(rktio_get_inexact_milliseconds() - start > 0.1); ps = rktio_make_poll_set(rktio); check_valid(ps); @@ -1005,6 +1021,35 @@ int main(int argc, char **argv) rktio_close(rktio, fd); } + if (verbose) + printf("time and date\n"); + + { + intptr_t now; + rktio_date_t *today; + int gmt; + + now = rktio_get_seconds(rktio); + + for (gmt = 0; gmt <= 1; gmt++) { + today = rktio_seconds_to_date(rktio, now, 5000, gmt); + check_valid(today); + + printf("%2.2d:%2.2d:%2.2d %s(%+d/%+d%s) on %s %d-%s-%ld [day %d of year]\n", + today->hour, today->minute, today->second, + today->zone_name ? today->zone_name : "", + today->zone_offset, today->zone_offset / (60 * 60), + (today->is_dst ? ";DST" : ""), + week_day_name(rktio, today->day_of_week), + today->day, month_name(rktio, today->month), today->year, + today->day_of_year); + + if (today->zone_name) + free(today->zone_name); + free(today); + } + } + if (verbose) printf("done\n"); diff --git a/racket/src/rktio/rktio.h b/racket/src/rktio/rktio.h index 0bf2dcdbb7..14bd335dd5 100644 --- a/racket/src/rktio/rktio.h +++ b/racket/src/rktio/rktio.h @@ -4,8 +4,7 @@ #include "rktio_config.h" /* A rktio_t value represents an instance of the Racket I/O system. - Every rktio_...() function takes it as the first argument, except - for rktio_init(), rktio_signal_received_at(), and rktio_free(). */ + Almost every rktio_...() function takes it as the first argument. */ typedef struct rktio_t rktio_t; rktio_t *rktio_init(void); @@ -386,6 +385,28 @@ rktio_signal_handle_t *rktio_get_signal_handle(rktio_t *rktio); void rktio_signal_received_at(rktio_signal_handle_t *h); void rktio_signal_received(rktio_t *rktio); +/*************************************************/ +/* Time and date */ + +typedef struct rktio_date_t { + int nanosecond, second, minute, hour, day, month; + intptr_t year; + int day_of_week; + int day_of_year; + int is_dst; + int zone_offset; + char *zone_name; /* can be NULL */ +} rktio_date_t; + +intptr_t rktio_get_milliseconds(void); +double rktio_get_inexact_milliseconds(void); + +intptr_t rktio_get_process_milliseconds(rktio_t *rktio); +intptr_t scheme_get_process_children_milliseconds(rktio_t *rktio); + +intptr_t rktio_get_seconds(rktio_t *rktio); +rktio_date_t *rktio_seconds_to_date(rktio_t *rktio, intptr_t seconds, intptr_t nanoseconds, int get_gmt); + /*************************************************/ /* Errors */ @@ -418,6 +439,7 @@ enum { RKTIO_ERROR_INFO_TRY_AGAIN, /* for UDP */ RKTIO_ERROR_TRY_AGAIN, /* for UDP */ RKTIO_ERROR_TRY_AGAIN_WITH_IPV4, /* for TCP listen */ + RKTIO_ERROR_TIME_OUT_OF_RANGE, }; /* GAI error sub-codes */ diff --git a/racket/src/rktio/rktio_config.h.in b/racket/src/rktio/rktio_config.h.in index 44283a24ff..7bd90865e7 100644 --- a/racket/src/rktio/rktio_config.h.in +++ b/racket/src/rktio/rktio_config.h.in @@ -37,4 +37,4 @@ typedef unsigned long uintptr_t; #undef RKTIO_STATIC_FDSET_SIZE /* In case you want to use fcntl for file locks */ -#undef USE_FCNTL_AND_FORK_FOR_FILE_LOCKS +#undef RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS diff --git a/racket/src/rktio/rktio_error.c b/racket/src/rktio/rktio_error.c index 79c73de575..058774e70f 100644 --- a/racket/src/rktio/rktio_error.c +++ b/racket/src/rktio/rktio_error.c @@ -45,9 +45,11 @@ int rktio_get_last_error_kind(rktio_t *rktio) const char *rktio_get_error_string(rktio_t *rktio, int kind, int errid) { const char *s = NULL; - if (kind == RKTIO_ERROR_KIND_POSIX) + if (kind == RKTIO_ERROR_KIND_POSIX) { +#ifndef NO_STRERROR_AVAILABLE s = strerror(errid); - else if (kind == RKTIO_ERROR_KIND_GAI) +#endif + } else if (kind == RKTIO_ERROR_KIND_GAI) s = rktio_gai_strerror(errid); if (s) return s; return "???"; diff --git a/racket/src/rktio/rktio_fd.c b/racket/src/rktio/rktio_fd.c index d26b1c48b5..9ecae59ef5 100644 --- a/racket/src/rktio/rktio_fd.c +++ b/racket/src/rktio/rktio_fd.c @@ -308,7 +308,7 @@ void rktio_reliably_close(intptr_t s) { int rktio_close(rktio_t *rktio, rktio_fd_t *rfd) { #ifdef RKTIO_SYSTEM_UNIX -# ifdef USE_FCNTL_AND_FORK_FOR_FILE_LOCKS +# ifdef RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS if (!(rfd->modes & RKTIO_OPEN_SOCKET)) rktio_release_lockf(rktio, rfd->fd); # endif diff --git a/racket/src/rktio/rktio_flock.c b/racket/src/rktio/rktio_flock.c index c8040e2a6b..5bd7253e56 100644 --- a/racket/src/rktio/rktio_flock.c +++ b/racket/src/rktio/rktio_flock.c @@ -4,7 +4,7 @@ #include #include -#if defined(RKTIO_SYSTEM_UNIX) && !defined(USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) +#if defined(RKTIO_SYSTEM_UNIX) && !defined(RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) # define USE_FLOCK_FOR_FILE_LOCKS #endif @@ -12,7 +12,7 @@ #include #endif -#if defined(USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) +#if defined(RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) #include #include typedef struct pair_t { int car, cdr; } pair_t; @@ -39,7 +39,7 @@ int rktio_file_lock_try(rktio_t *rktio, rktio_fd_t *rfd, int excl) get_posix_error(); return RKTIO_LOCK_ERROR; } -# elif defined(USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) +# elif defined(RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) /* An lockf() is cancelled if *any* file descriptor to the same file is closed within the same process. We avoid that problem by forking a new process whose only job is to use lockf(). */ @@ -194,7 +194,7 @@ int rktio_file_lock_try(rktio_t *rktio, rktio_fd_t *rfd, int excl) #endif } -#ifdef USE_FCNTL_AND_FORK_FOR_FILE_LOCKS +#ifdef RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS void rktio_release_lockf(rktio_t *rktio, int fd) { if (rktio->locked_fd_process_map) { @@ -227,7 +227,7 @@ int rktio_file_unlock(rktio_t *rktio, rktio_fd_t *rfd) } while ((ok == -1) && (errno == EINTR)); ok = !ok; if (!ok) get_posix_error(); -# elif defined(USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) +# elif defined(RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS) rktio_release_lockf(rktio, fd); ok = 1; # else diff --git a/racket/src/rktio/rktio_fs.c b/racket/src/rktio/rktio_fs.c index 34065ccd67..9f601de26b 100644 --- a/racket/src/rktio/rktio_fs.c +++ b/racket/src/rktio/rktio_fs.c @@ -33,10 +33,6 @@ # define BIG_OFF_T_IZE(n) n #endif -#if defined(__linux__) -# define DIRENT_NO_NAMLEN -#endif - #if defined(RKTIO_SYSTEM_UNIX) && !defined(NO_UNIX_USERS) static int have_user_ids = 0; static uid_t uid; diff --git a/racket/src/rktio/rktio_network.c b/racket/src/rktio/rktio_network.c index d124411a1f..4f4b8187dc 100644 --- a/racket/src/rktio/rktio_network.c +++ b/racket/src/rktio/rktio_network.c @@ -36,27 +36,6 @@ typedef struct sockaddr_in rktio_unspec_address; #define REGISTER_SOCKET(s) /**/ #define UNREGISTER_SOCKET(s) /**/ -# if defined(__linux__) || defined(OS_X) -/* RKTIO_TCP_LISTEN_IPV6_ONLY_SOCKOPT uses IPV6_V6ONLY for IPv6 - listeners when the same listener has an IPv4 address, which means - that the IpV6 listener accepts only IPv6 connections. This is used - with Linux, for example, because a port cannot have both an IPv4 - and IPv6 listener if the IPv6 one doesn't use IPV6_V6ONLY. (The - two listeners might be for different interfaces, in which case - IPV6_V6ONLY is not necessary, but we must err on the side of being - too restrictive. If IPV6_V6ONLY is not #defined or if setting the - option doesn't work, then the IPv6 addresses are silently ignored - when creating the listener (but only where there is at least once - IPv4 address). */ -# define RKTIO_TCP_LISTEN_IPV6_ONLY_SOCKOPT -# endif - -#if defined(sun) -/* USE_NULL_TO_DISCONNECT_UDP calls connect() with NULL instead of - an AF_UNSPEC address to disconnect a UDP socket. */ -# define USE_NULL_TO_DISCONNECT_UDP -#endif - #endif #ifdef CANT_SET_SOCKET_BUFSIZE diff --git a/racket/src/rktio/rktio_platform.h b/racket/src/rktio/rktio_platform.h new file mode 100644 index 0000000000..3a4855b796 --- /dev/null +++ b/racket/src/rktio/rktio_platform.h @@ -0,0 +1,373 @@ + /************** SunOS/Solaris ****************/ + +#if defined(sun) + +# include "uconfig.h" + +# define USE_EXPLICT_FP_FORM_CHECK + +# include +# ifdef ECHRNG +/* Solaris */ +# define DIRENT_NO_NAMLEN +# define NO_USLEEP +# define USE_ULIMIT +# define SOME_FDS_ARE_NOT_SELECTABLE +# define RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS +# define USE_TIMEZONE_AND_ALTZONE_VAR +# define USE_TZNAME_VAR +# define USE_NULL_TO_DISCONNECT_UDP +# else +/* SunOS4 */ +# define USE_TM_GMTOFF_FIELD +# define USE_TM_ZONE_FIELD +# define NO_STRERROR_AVAILABLE +# define USE_FNDELAY_O_NONBLOCK +# endif + +# ifdef _POSIX_PTHREAD_SEMANTICS +# define SUBPROCESS_USE_FORK1 +# endif + +#endif + + /************** RS6000/AIX ****************/ + +# if defined(_IBMR2) + +# define SELECT_INCLUDE + +# define USE_TIMEZONE_VAR_W_DLS +# define USE_TZNAME_VAR + +#endif + + /************** Linux ****************/ + +#if defined(__linux__) + +# define DIRENT_NO_NAMLEN + +# define SIGSET_NEEDS_REINSTALL + +# define USE_TIMEZONE_VAR_W_DLS +# define USE_TZNAME_VAR + +# define RKTIO_TCP_LISTEN_IPV6_ONLY_SOCKOPT + +# ifdef __ANDROID__ +# define PROTOENT_IS_INT IPPROTO_TCP +# endif + +#endif + + /********************* NetBSD ***********************/ + +#if defined(__NetBSD__) + +# define USE_TM_GMTOFF_FIELD +# define USE_TM_ZONE_FIELD + +#endif + + /************** OpenBSD ****************/ + /* Thanks to Bengt Kleberg */ + +#if defined(__OpenBSD__) + +# define USE_TM_GMTOFF_FIELD +# define USE_TM_ZONE_FIELD + +#endif + + /************** FreeBSD ****************/ + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + +# define USE_TM_GMTOFF_FIELD +# define USE_TM_ZONE_FIELD +# define MAX_VALID_DATE_SECONDS_BITS 51 + +#endif + + /************** SGI/IRIX ****************/ + +#if (defined(mips) || defined(__mips)) \ + && !(defined(ultrix) || defined(__ultrix) || defined(__linux__) || defined(__OpenBSD__)) + +# define DIRENT_NO_NAMLEN + +# define BSTRING_INCLUDE + +# define NO_USLEEP + +# define USE_TIMEZONE_AND_ALTZONE_VAR +# define USE_TZNAME_VAR + +#endif + + /************** Ultrix ****************/ + +#if defined(ultrix) || defined(__ultrix) + +# define DIRENT_NO_NAMLEN + +# define NO_USLEEP + +#endif + + /************** ALPHA/OSF1 ****************/ + +# if (defined(__alpha) || defined(__alpha__)) \ + && !defined(__linux__) && !defined(__NetBSD__) && !defined(__OpenBSD__) + +# define USE_FNDELAY_O_NONBLOCK + +#endif + + /************** HP/UX with cc or gcc ****************/ + +#if defined(__hpux) + +# define SOME_FDS_ARE_NOT_SELECTABLE + +# define USE_SYSCALL_GETRUSAGE + +# define USE_ULIMIT + +# define USE_TIMEZONE_VAR_W_DLS +# define USE_TZNAME_VAR + +#endif + + /************** x86/SCO Unix with gcc ****************/ + /* Contributed by Atanas Ivanov */ + +#if defined(_M_XENIX) && defined(_M_SYSV) + +# define DIRENT_NO_NAMLEN + +#endif + + /****************** Windows with MSVC or MinGW *****************/ + +#if (defined(__BORLANDC__) \ + || ((defined(_MSC_VER) || defined(__MINGW32__)) \ + && (defined(__WIN32__) || defined(WIN32) || defined(_WIN32)))) + +# if defined(_MSC_VER) || defined(__MINGW32__) +# define NO_READDIR +# define USE_FINDFIRST +# define MKDIR_NO_MODE_FLAG +# endif +# if defined(__BORLANDC__) +# define DIRENT_NO_NAMLEN +# define MKDIR_NO_MODE_FLAG +# endif + +#endif + + /******************** Windows with Cygwin ******************/ + +#if defined(__CYGWIN32__) + +# define RKTIO_BINARY O_BINARY + +# define DIRENT_NO_NAMLEN + +# define SIGCHILD_DOESNT_INTERRUPT_SELECT +# define SIGSET_NEEDS_REINSTALL + +# define CANT_SET_SOCKET_BUFSIZE +# define NO_NEED_FOR_BEGINTHREAD +# define USE_CREATE_PIPE + +# define USE_PLAIN_TIME +# define USE_TOD_FOR_TIMEZONE + +#endif + + /************** MacOS and Darwin ****************/ + +#if defined(__APPLE__) && defined(__MACH__) + +# define RKTIO_TCP_LISTEN_IPV6_ONLY_SOCKOPT + +# define USE_TM_GMTOFF_FIELD +# define USE_TM_ZONE_FIELD + +# define UDP_DISCONNECT_EADRNOTAVAIL_OK + +# endif + + /************ QNX *************/ + +#if defined(__QNX__) + +# define SIGSET_NEEDS_REINSTALL + +# define BROKEN_READLINK_NUL_TERMINATOR + +#endif + + /************ Dragonfly *************/ + +#if defined(__DragonFly__) + +# define USE_TM_GMTOFF_FIELD +# define USE_TM_ZONE_FIELD +# define MAX_VALID_DATE_SECONDS_BITS 51 + +#endif + + /***************************************************/ + +/***** CONFIGURATION FLAG DESCRPTIONS ******/ + + /*********************/ + /* Date and time */ +/*********************/ + + /* DONT_USE_GETRUSAGE uses clock() for timing info. */ + + /* USE_SYSCALL_GETRUSAGE uses syscall() to implement getrusage() for + timing info. Used with USE_GETRUSAGE. */ + + /* CLOCKS_PER_SEC relates the values returned by clock() to + real seconds. (The difference between two clock() calls is + divided by this number.) Usually, this is defined in ; + it defaults to 1000000 */ + + /* USE_PLAIN_TIME uses time. */ + + /* CLOCK_IS_USER_TIME uses the system time for user milliseconds. */ + + /* USER_TIME_IS_CLOCK uses the user time for system milliseconds. */ + + /* MAX_VALID_DATE_SECONDS_BITS sets a maximum number of bits for + seconds to pass to localtime() ro gmtime(). */ + + /* MIN_VALID_DATE_SECONDS sets a minimum vald time in seconds. */ + + /* USE_TIMEZONE_VAR gets timezone offset from a timezone global. + USE_TOD_FOR_TIMEZONE gets timezone offset via gettimeofday. + USE_TIMEZONE_VAR_W_DLS is similar, but adds 1 hour when daylight + savings is in effect. + USE_TIMEZONE_AND_ALTZONE_VAR is similar, but uses altzone when + daylight savings is in effect. + USE_TM_GMTOFF_FIELD gets timezone offset from the tm_gmtoff field + of the tm struct. */ + + /* USE_TZNAME_VAR gets the timezone name from a tzname global. + USE_TM_ZONE_FIELD gets the timezone name from a tm_zone field + of the tm struct. */ + + /*******************/ + /* Filesystem */ +/*******************/ + + /* NO_STAT_PROC means that there is no stat() function. */ + + /* NO_MKDIR means that there is no mkdir() function. */ + + /* BROKEN_READLINK_NUL_TERMINATOR means that readlink() may + report a length that includes trailing NUL terminators, + which should be stripped away. */ + + /* USE_GETDISK uses getdisk() and setdisk() to implement the + filesystem-root-list primitive under DOS. */ + + /* NO_READDIR means that there is no opendir() and readdir() for + implementing directory-list. */ + + /* DIRENT_NO_NAMLEN specifies that dirent entries do not have a + d_namlen field; this is used only when NO_READDIR is not + specified. */ + + /* MKDIR_NO_MODE_FLAG specifies that mkdir() takes only one argument, + instead of a directory name and mode flags. */ + + /***********************/ + /* File descriptors */ +/***********************/ + + /* RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS means that fnctl() and fork() + should be used to implement file locking instead of flock(). */ + + /* SUBPROCESS_USE_FORK1 uses fork1() instead of fork(). */ + + /* USE_FNDELAY_O_NONBLOCK uses FNDELAY instead of O_NONBLOCK for + fcntl on Unix TCP sockets. */ + + /* SOME_FDS_ARE_NOT_SELECTABLE indicates that select() doesn't work + for reading on all kinds of file descriptors. Such FDs must never + be able to go from no-char-ready to char-ready while Racket is + sleeping. */ + + /* USE_TRANSITIONAL_64_FILE_OPS uses fseeko64, lseek64, stat64, etc. + for file operations involving sizes (that can require 64-bit + arithmetic). The `configure` script normally finds this one. */ + + /* USE_ULIMIT uses ulimit instead of getdtablesize. */ + + /* CANT_SET_SOCKET_BUFSIZE turns off setting the buffer size for + Unix TCP sockets. */ + + /* USE_NULL_TO_DISCONNECT_UDP calls connect() with NULL instead of + an AF_UNSPEC address to disconnect a UDP socket. */ + + /* UDP_DISCONNECT_EADRNOTAVAIL_OK means that a disconnecting call + to connect() might return EADDRNOTAVAIL instead of + EAFNOSUPPORT. */ + + /* MZ_BINARY is combined with other flags in all calls to open(); + it can be defined to O_BINARY in Cygwin, for example. */ + + /* MZ_TCP_LISTEN_IPV6_ONLY_SOCKOPT uses IPV6_V6ONLY for IPv6 + listeners when the same listener has an IPv4 address, which means + that the IpV6 listener accepts only IPv6 connections. This is used + with Linux, for example, because a port cannot have both an IPv4 + and IPv6 listener if the IPv6 one doesn't use IPV6_V6ONLY. (The + two listeners might be for different interfaces, in which case + IPV6_V6ONLY is not necessary, but we must err on the side of being + too restrictive. If IPV6_V6ONLY is not #defined or if setting the + option doesn't work, then the IPv6 addresses are silently ignored + when creating the listener (but only where there is at least once + IPv4 address). */ + + /***********************/ + /* Signals */ +/***********************/ + + /* SIGSET_NEEDS_REINSTALL reinstalls a signal handler when it + is called to handle a signal. The expected semantics of sigset() + (when this flags is not defined) is that a signal handler is NOT + reset to SIG_DFL after a handler is called to handle a signal. */ + + /* USE_CREATE_PIPE uses CreatePipe() instead of _pipe() for Windows. */ + + /* SIGCHILD_DOESNT_INTERRUPT_SELECT indicates that the SIGCHILD + signal, sent when a child OS process dies, does not interrupt + select(). This flag is needed for Cygwin B20. */ + + /***********************/ + /* Miscellaneous */ +/***********************/ + + /* DIR_INCLUDE if there's a file (mainly for Windows). */ + + /* DIRECT_INCLUDE if there's a file (mainly for Windows). */ + + /* IO_INCLUDE if there's a file (mainly for Windows). */ + + /* SELECT_INCLUDE if there's a file (mainly for Unix). */ + + /* BSTRING_INCLUDE if there's a file (mainly for Unix). */ + + /* NO_SLEEP means that there is no sleep() function. Used only in + standalone Racket. */ + + /* NO_USLEEP means that there is no usleep() function. Used only in + standalone Racket. Used only if NO_SLEEP is undefined. */ + + /* NO_STRERROR_AVAILABLE means that strerror() is not available. */ diff --git a/racket/src/rktio/rktio_private.h b/racket/src/rktio/rktio_private.h index 291a8818c0..4e7332a9f5 100644 --- a/racket/src/rktio/rktio_private.h +++ b/racket/src/rktio/rktio_private.h @@ -2,6 +2,8 @@ # define OS_X #endif +#include "rktio_platform.h" + #ifdef RKTIO_SYSTEM_WINDOWS # include # include @@ -84,7 +86,7 @@ struct rktio_t { wchar_t *wide_buffer; #endif -#ifdef USE_FCNTL_AND_FORK_FOR_FILE_LOCKS +#ifdef RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS struct rktio_hash_t *locked_fd_process_map; #endif }; @@ -250,10 +252,10 @@ void rktio_set_windows_error(rktio_t *rktio, int errid); # define set_windows_error(errid) rktio_set_windows_error(rktio, errid) #endif -#if defined(USE_FCNTL_O_NONBLOCK) -# define RKTIO_NONBLOCKING O_NONBLOCK -#else +#if defined(USE_FNDELAY_O_NONBLOCK) # define RKTIO_NONBLOCKING FNDELAY +#else +# define RKTIO_NONBLOCKING O_NONBLOCK #endif #ifndef RKTIO_BINARY @@ -277,6 +279,19 @@ void rktio_winsock_done(rktio_t *rktio); #endif void rktio_init_wide(rktio_t *rktio); -#ifdef USE_FCNTL_AND_FORK_FOR_FILE_LOCKS +#ifdef RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS void rktio_release_lockf(rktio_t *rktio, int fd); #endif + +#ifdef RKTIO_SYSTEM_WINDOWS +# ifdef _MSC_VER +typedef _int64 rktio_int64_t; +typedef unsigned _int64 rktio_uint64_t; +# else +typedef __int64 rktio_int64_t; +typedef unsigned __int64 rktio_uint64_t; +# endif +#else +typedef long long rktio_int64_t; +typedef unsigned long long rktio_uint64_t; +#endif diff --git a/racket/src/rktio/rktio_process.c b/racket/src/rktio/rktio_process.c index a34b4aee98..44c798118d 100644 --- a/racket/src/rktio/rktio_process.c +++ b/racket/src/rktio/rktio_process.c @@ -752,10 +752,10 @@ static void collect_process_time(rktio_t *rktio, DWORD w, rktio_process_t *sp) if ((w != STILL_ACTIVE) && !sp->got_time) { FILETIME cr, ex, kr, us; if (GetProcessTimes(sp->handle, &cr, &ex, &kr, &us)) { - __int64 v; + rktio_int64_t v; uintptr_t msecs; - v = ((((__int64)kr.dwHighDateTime << 32) + kr.dwLowDateTime) - + (((__int64)us.dwHighDateTime << 32) + us.dwLowDateTime)); + v = ((((rktio_int64_t)kr.dwHighDateTime << 32) + kr.dwLowDateTime) + + (((rktio_int64_t)us.dwHighDateTime << 32) + us.dwLowDateTime)); msecs = (uintptr_t)(v / 10000); rktio->process_children_msecs += msecs; diff --git a/racket/src/rktio/rktio_time.c b/racket/src/rktio/rktio_time.c new file mode 100644 index 0000000000..5944a7e451 --- /dev/null +++ b/racket/src/rktio/rktio_time.c @@ -0,0 +1,484 @@ +#include "rktio.h" +#include "rktio_private.h" +#include +#include + +#if !defined(DONT_USE_GETRUSAGE) && defined(RKTIO_SYSTEM_UNIX) +# define USE_GETRUSAGE +#endif + +#ifdef RKTIO_SYSTEM_UNIX +# include +# include +# ifdef USE_GETRUSAGE +# include +# include +# include +# include +# endif /* USE_GETRUSAGE */ +# ifdef USE_SYSCALL_GETRUSAGE +# include +# define getrusage(a, b) syscall(SYS_GETRUSAGE, a, b) +# define USE_GETRUSAGE +# endif /* USE_SYSCALL_GETRUSAGE */ +# if !defined(USE_GETRUSAGE) && !defined(USER_TIME_IS_CLOCK) +# include +# endif +#endif + +#ifdef RKTIO_SYSTEM_WINDOWS +typedef BOOL (WINAPI*GetTimeZoneInformationForYearProc_t)(USHORT wYear, void* pdtzi, LPTIME_ZONE_INFORMATION ptzi); +static GetTimeZoneInformationForYearProc_t GetTimeZoneInformationForYearProc; + +typedef BOOL (WINAPI*SystemTimeToTzSpecificLocalTimeExProc_t)(void *lpTimeZoneInformation, + const SYSTEMTIME *lpUniversalTime, + LPSYSTEMTIME lpLocalTime); +static SystemTimeToTzSpecificLocalTimeExProc_t SystemTimeToTzSpecificLocalTimeExProc; +#endif + +/*========================================================================*/ +/* Time */ +/*========================================================================*/ + +#ifndef CLOCKS_PER_SEC +#define CLOCKS_PER_SEC 1000000 +#endif + +#ifdef RKTIO_SYSTEM_WINDOWS +/* Number of milliseconds from 1601 to 1970: */ +# define MSEC_OFFSET 11644473600000 + +rktio_int64_t get_hectonanoseconds_as_longlong() +/* this function can be called from any OS thread */ +{ + FILETIME ft; + rktio_int64_t v; + GetSystemTimeAsFileTime(&ft); + v = ((rktio_int64_t)ft.dwHighDateTime << 32) | ft.dwLowDateTime; + v -= ((rktio_int64_t)MSEC_OFFSET * 10000); + return v; +} +#endif + +intptr_t rktio_get_milliseconds(void) +/* this function can be called from any OS thread */ +{ +#ifdef RKTIO_SYSTEM_WINDOWS + return (intptr_t)(get_hectonanoseconds_as_longlong() / (rktio_int64_t)10000); +#else + struct timeval now; + gettimeofday(&now, NULL); + return now.tv_sec * 1000 + now.tv_usec / 1000; +#endif +} + +double rktio_get_inexact_milliseconds(void) +/* this function can be called from any OS thread */ +{ +#ifdef RKTIO_SYSTEM_WINDOWS + rktio_int64_t v; + v = get_hectonanoseconds_as_longlong(); + return (double)(v / 10000) + (((double)(v % 10000)) / 10000.0); +#else + struct timeval now; + gettimeofday(&now, NULL); + return (double)now.tv_sec * 1000.0 + (double)now.tv_usec / 1000; +#endif +} + +intptr_t rktio_get_process_milliseconds(rktio_t *rktio) +{ +#ifdef USER_TIME_IS_CLOCK + return scheme_get_milliseconds(); +#else +# ifdef USE_GETRUSAGE + struct rusage use; + intptr_t s, u; + + do { + if (!getrusage(RUSAGE_SELF, &use)) + break; + } while (errno == EINTR); + + s = use.ru_utime.tv_sec + use.ru_stime.tv_sec; + u = use.ru_utime.tv_usec + use.ru_stime.tv_usec; + + return s * 1000 + u / 1000; +# else +# ifdef RKTIO_SYSTEM_WINDOWS + { + FILETIME cr, ex, kr, us; + if (GetProcessTimes(GetCurrentProcess(), &cr, &ex, &kr, &us)) { + rktio_int64_t v; + v = ((((rktio_int64_t)kr.dwHighDateTime << 32) + kr.dwLowDateTime) + + (((rktio_int64_t)us.dwHighDateTime << 32) + us.dwLowDateTime)); + return (uintptr_t)(v / 10000); + } else + return 0; /* anything better to do? */ + } +# else + return clock() * 1000 / CLOCKS_PER_SEC; + +# endif +# endif +#endif +} + +intptr_t scheme_get_process_children_milliseconds(rktio_t *rktio) +{ +#ifdef USER_TIME_IS_CLOCK + return 0; +#else +# ifdef USE_GETRUSAGE + struct rusage use; + intptr_t s, u; + + do { + if (!getrusage(RUSAGE_CHILDREN, &use)) + break; + } while (errno == EINTR); + + s = use.ru_utime.tv_sec + use.ru_stime.tv_sec; + u = use.ru_utime.tv_usec + use.ru_stime.tv_usec; + + return (s * 1000 + u / 1000); +# else +# ifdef RKTIO_SYSTEM_WINDOWS + return rktio->process_children_msecs; +# else + clock_t t; + times(&t); + return (t.tms_cutime + t.tms_cstime) * 1000 / CLK_TCK; +# endif +# endif +#endif +} + +intptr_t rktio_get_seconds(rktio_t *rktio) +{ +#ifdef RKTIO_SYSTEM_WINDOWS + return (intptr_t)(get_hectonanoseconds_as_longlong() / (rktio_int64_t)10000000); +#else +# ifdef USE_PLAIN_TIME + time_t now; + now = time(NULL); + return now; +# else + struct timeval now; + gettimeofday(&now, NULL); + return now.tv_sec; +# endif +#endif +} + +/*========================================================================*/ +/* Date */ +/*========================================================================*/ + +#if defined(RKTIO_SYSTEM_WINDOWS) +/* Assuming not a leap year (and adjusted elsewhere): */ +static int month_offsets[13] = { 0, 31, 59, 90, + 120, 151, 181, 212, + 243, 273, 304, 334, + 365}; + +# define dtxCOMP(f) if (a->f < b->f) return 1; if (a->f > b->f) return 0; + +static int is_start_day_before(SYSTEMTIME *a, SYSTEMTIME *b) +{ + dtxCOMP(wYear); + + /* When comparing DST boundaries, we expect to get here, + because wYear will be 0 to mean "every year". */ + + dtxCOMP(wMonth); + + /* When comparing DST boundaries, it's unlikely that we'll get here, + because that would mean that StdT and DST start in the same month. */ + + dtxCOMP(wDay); /* for DST boundaires, this is a week number */ + dtxCOMP(wDayOfWeek); + dtxCOMP(wHour); + dtxCOMP(wMinute); + + return 0; +} + +static int is_day_before(SYSTEMTIME *a, SYSTEMTIME *b) +/* a is a date, and b is a DST boundary spec */ +{ + int dos, doc; + + if (b->wYear) { + dtxCOMP(wYear); + } + + dtxCOMP(wMonth); + + /* "Date" of a Sunday this month, 0 to 6: */ + dos = ((a->wDay - a->wDayOfWeek) + 7) % 7; + /* Date of first b->wDayOfWeek this month, 1 to 7: */ + doc = (dos + b->wDayOfWeek) % 7; + if (doc == 0) doc = 7; + /* Date of change this year: */ + doc = doc + ((b->wDay - 1) * 7); + if (doc > (month_offsets[b->wMonth] - month_offsets[b->wMonth-1])) + doc -= 7; + /* Above assumes that a time change doesn't occur on a leap day! */ + + if (a->wDay < doc) + return 1; + if (a->wDay > doc) + return 0; + + dtxCOMP(wHour); + dtxCOMP(wMinute); + + return 0; +} +# undef dtxCOMP +#endif + +#if defined(OS_X) && defined(__x86_64__) +/* work around a bug in localtime() in 10.6.8 */ +# include +# include +static int VALID_TIME_RANGE(intptr_t lnow) +{ + /* Fits in 32 bits? */ + int ilnow = (int)lnow; + if (lnow == (intptr_t)ilnow) + return 1; + + /* 10.7 or later? */ + { + int a[2]; + size_t len; + char *vers; + + a[0] = CTL_KERN; + a[1] = KERN_OSRELEASE; + sysctl(a, 2, NULL, &len, NULL, 0); + vers = malloc(len * sizeof(char)); + sysctl(a, 2, vers, &len, NULL, 0); + + if ((vers[0] == '1') && (vers[1] == '0') && (vers[2] == '.')) { + /* localtime() in 10.6.x (= 10.x at the kernel layer) doesn't seem + to work right with negative numbers that don't fit into 32 bits */ + free(vers); + return 0; + } + free(vers); + } + + return 1; +} +#else + +# ifdef MIN_VALID_DATE_SECONDS +# define VALID_TIME_RANGE_MIN(x) ((x) >= MIN_VALID_DATE_SECONDS) +# else +# define VALID_TIME_RANGE_MIN(x) 1 +# endif + +# if defined(MAX_VALID_DATE_SECONDS_BITS) && defined(SIXTY_FOUR_BIT_INTEGERS) +# define VALID_TIME_RANGE_BITS(x) (((x) >= 0) \ + ? ((x) == ((x) & (((intptr_t)1 << MAX_VALID_DATE_SECONDS_BITS) - 1))) \ + : ((-(x)) == ((-(x)) & (((intptr_t)1 << MAX_VALID_DATE_SECONDS_BITS) - 1)))) +# else +# define VALID_TIME_RANGE_BITS(x) 1 +# endif + +# define VALID_TIME_RANGE(x) (VALID_TIME_RANGE_MIN(x) && VALID_TIME_RANGE_BITS(x)) + +#endif + +rktio_date_t *rktio_seconds_to_date(rktio_t *rktio, intptr_t seconds, intptr_t nanoseconds, int get_gmt) +{ + intptr_t lnow; + int hour, min, sec, month, day, wday, yday, dst; + intptr_t year; + long tzoffset; +#ifdef RKTIO_SYSTEM_WINDOWS +# define CHECK_TIME_T uintptr_t + SYSTEMTIME localTime; +#else +# define CHECK_TIME_T time_t + struct tm *localTime; +#endif + CHECK_TIME_T now; + char *tzn; + rktio_date_t *result; + + lnow = seconds; + + if ((((intptr_t)(now = (CHECK_TIME_T)lnow)) == lnow) + && VALID_TIME_RANGE(lnow)) { + int success; + +#ifdef RKTIO_SYSTEM_WINDOWS + { + rktio_uint64_t tmpC; + tmpC = ((rktio_uint64_t)lnow * 10000000); + if ((rktio_int64_t)tmpC / 10000000 != lnow) { + /* overflow */ + success = 0; + } else { + rktio_int64_t nsC; + FILETIME ft; + nsC = tmpC + ((rktio_uint64_t)MSEC_OFFSET * 10000); + if (nsC < (rktio_int64_t)tmpC) { + /* overflow */ + success = 0; + } else { + ft.dwLowDateTime = nsC & (rktio_int64_t)0xFFFFFFFF; + ft.dwHighDateTime = nsC >> 32; + success = FileTimeToSystemTime(&ft, &localTime); + if (success && !get_gmt) { + SYSTEMTIME t2 = localTime; + if (SystemTimeToTzSpecificLocalTimeExProc) + success = SystemTimeToTzSpecificLocalTimeExProc(NULL, &t2, &localTime); + else + success = SystemTimeToTzSpecificLocalTime(NULL, &t2, &localTime); + } + } + } + } +#else + if (get_gmt) + localTime = gmtime(&now); + else + localTime = localtime(&now); + success = !!localTime; +#endif + + if (success) { +#ifdef RKTIO_SYSTEM_WINDOWS + + hour = localTime.wHour; + min = localTime.wMinute; + sec = localTime.wSecond; + + month = localTime.wMonth; + day = localTime.wDay; + year = localTime.wYear; + + wday = localTime.wDayOfWeek; + yday = month_offsets[localTime.wMonth-1] + day-1; + /* leap-year adjustment: */ + if ((month > 2) + && ((year % 4) == 0) + && (((year % 100) != 0) || ((year % 400) == 0))) + yday++; + + dst = 0; + if (get_gmt) { + tzoffset = 0; + tzn = "UTC"; + } else { + TIME_ZONE_INFORMATION tz; + if (GetTimeZoneInformationForYearProc) + GetTimeZoneInformationForYearProc(localTime.wYear, NULL, &tz); + else + (void)GetTimeZoneInformation(&tz); + if (tz.StandardDate.wMonth) { + if (is_start_day_before(&tz.DaylightDate, &tz.StandardDate)) { + /* northern hemisphere */ + dst = (!is_day_before(&localTime, &tz.DaylightDate) + && is_day_before(&localTime, &tz.StandardDate)); + } else { + /* southern hemisphere */ + dst = (is_day_before(&localTime, &tz.StandardDate) + || !is_day_before(&localTime, &tz.DaylightDate)); + } + } + if (dst) { + tzoffset = (tz.Bias + tz.DaylightBias) * -60; + tzn = NARROW_PATH_copy(tz.DaylightName); + } else { + tzoffset = (tz.Bias + tz.StandardBias) * -60; + tzn = NARROW_PATH_copy(tz.StandardName); + } + } +#else + hour = localTime->tm_hour; + min = localTime->tm_min; + sec = localTime->tm_sec; + + month = localTime->tm_mon + 1; + day = localTime->tm_mday; + year = (uintptr_t)localTime->tm_year + 1900; + + wday = localTime->tm_wday; + yday = localTime->tm_yday; + + if (get_gmt) + dst = 0; + else + dst = localTime->tm_isdst; + + tzoffset = 0; + if (!get_gmt) { +# ifdef USE_TIMEZONE_VAR + tzoffset = -MSC_IZE(timezone); +# endif +# ifdef USE_TOD_FOR_TIMEZONE + { + struct timezone xtz; + struct timeval xtv; + gettimeofday(&xtv, &xtz); + tzoffset = -(xtz.tz_minuteswest * 60); + } +# endif +# ifdef USE_TIMEZONE_VAR_W_DLS + tzoffset = -(MSCBOR_IZE(timezone) - (dst ? 3600 : 0)); +# endif +# ifdef USE_TIMEZONE_AND_ALTZONE_VAR + if (dst) + tzoffset = -altzone; + else + tzoffset = -timezone; +# endif +# ifdef USE_TM_GMTOFF_FIELD + tzoffset = localTime->tm_gmtoff; +# endif +# ifdef USE_TZNAME_VAR + tzn = MSC_IZE(tzname)[localTime->tm_isdst]; +# elif defined(USE_TM_ZONE_FIELD) + tzn = localTime->tm_zone; +# else + tzn = NULL; +# endif + } else + tzn = "UTC"; + +#endif + + if (!tzn) + tzn = "?"; + + result = malloc(sizeof(rktio_date_t)); + + result->nanosecond = nanoseconds; + result->second = sec; + result->minute = min; + result->hour = hour; + result->day = day; + result->month = month; + result->year = year; + result->day_of_week = wday; + result->day_of_year = yday; + result->is_dst = (dst ? 1 : 0); + result->zone_offset = tzoffset; + if (tzn) + result->zone_name = strdup(tzn); + else + result->zone_name = NULL; + + return result; + } + } + + set_racket_error(RKTIO_ERROR_TIME_OUT_OF_RANGE); + return NULL; +}