rktio: add date & time

This commit is contained in:
Matthew Flatt 2017-06-13 12:25:20 -06:00
parent c006e951f4
commit 636b1637b4
13 changed files with 965 additions and 45 deletions

View File

@ -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

View File

@ -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");

View File

@ -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 */

View File

@ -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

View File

@ -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 "???";

View File

@ -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

View File

@ -4,7 +4,7 @@
#include <stdlib.h>
#include <string.h>
#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 <sys/file.h>
#endif
#if defined(USE_FCNTL_AND_FORK_FOR_FILE_LOCKS)
#if defined(RKTIO_USE_FCNTL_AND_FORK_FOR_FILE_LOCKS)
#include <unistd.h>
#include <fcntl.h>
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

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,373 @@
/************** SunOS/Solaris ****************/
#if defined(sun)
# include "uconfig.h"
# define USE_EXPLICT_FP_FORM_CHECK
# include <errno.h>
# 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 <nasko@noac.bg> */
#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 <time.h>;
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 <dir.h> file (mainly for Windows). */
/* DIRECT_INCLUDE if there's a <direct.h> file (mainly for Windows). */
/* IO_INCLUDE if there's a <io.h> file (mainly for Windows). */
/* SELECT_INCLUDE if there's a <sys/select.h> file (mainly for Unix). */
/* BSTRING_INCLUDE if there's a <bstring.h> 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. */

View File

@ -2,6 +2,8 @@
# define OS_X
#endif
#include "rktio_platform.h"
#ifdef RKTIO_SYSTEM_WINDOWS
# include <winsock2.h>
# include <windows.h>
@ -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

View File

@ -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;

View File

@ -0,0 +1,484 @@
#include "rktio.h"
#include "rktio_private.h"
#include <stdlib.h>
#include <string.h>
#if !defined(DONT_USE_GETRUSAGE) && defined(RKTIO_SYSTEM_UNIX)
# define USE_GETRUSAGE
#endif
#ifdef RKTIO_SYSTEM_UNIX
# include <time.h>
# include <sys/time.h>
# ifdef USE_GETRUSAGE
# include <sys/types.h>
# include <sys/time.h>
# include <sys/resource.h>
# include <errno.h>
# endif /* USE_GETRUSAGE */
# ifdef USE_SYSCALL_GETRUSAGE
# include <sys/syscall.h>
# 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 <sys/times.h>
# 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 <sys/param.h>
# include <sys/sysctl.h>
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;
}