ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /os/port/tod.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
/* compute nanosecond epoch time from the fastest ticking clock
* on the system. converting the time to nanoseconds requires
* the following formula
*
* t = (((1000000000<<31)/f)*ticks)>>31
*
* where
*
* 'f' is the clock frequency
* 'ticks' are clock ticks
*
* to avoid too much calculation in todget(), we calculate
*
* mult = (1000000000<<32)/f
*
* each time f is set. f is normally set by a user level
* program writing to /dev/fastclock. mul64fract will then
* take that fractional multiplier and a 64 bit integer and
* return the resulting integer product.
*
* We assume that the cpu's of a multiprocessor are synchronized.
* This assumption needs to be questioned with each new architecture.
*/
/* frequency of the tod clock */
#define TODFREQ 1000000000ULL
struct {
int init; // true if initialized
ulong cnt;
Lock;
uvlong multiplier; // t = off + (multiplier*ticks)>>31
uvlong divider; // ticks = (divider*(ticks-off))>>31
vlong hz; // frequency of fast clock
vlong last; // last reading of fast clock
vlong off; // offset from epoch to last
vlong lasttime; // last return value from todget
vlong delta; // add 'delta' each slow clock tick from sstart to send
ulong sstart; // ...
ulong send; // ...
} tod;
void
todinit(void)
{
if(tod.init)
return;
ilock(&tod);
tod.last = fastticks((uvlong*)&tod.hz);
iunlock(&tod);
todsetfreq(tod.hz);
tod.init = 1;
addclock0link(todfix, 100);
}
/*
* calculate multiplier
*/
void
todsetfreq(vlong f)
{
ilock(&tod);
tod.hz = f;
/* calculate multiplier for time conversion */
tod.multiplier = mk64fract(TODFREQ, f);
tod.divider = mk64fract(f, TODFREQ);
iunlock(&tod);
}
/*
* Set the time of day struct
*/
void
todset(vlong t, vlong delta, int n)
{
if(!tod.init)
todinit();
ilock(&tod);
if(t >= 0){
tod.off = t;
tod.last = fastticks(nil);
tod.lasttime = 0;
tod.delta = 0;
tod.sstart = tod.send;
} else {
if(n <= 0)
n = 1;
n *= HZ;
if(delta < 0 && n > -delta)
n = -delta;
if(delta > 0 && n > delta)
n = delta;
delta = delta/n;
tod.sstart = MACHP(0)->ticks;
tod.send = tod.sstart + n;
tod.delta = delta;
}
iunlock(&tod);
}
/*
* get time of day
*/
vlong
todget(vlong *ticksp)
{
uvlong x;
vlong ticks, diff;
ulong t;
if(!tod.init)
todinit();
// we don't want time to pass twixt the measuring of fastticks
// and grabbing tod.last. Also none of the vlongs are atomic so
// we have to look at them inside the lock.
ilock(&tod);
tod.cnt++;
ticks = fastticks(nil);
// add in correction
if(tod.sstart != tod.send){
t = MACHP(0)->ticks;
if(t >= tod.send)
t = tod.send;
tod.off = tod.off + tod.delta*(t - tod.sstart);
tod.sstart = t;
}
// convert to epoch
diff = ticks - tod.last;
if(diff < 0)
diff = 0;
mul64fract(&x, diff, tod.multiplier);
x += tod.off;
// time can't go backwards
if(x < tod.lasttime)
x = tod.lasttime;
else
tod.lasttime = x;
iunlock(&tod);
if(ticksp != nil)
*ticksp = ticks;
return x;
}
/*
* convert time of day to ticks
*/
uvlong
tod2fastticks(vlong ns)
{
uvlong x;
ilock(&tod);
mul64fract(&x, ns-tod.off, tod.divider);
x += tod.last;
iunlock(&tod);
return x;
}
/*
* called regularly to avoid calculation overflows
*/
void
todfix(void)
{
vlong ticks, diff;
uvlong x;
ticks = fastticks(nil);
diff = ticks - tod.last;
if(diff > tod.hz){
ilock(&tod);
// convert to epoch
mul64fract(&x, diff, tod.multiplier);
if(x > 30000000000ULL) print("todfix %llud\n", x);
x += tod.off;
// protect against overflows
tod.last = ticks;
tod.off = x;
iunlock(&tod);
}
}
long
tseconds(void)
{
vlong x;
int i;
x = todget(nil);
x = x/TODFREQ;
i = x;
return i;
}
// convert milliseconds to fast ticks
//
uvlong
ms2fastticks(ulong ms)
{
if(!tod.init)
todinit();
return (tod.hz*ms)/1000ULL;
}
/*
* convert nanoseconds to fast ticks
*/
uvlong
ns2fastticks(uvlong ns)
{
uvlong res;
if(!tod.init)
todinit();
mul64fract(&res, ns, tod.divider);
return res;
}
/*
* convert fast ticks to ns
*/
uvlong
fastticks2ns(uvlong ticks)
{
uvlong res;
if(!tod.init)
todinit();
mul64fract(&res, ticks, tod.multiplier);
return res;
}
/*
* Make a 64 bit fixed point number that has a decimal point
* to the left of the low order 32 bits. This is used with
* mul64fract for converting twixt nanoseconds and fastticks.
*
* multiplier = (to<<32)/from
*/
uvlong
mk64fract(uvlong to, uvlong from)
{
/*
int shift;
if(to == 0ULL)
return 0ULL;
shift = 0;
while(shift < 32 && to < (1ULL<<(32+24))){
to <<= 8;
shift += 8;
}
while(shift < 32 && to < (1ULL<<(32+31))){
to <<= 1;
shift += 1;
}
return (to/from)<<(32-shift);
*/
return (to<<32)/from;
}