ref: 3bf89ed825835b574c3d1c7f640918e65aac488d
dir: /sys/src/9/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 #define MicroFREQ 1000000ULL struct { int init; /* true if initialized */ ulong cnt; Lock; uvlong multiplier; /* ns = off + (multiplier*ticks)>>31 */ uvlong divider; /* ticks = (divider*(ns-off))>>31 */ uvlong umultiplier; /* µs = (µmultiplier*ticks)>>31 */ uvlong udivider; /* ticks = (µdivider*µs)>>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; static void todfix(void); void todinit(void) { if(tod.init) return; ilock(&tod); tod.init = 1; /* prevent reentry via fastticks */ tod.last = fastticks((uvlong *)&tod.hz); iunlock(&tod); todsetfreq(tod.hz); addclock0link(todfix, 100); } /* * calculate multiplier */ void todsetfreq(vlong f) { if (f <= 0) panic("todsetfreq: freq %lld <= 0", f); ilock(&tod); tod.hz = f; /* calculate multiplier for time conversion */ tod.multiplier = mk64fract(TODFREQ, f); tod.divider = mk64fract(f, TODFREQ) + 1; tod.umultiplier = mk64fract(MicroFREQ, f); tod.udivider = mk64fract(f, MicroFREQ) + 1; 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; if (n == 0) { iprint("todset: n == 0, delta == %lld\n", delta); delta = 0; } else 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 */ static void todfix(void) { vlong ticks, diff; uvlong x; ticks = fastticks(nil); diff = ticks - tod.last; if(diff <= tod.hz) return; ilock(&tod); diff = ticks - tod.last; if(diff > tod.hz){ /* convert to epoch */ mul64fract(&x, diff, tod.multiplier); x += tod.off; /* protect against overflows */ tod.last = ticks; tod.off = x; } iunlock(&tod); } long seconds(void) { return (vlong)todget(nil) / TODFREQ; } uvlong fastticks2us(uvlong ticks) { uvlong res; if(!tod.init) todinit(); mul64fract(&res, ticks, tod.umultiplier); return res; } uvlong us2fastticks(uvlong us) { uvlong res; if(!tod.init) todinit(); mul64fract(&res, us, tod.udivider); return res; } /* * 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; }