use strict; use warnings; package Interval { use Time::HiRes; use constant HAS_CLOCK_MONOTONIC => eval { # use monotonic clock if available Time::HiRes::clock_gettime( Time::HiRes::CLOCK_MONOTONIC() ); 1; }; sub new { my ($class, $delay) = @_; return bless [ $delay, undef ], $class; } sub yield { my ($self) = @_; my ($delay, $lapse) = @{ $self }; my $time = HAS_CLOCK_MONOTONIC ? Time::HiRes::clock_gettime( Time::HiRes::CLOCK_MONOTONIC() ) : Time::HiRes::time(); if (!$delay || !defined $lapse) { $lapse = $time; } elsif ($lapse + $delay - $time < 0) { $lapse += int(abs($time - $lapse) / $delay + 0.5) * $delay; } $self->[1] = ($lapse += $delay); Time::HiRes::usleep(($lapse - $time) * 1e6); return; } } use Time::HiRes qw{ sleep time }; my $interval = Interval->new(0.1); my $last_value = 0; sub func { my ($last_value, $seq) = @_; printf "%0.3f $last_value $seq\n", time; sleep 0.1 if $seq == 3; # simulate extra time return $seq; } for my $seq (1..20) { # delay until the next time period $interval->yield; my $value = func($last_value, $seq); $last_value = $value; } __END__ 1597215036.077 0 1 1597215036.177 1 2 1597215036.277 2 3 # 0.1 gap between sequence 3 and 4 due to 3 taking extra time 1597215036.477 3 4 1597215036.577 4 5 1597215036.677 5 6 1597215036.777 6 7 1597215036.877 7 8 1597215036.978 8 9 1597215037.078 9 10 1597215037.177 10 11 1597215037.277 11 12 1597215037.377 12 13 1597215037.476 13 14 1597215037.578 14 15 1597215037.677 15 16 1597215037.777 16 17 1597215037.878 17 18 1597215037.977 18 19 1597215038.077 19 20