how does timer: deal with real-time clock changes?

Matthias Lang matthias@REDACTED
Thu Mar 14 14:15:30 CET 2002

Ulf Wiger writes:

 > The following code in timer.erl seems to define this behaviour:

 > timer_timeout([{Time, BRef, {repeat, Interv, To}, MFA} | Ts],
 >               SysTime) ->
 >     do_apply(MFA),
 >     Ts0 = insert_sort({Time + Interv, BRef,   %% <<==== here!
 >                       {repeat, Interv, To}, MFA}, Ts),
 >     timer_timeout(Ts0, SysTime).
 > Since it adds Interv (in your case, 1000 ms) to the calculated
 > trigger time, instead of the actual trigger time, you will end up
 > with an interval timer that acts like you describe....
 > ...assuming that erlang:now() jumps around when the system clock
 > is altered!
 > I thought that erlang:now() had been adjusted on all platforms to
 > adjust smoothly to changes to the system clock. This is the case
 > on Solaris, at least. I do not see how the described behaviour
 > could happen given a well-behaved erlang:now().

Your description of what's going on is perfect.

The problem is with the code which smoothes out time changes. There are
two versions in erl_time_sup.c, and the one used for Linux systems
starts like this

 static void get_tolerant_timeofday(SysTimeval *tvp)
      clock_t current_ct;
      SysTimeval current_tv;
      Milli ct_diff;
      Milli tv_diff;
      Milli current_correction;
      long act_correction; /* Never more than a long (?) */
      Milli max_adjust;

Milli is a typedef for a 64-bit integer. The comment points to the
root of the problem. In my case, huge adjustments (say, 2 months)
caused timers to fail, and smaller ones did not. And indeed, 2 months
of milliseconds (5184000000) does not fit in a 32 bit integer.

Changing act_correction's type to Milli fixes the problem. Any reason
why this shouldn't be put in to R8B-1?

Aside: it is fundamentally wrong to combine wall time with the base
  used for time interval measurements if you cannot guarantee that wall
  time is continuous. No system that allows manual time adjustment can
  guarantee that. Smoothing out now() is a hack which we're probably
  stuck with because of existing code which relies on now() being
  continuous, including the timer() module. 

  A quick look at time.c suggests that the BIF timers 
  (e.g. erlang:send_after) are not related to now(). Does anyone
  know if 'receive' timeouts are?


More information about the erlang-questions mailing list