Skip Menu |

This queue is for tickets about the Time-HiRes CPAN distribution.

Report information
The Basics
Id: 78656
Status: resolved
Priority: 0/
Queue: Time-HiRes

People
Owner: Nobody in particular
Requestors: RCAPUTO [...] cpan.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: Patch: OSX clock_getres() and clock_gettime() support
Please find attached a patch that adds clock_getres() and clock_gettime() support for Mac OSX. CLOCK_MONOTONIC is properly supported, and CLOCK_REALTIME is implemented in terms of gettimeofday(). The existing tests pass. % prove -Ilib -Iblib/lib -Iblib/arch t/alarm.t ......... ok t/clock.t ......... ok t/gettimeofday.t .. ok t/itimer.t ........ ok t/nanosleep.t ..... ok t/sleep.t ......... ok t/stat.t .......... ok t/time.t .......... ok t/tv_interval.t ... ok t/ualarm.t ........ ok t/usleep.t ........ ok All tests successful. Files=11, Tests=68, 26 wallclock secs ( 0.10 usr 0.05 sys + 14.34 cusr 0.21 csys = 14.70 CPU) Result: PASS 1) macbook-wifki:~/projects/misc/3rd-party-projects/Time-HiRes-1.9725-patched% prove -Ilib -Iblib/lib -Iblib/arch -v t/clock.tt/clock.t .. 1..5 # I am the main process 62659, starting the watchdog process... # The watchdog process 62660 launched, continuing testing... # I am the watchdog process 62660, sleeping for 360 seconds... ok 1 - require Time::HiRes; # have_clock_gettime = 1 # have_clock_getres = 1 # have_clock_nanosleep = 0 # have_clock = 1 # CLOCK_REALTIME: try = 1 # t1 = 1343628900.29546, t0 = 1343628898.29596 # dt = 1.9995002746582, rt = 0.333000183105469 ok 2 ok 3 ok 4 # skip no clock_nanosleep # clock = 0.047162 # clock = 0.047162 0.120666 # clock = 0.047162 0.120666 0.192792 # clock = 0.047162 0.120666 0.192792 0.264973 ok 5 # I am the main process 62659, terminating the watchdog process 62660 before it terminates me in 358 seconds (testing took 2 seconds). # kill KILL 62660 = 1 # All done. ok All tests successful. Files=1, Tests=5, 2 wallclock secs ( 0.03 usr 0.01 sys + 0.25 cusr 0.01 csys = 0.30 CPU) Result: PASS
Subject: macos-clock-monotonic.diff
diff -rN Time-HiRes-1.9725/HiRes.xs Time-HiRes-1.9725-patched/HiRes.xs 42a43,44 > #include "clock_emu.h" > diff -rN Time-HiRes-1.9725/Makefile.PL Time-HiRes-1.9725-patched/Makefile.PL 308a309 > #include "clock_emu.h" 536,538c537 < if (exists $Config{d_clock_gettime}) { < $has_clock_gettime++ if $Config{d_clock_gettime}; # Unlikely... < } elsif (has_clock_xxx('gettime')) { --- > if (has_clock_xxx('gettime')) { 540a540,541 > } elsif (exists $Config{d_clock_gettime}) { > $has_clock_gettime++ if $Config{d_clock_gettime}; # Unlikely... diff -rN Time-HiRes-1.9725/clock_emu.h Time-HiRes-1.9725-patched/clock_emu.h 0a1,49 > #ifndef __CLOCK_EMU_H__ > > // MacOS X has a monotonic clock, but it's not POSIX. > > # if defined(__APPLE__) > > # include <sys/time.h> > # include <mach/mach_time.h> > > # define CLOCK_REALTIME 0x01 > # define CLOCK_MONOTONIC 0x02 > > static uint64_t monotonic_timebase_factor = 0; > > int clock_gettime(int clock_id, struct timespec *ts) { > switch (clock_id) { > case CLOCK_REALTIME: > // Cheezy hack? What would be better? > return gettimeofday(ts, NULL); > > case CLOCK_MONOTONIC: > if (monotonic_timebase_factor == 0) { > mach_timebase_info_data_t timebase_info; > (void)mach_timebase_info(&timebase_info); > monotonic_timebase_factor = timebase_info.numer / timebase_info.denom; > } > > uint64_t monotonic_nanoseconds = ( > mach_absolute_time() * monotonic_timebase_factor > ); > > ts->tv_sec = monotonic_nanoseconds / 1E9; > ts->tv_nsec = monotonic_nanoseconds - ts->tv_sec; > return 0; > > default: > return -1; > } > } > > int clock_getres(int clock_id, struct timespec *ts) { > ts->tv_sec = 0; > ts->tv_nsec = 1; > return 0; > } > > # endif > > #endif
I would love to see this patch applied.
There is now also a planned Mojolicious feature waiting for this patch to be applied. https://github.com/kraih/mojo/pull/469
Subject: Re: [rt.cpan.org #78656] Patch: OSX clock_getres() and clock_gettime() support
Date: Thu, 28 Mar 2013 13:12:22 +0000
To: Rocco Caputo via RT <bug-Time-HiRes [...] rt.cpan.org>
From: Zefram <zefram [...] fysh.org>
First, sorry about the delay in looking at this. I was out of action for some months for medical reasons, starting roughly when you submitted this patch, and I still haven't fully resumed CPAN activities. Rocco Caputo via RT wrote: Show quoted text
>Please find attached a patch that adds clock_getres() and clock_gettime() support for Mac OSX. CLOCK_MONOTONIC is properly >supported, and CLOCK_REALTIME is implemented in terms of gettimeofday().
Thanks. I'm fine with the intent of this patch, but I believe I see some bugs that need fixing. You mix up struct timeval and struct timespec in the gettimeofday() call. Your splitting of the monotonic ns count into s and ns looks wrong: presumably there should be a multiplication of the seconds by 1e9 for the subtraction. The 1e9 constant probably ought to be an integer literal, rather than the present floating point. It would help if you could work on these problems and submit a revised patch, as I don't have an OS X to test on. You'll need to apply the kind of tests that aren't amenable to automation: things like reading a clock repeatedly in a tight loop and checking that the readings are spaced with reasonable consistency. -zefram
Please find attached three patches. They implement the clock_getres() and clock_gettime() emulations for OSX, and while I was in the neighborhood, also clock_nanosleep(). Not perfectly, but should be close enough. The patches were written from scratch. Tested to work on OSX, without and with threads (note that they introduce a mutex, only in OSX, for certain initialization stuff), and not to break things in a few Linux/BSD/Solaris/other boxes.
Subject: 0001-OS-X-clock_gettime-and-clock_getres-emulation.patch
From 2e136bd591cc68f15a816932a5084896208c4dfd Mon Sep 17 00:00:00 2001 From: Jarkko Hietaniemi <jhi@iki.fi> Date: Thu, 14 Jan 2016 08:06:26 -0500 Subject: [PATCH 1/3] OS X clock_gettime() and clock_getres() emulation Note that CLOCK_REALTIME and CLOCK_MONOTONIC are the same clock, so both are monotonic. The difference is that the CLOCK_REALTIME is offset by the gettimeofday() result on the first use of these interfaces, and thereafter will closely track the gettimeofday() values. https://developer.apple.com/library/mac/qa/qa1398/_index.html "Mach Absolute Time Units" --- cpan/Time-HiRes/HiRes.xs | 97 +++++++++++++++++++++++++++++++++++++++++++++ cpan/Time-HiRes/Makefile.PL | 22 +++++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/cpan/Time-HiRes/HiRes.xs b/cpan/Time-HiRes/HiRes.xs index a4cece2..8b39dfd 100644 --- a/cpan/Time-HiRes/HiRes.xs +++ b/cpan/Time-HiRes/HiRes.xs @@ -759,6 +759,103 @@ hrstatns(UV *atime_nsec, UV *mtime_nsec, UV *ctime_nsec) #endif /* !TIME_HIRES_STAT */ } +/* Until Apple implements clock_gettime() (ditto clock_getres()) + * we will emulate it using Mach interfaces. */ +#if defined(PERL_DARWIN) && !defined(CLOCK_REALTIME) + +# include <mach/mach_time.h> + +# define CLOCK_REALTIME 0x01 +# define CLOCK_MONOTONIC 0x02 + +# define TIMER_ABSTIME 0x01 + +#ifdef USE_ITHREADS +STATIC perl_mutex darwin_time_mutex; +#endif + +static uint64_t absolute_time_init; +static mach_timebase_info_data_t timebase_info; +static struct timespec timespec_init; + +static int darwin_time_init() { +#ifdef USE_ITHREAD + PERL_MUTEX_LOCK(&darwin_time_mutex); +#endif + struct timeval tv; + int success = 1; + if (absolute_time_init == 0) { + /* mach_absolute_time() cannot fail */ + absolute_time_init = mach_absolute_time(); + success = mach_timebase_info(&timebase_info) == KERN_SUCCESS; + if (success) { + success = gettimeofday(&tv, NULL) == 0; + if (success) { + timespec_init.tv_sec = tv.tv_sec; + timespec_init.tv_nsec = tv.tv_usec * 1000; + } + } + } +#ifdef USE_ITHREAD + PERL_MUTEX_UNLOCK(&darwin_time_mutex); +#endif + return success; +} + +static int clock_gettime(int clock_id, struct timespec *ts) { + if (darwin_time_init() && timebase_info.denom) { + switch (clock_id) { + case CLOCK_REALTIME: + { + uint64_t nanos = + ((mach_absolute_time() - absolute_time_init) * + (uint64_t)timebase_info.numer) / (uint64_t)timebase_info.denom; + ts->tv_sec = timespec_init.tv_sec + nanos / IV_1E9; + ts->tv_nsec = timespec_init.tv_nsec + nanos % IV_1E9; + return 0; + } + + case CLOCK_MONOTONIC: + { + uint64_t nanos = + (mach_absolute_time() * + (uint64_t)timebase_info.numer) / (uint64_t)timebase_info.denom; + ts->tv_sec = nanos / IV_1E9; + ts->tv_nsec = nanos - ts->tv_sec * IV_1E9; + return 0; + } + + default: + break; + } + } + + SETERRNO(EINVAL, LIB_INVARG); + return -1; +} + +static int clock_getres(int clock_id, struct timespec *ts) { + if (darwin_time_init() && timebase_info.denom) { + switch (clock_id) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + ts->tv_sec = 0; + /* In newer kernels both the numer and denom are one, + * resulting in conversion factor of one, which is of + * course unrealistic. */ + ts->tv_nsec = timebase_info.numer / timebase_info.denom; + return 0; + default: + break; + } + } + + SETERRNO(EINVAL, LIB_INVARG); + return -1; +} + +#endif /* PERL_DARWIN */ + #include "const-c.inc" MODULE = Time::HiRes PACKAGE = Time::HiRes diff --git a/cpan/Time-HiRes/Makefile.PL b/cpan/Time-HiRes/Makefile.PL index bbdd7a7..d1647fe 100644 --- a/cpan/Time-HiRes/Makefile.PL +++ b/cpan/Time-HiRes/Makefile.PL @@ -536,6 +536,7 @@ EOD print "Looking for clock_gettime()... "; my $has_clock_gettime; + my $has_clock_gettime_emulation; if (exists $Config{d_clock_gettime}) { $has_clock_gettime++ if $Config{d_clock_gettime}; # Unlikely... } elsif (has_clock_xxx('gettime')) { @@ -544,11 +545,17 @@ EOD } elsif (defined $SYSCALL_H && has_clock_xxx_syscall('gettime')) { $has_clock_gettime++; $DEFINE .= ' -DTIME_HIRES_CLOCK_GETTIME -DTIME_HIRES_CLOCK_GETTIME_SYSCALL'; + } elsif ($^O eq 'darwin') { + $has_clock_gettime_emulation++; + $has_clock_gettime++; + $DEFINE .= ' -DTIME_HIRES_CLOCK_GETTIME'; } if ($has_clock_gettime) { if ($DEFINE =~ /-DTIME_HIRES_CLOCK_GETTIME_SYSCALL/) { print "found (via syscall).\n"; + } elsif ($has_clock_gettime_emulation) { + print "found (via emulation).\n"; } else { print "found.\n"; } @@ -558,6 +565,7 @@ EOD print "Looking for clock_getres()... "; my $has_clock_getres; + my $has_clock_getres_emulation; if (exists $Config{d_clock_getres}) { $has_clock_getres++ if $Config{d_clock_getres}; # Unlikely... } elsif (has_clock_xxx('getres')) { @@ -566,11 +574,17 @@ EOD } elsif (defined $SYSCALL_H && has_clock_xxx_syscall('getres')) { $has_clock_getres++; $DEFINE .= ' -DTIME_HIRES_CLOCK_GETRES -DTIME_HIRES_CLOCK_GETRES_SYSCALL'; + } elsif ($^O eq 'darwin') { + $has_clock_getres_emulation++; + $has_clock_getres++; + $DEFINE .= ' -DTIME_HIRES_CLOCK_GETRES'; } if ($has_clock_getres) { if ($DEFINE =~ /-DTIME_HIRES_CLOCK_GETRES_SYSCALL/) { print "found (via syscall).\n"; + } elsif ($has_clock_getres_emulation) { + print "found (via emulation).\n"; } else { print "found.\n"; } @@ -832,7 +846,7 @@ sub doConstants { d_nanosleep d_clock_gettime d_clock_getres d_clock d_clock_nanosleep d_hires_stat)) { my $macro = $_; - if ($macro =~ /^(d_nanosleep|d_clock_gettime|d_clock_getres|d_clock|d_clock_nanosleep)$/) { + if ($macro =~ /^(d_nanosleep|d_clock|d_clock_nanosleep)$/) { $macro =~ s/^d_(.+)/TIME_HIRES_\U$1/; } elsif ($macro =~ /^(d_hires_stat)$/) { my $d_hires_stat = 0; @@ -840,6 +854,12 @@ sub doConstants { push @names, {name => $_, macro => "TIME_HIRES_STAT", value => $d_hires_stat, default => ["IV", "0"]}; next; + } elsif ($macro =~ /^(d_clock_gettime|d_clock_getres)$/) { + $macro =~ s/^d_(.+)/TIME_HIRES_\U$1/; + my $val = ($DEFINE =~ /-D$macro\b/) ? 1 : 0; + push @names, {name => $_, macro => $macro, value => $val, + default => ["IV", "0"]}; + next; } else { $macro =~ s/^d_(.+)/HAS_\U$1/; } -- 2.7.0
Subject: 0002-OS-X-clock_nanosleep-emulation.patch
From 294d252078ecb71999397c6763f7ffdb4892a360 Mon Sep 17 00:00:00 2001 From: Jarkko Hietaniemi <jhi@iki.fi> Date: Wed, 13 Jan 2016 21:47:44 -0500 Subject: [PATCH 2/3] OS X clock_nanosleep() emulation https://developer.apple.com/library/ios/technotes/tn2169/_index.html "High Precision Timers in iOS / OS X" Returning the unspent time in the case of relative sleep is not implemented. --- cpan/Time-HiRes/HiRes.xs | 38 ++++++++++++++++++++++++++++++++++++++ cpan/Time-HiRes/Makefile.PL | 15 ++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/cpan/Time-HiRes/HiRes.xs b/cpan/Time-HiRes/HiRes.xs index 8b39dfd..2efc018 100644 --- a/cpan/Time-HiRes/HiRes.xs +++ b/cpan/Time-HiRes/HiRes.xs @@ -854,6 +854,44 @@ static int clock_getres(int clock_id, struct timespec *ts) { return -1; } +static int clock_nanosleep(int clock_id, int flags, + const struct timespec *rqtp, + struct timespec *rmtp) { + if (darwin_time_init()) { + switch (clock_id) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + { + uint64_t nanos = rqtp->tv_sec * IV_1E9 + rqtp->tv_nsec; + int success; + if ((flags & TIMER_ABSTIME)) { + uint64_t back = + timespec_init.tv_sec * IV_1E9 + timespec_init.tv_nsec; + nanos = nanos > back ? nanos - back : 0; + } + success = + mach_wait_until(mach_absolute_time() + nanos) == KERN_SUCCESS; + + /* In the relative sleep, the rmtp should be filled in with + * the 'unused' part of the rqtp in case the sleep gets + * interrupted by a signal. But it is unknown how signals + * interact with mach_wait_until(). In the absolute sleep, + * the rmtp should stay untouched. */ + rmtp->tv_sec = 0; + rmtp->tv_nsec = 0; + + return success; + } + + default: + break; + } + } + + SETERRNO(EINVAL, LIB_INVARG); + return -1; +} + #endif /* PERL_DARWIN */ #include "const-c.inc" diff --git a/cpan/Time-HiRes/Makefile.PL b/cpan/Time-HiRes/Makefile.PL index d1647fe..a2d3b12 100644 --- a/cpan/Time-HiRes/Makefile.PL +++ b/cpan/Time-HiRes/Makefile.PL @@ -594,15 +594,24 @@ EOD print "Looking for clock_nanosleep()... "; my $has_clock_nanosleep; + my $has_clock_nanosleep_emulation; if (exists $Config{d_clock_nanosleep}) { $has_clock_nanosleep++ if $Config{d_clock_nanosleep}; # Unlikely... } elsif (has_clock_nanosleep()) { $has_clock_nanosleep++; $DEFINE .= ' -DTIME_HIRES_CLOCK_NANOSLEEP'; + } elsif ($^O eq 'darwin') { + $has_clock_nanosleep++; + $has_clock_nanosleep_emulation++; + $DEFINE .= ' -DTIME_HIRES_CLOCK_NANOSLEEP'; } if ($has_clock_nanosleep) { - print "found.\n"; + if ($has_clock_nanosleep_emulation) { + print "found (via emulation).\n"; + } else { + print "found.\n"; + } } else { print "NOT found.\n"; } @@ -846,7 +855,7 @@ sub doConstants { d_nanosleep d_clock_gettime d_clock_getres d_clock d_clock_nanosleep d_hires_stat)) { my $macro = $_; - if ($macro =~ /^(d_nanosleep|d_clock|d_clock_nanosleep)$/) { + if ($macro =~ /^(d_nanosleep|d_clock)$/) { $macro =~ s/^d_(.+)/TIME_HIRES_\U$1/; } elsif ($macro =~ /^(d_hires_stat)$/) { my $d_hires_stat = 0; @@ -854,7 +863,7 @@ sub doConstants { push @names, {name => $_, macro => "TIME_HIRES_STAT", value => $d_hires_stat, default => ["IV", "0"]}; next; - } elsif ($macro =~ /^(d_clock_gettime|d_clock_getres)$/) { + } elsif ($macro =~ /^(d_clock_gettime|d_clock_getres|d_clock_nanosleep)$/) { $macro =~ s/^d_(.+)/TIME_HIRES_\U$1/; my $val = ($DEFINE =~ /-D$macro\b/) ? 1 : 0; push @names, {name => $_, macro => $macro, value => $val, -- 2.7.0
Subject: 0003-Warning-on-the-clock_getres-results.patch
From 48961e3f2140679f169454b31b2edd08ade5fb7e Mon Sep 17 00:00:00 2001 From: Jarkko Hietaniemi <jhi@iki.fi> Date: Thu, 14 Jan 2016 10:31:01 -0500 Subject: [PATCH 3/3] Warning on the clock_getres() results --- cpan/Time-HiRes/HiRes.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cpan/Time-HiRes/HiRes.pm b/cpan/Time-HiRes/HiRes.pm index 843d586..ef87d4d 100644 --- a/cpan/Time-HiRes/HiRes.pm +++ b/cpan/Time-HiRes/HiRes.pm @@ -356,6 +356,14 @@ specified by C<$which>. All implementations that support POSIX high resolution timers are supposed to support at least the C<$which> value of C<CLOCK_REALTIME>, see L</clock_gettime>. +B<NOTE>: the resolution returned may be highly optimistic. +Even if the resolution is high (a small number), all it means is that +you'll be able to specify the arguments to clock_gettime() and +clock_nanosleep() with that resolution, it doesn't mean that the +clock_gettime() results or the clock_nanosleep() effects will be of +that resolution, because various overheads and the overall system load +will affect the execution. + =item clock_nanosleep ( $which, $nanoseconds, $flags = 0) Sleeps for the number of nanoseconds (1e9ths of a second) specified. -- 2.7.0
Fixed in blead (now that T::H is blead-first) as of http://perl5.git.perl.org/perl.git/commitdiff/bcd2c0efd9224fe75a0bb40ebb907817ba1713c4 which bumps T::H to 1.9729
Fixed in 1.9731.
On Sun Mar 13 19:39:11 2016, JHI wrote: Show quoted text
> Fixed in 1.9731.
The 1.9731 did not work with threaded Perls in OS X, so 1.9732 happened.