1

Long time reader, first time poster...

I have a long-running PHP 7 process hosted on an Ubuntu 16.04 machine. It performs a variety of functions at various intervals; somethings every 2m, some ever 15m, etc. I trigger these functions based on the current epoch time compared the the 'next_time_to_run' maintained for each function. The next_time_to_run is recalculated each time the function is performed. For example, if a function is performed every 2min, then when it is triggered it simply adds 120 to the current epoch time to find the next trigger time.

I recently ran into an issue where the NTP process changed the system time rather dramatically while my process was running. As you might imagine this threw my scheduling into a tizzy. (oh yes, I said 'tizzy')

My question is this -- is it possible to register a callback/event handler/etc. that would be fired when the system time (epoch) is changed? BTW, the system clock is maintained at UTC.

Thanks oh-great-and-glorious-StackOverflow-hive-mind!

$dothing1time = time() + 120;
$dothing2time = time() + 3600;

while(1) {
    $now = time();
    if( $now >= $dothing1time ) { dothing1(); $dothing1time = $now + 120; }

    if( $now >= $dothing2time ) { dothing2(); $dothing2time = $now + 3600; }

    sleep( 30 );
}
TimC
  • 13
  • 2
  • most ugly and fast solution: 1) disable ntp daemon, 2) once a day (week/month) check the time inside your long script 2) launch the ntp update directly from your script 3) check the time again and find the diff – user1597430 Jan 10 '19 at 23:55
  • 2
    There is no built-in mechanism to be notified of a time change. I'd just split the functions up into separate commands and run each with its own cron -- totally removes all the time management from your code. – Alex Howansky Jan 10 '19 at 23:58
  • 2
    Depends on if you just want to [probe for adjustements](https://stackoverflow.com/questions/2251635/linux-detecting-change-of-system-time) or need [real notifications](https://lkml.org/lkml/2010/9/16/405) (not possible in PHP even with FFI). The reeal question is why this long-running script implements delays. Wouldn't multiple cron jobs be simpler? – mario Jan 10 '19 at 23:58
  • The functions do need to maintain sync with the 'real world' so disabling NTP is not viable. Running it sporadically, perhaps. – TimC Jan 11 '19 at 00:17
  • There are many more timed functions going on, and it monitors other 'stuff' so running as cron would not be practical (some functions need to be coordinated with others, etc.) – TimC Jan 11 '19 at 00:19
  • I'm thinking that perhaps I'll query NTP sync status and do something with that... for example, if it changes from 'not' to 'yes' then reset all my trigger times... – TimC Jan 11 '19 at 00:23
  • This isn't a problem to be solved in application code, this is a systems-level problem. The solution to which is running a properly-configured NTP daemon to keep the system clock in sync. – Sammitch Jan 11 '19 at 01:20
  • I don't see where time() has anything to do with anything here! In other words, set $now to 0, then update $now after every 30 seconds of sleep and that will allow you to do whatever you are doing without the need to ever call time(); see my example... – Stephanie Temple Jan 11 '19 at 02:01
  • @Sammitch - You are presuming an always-on internet connection, further, at initial startup, particularly on a freshly built machine (the devices in question are 'cloned' using pre-built disk images) the initial NTP sync may not have occurred by the time my app. starts. This specific situation was what triggered the question in the first place. Delaying startup until after initial NTP sync is also not an option as some devices may run for extended periods without a network connection. – TimC Jan 11 '19 at 13:37

2 Answers2

0
<?php

ignore_user_abort ( TRUE );

set_time_limit ( 0 );

$dothing1time = 120;

$dothing2time = 3600;

$now = 0;

$tic = 0;

$toc = 0;

while ( 1 )
{

    if ( $now >= $dothing1time )
    {
        dothing1 ( ++$tic );

        $dothing1time = $now + 120;
    }

    if( $now >= $dothing2time )
    {
        dothing2 ( ++$toc );

        $dothing2time = $now + 3600;
    }

    sleep( 30 );

    $now += 30;
}


function dothing1 ( $tic )
{
    file_put_contents ( './dothing1.txt', $tic );
}

function dothing2 ( $toc )
{
    file_put_contents ( './dothing2.txt', $toc );
}

?>
  • Aye, I like this. I can use this method for the bits that don't require sync to a specific wall-time. Many thanks! – TimC Jan 11 '19 at 13:40
0

To answer your specific question, "No." To my knowledge, there is no timechanged event to which you could attach a listener.

Unfortunately, given your current program architecture, I suspect that a robust solution will require a monotonically and uniformly increasing clock -- something us software and user-space folks take for granted. Further unfortunately, there is not a PHP function that guarantees the monotonic and uniformly increasing properties for which you were "abusing" the time() function.

However, there are a couple of possibilities.

  • A PHP-native solution might use a thread that simply loops, sleeps, and increments a counter. I leave the thread instantiation and other boilerplate as an "exercise for the reader," but an example thread kernel might be:

    while ( $threadAlive ) {
      if ( 0 == sleep( 1 ) )
        $programMonotonicClock++;
    }
    

    You'll have to test, as this, too, might fall victim to an NTP time change event, depending on how sleep() is implemented in PHP.

  • Another, perhaps more robust option, would be to exploit Linux's jiffy count for your program "clock." Jiffies are the number of CPU clock cycles since the computer first booted. Again, leaving the process wrappings and further parsing as an "exercise for the reader," consider this shell command to get the current jiffy count:

    $ grep ^jiff /proc/timer_list | head -1
    

Now, you can detect yourself whether the time has changed (by comparing to your known and recent values of your program counter/jiffy count), or just treat the program counter/jiffy count as your new program clock source.

hunteke
  • 3,648
  • 1
  • 7
  • 17
  • This is similar to the approach above; use my own 'clock' to determine intervals rather than compare to system time. Jiffies are a novel ideal, I had forgotten they existed, but I think I'll go with the simpler solution for interval based 'dothings'. Thanks! – TimC Jan 11 '19 at 13:43
  • Although, I am now concerned about the PHP implementation of sleep(). I'll avoid any recursion inducing reference to loosing sleep over sleep(). ;-) – TimC Jan 11 '19 at 13:47