Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/port/tod.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname port/tod.c 1999/0219
## diff -e /dev/null /n/emeliedump/1999/0219/sys/src/brazil/port/tod.c
0a
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"


// frequency of the tod clock
#define TODFREQ	1000000000LL

enum
{
	Log2mult=	22,	// multiplier should have 22 bits
	Log2todfreq=	30,	// number of significant bits of TODFREQ
};

static vlong logtab[40];

struct {
	Lock;
	int	shift;		// time = (ticks*multiplier)>>shift
	vlong	multiplier;	// ...
	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 gettod
	vlong	delta;		// amount to add to bias each slow clock tick
	int	n;		// number of times to add in delta
	int	i;		// number of times we've added in delta
} tod;

int
log2(vlong x)
{
	int i;

	for(i = 0; i < nelem(logtab); i++){
		if(x < logtab[i])
			break;
	}
	return i+8;
}

void
todinit(void)
{
	vlong v;
	int i;

	v = 1LL<<8;
	for(i = 0; i < nelem(logtab); i++){
		logtab[i] = v;
		v <<= 1;
	}
	fastticks((uvlong*)&tod.hz);
	todsetfreq(tod.hz);
	addclock0link(todfix);
}

//
//  This routine makes sure that the multiplier has
//  at least Log2mult bits to guarantee that precision.
//
void
todsetfreq(vlong f)
{
	// this ensures that the multiplier has 22 bits
	ilock(&tod);
	tod.hz = f;
	tod.shift = Log2mult - Log2todfreq + log2(f);
	tod.multiplier = (TODFREQ<<tod.shift)/f;
	iunlock(&tod);
}

//
//  Set the time of day struct
//
void
todset(vlong t, vlong delta, int n)
{
	ilock(&tod);
	tod.i = tod.n = 0;
	if(t >= 0){
		tod.off = t;
		tod.last = fastticks(nil);
		tod.lasttime = 0;
	} else {
		tod.delta = delta;
		tod.n = n;
	}
	iunlock(&tod);
}

//
//  get time of day
//
vlong
todget(void)
{
	vlong ticks, x, diff;

	ilock(&tod);

	if(tod.hz == 0)
		ticks = fastticks((uvlong*)&tod.hz);
	else
		ticks = fastticks(nil);
	diff = ticks - tod.last;

	// convert to epoch
	x = (diff*tod.multiplier)>>tod.shift;
	x += tod.off;

	// protect against overflows
	if(diff > (1LL<<(63-Log2mult))){
		tod.last = ticks;
		tod.off = x;
	}

	/* time can't go backwards */
	if(x < tod.lasttime)
		x = tod.lasttime;
	tod.lasttime = x;

	iunlock(&tod);
	return x;
}

//
//  called every clock tick
//
void
todfix(void)
{
	if((MACHP(0)->ticks % HZ) != 0)
		return;
	ilock(&tod);
	if(tod.n > tod.i++)
		tod.off += tod.delta;
	iunlock(&tod);
}

long
seconds(void)
{
	vlong x;
	int i;

	x = todget();
	x /= TODFREQ;
	i = x;
	return i;
}
.
## diffname port/tod.c 1999/0225
## diff -e /n/emeliedump/1999/0219/sys/src/brazil/port/tod.c /n/emeliedump/1999/0225/sys/src/brazil/port/tod.c
141a

	// once a minute, make sure we don't overflow
	if((MACHP(0)->ticks % (60*HZ)) == 0)
		todget();
.
137a

	// once a second apply correction
.
116c
	if(diff > tod.maxdiff){
.
112c
	x = ((diff>>tod.s2)*tod.multiplier)>>(tod.s1-tod.s2);
.
73a
//print("hz %lld mult %lld s1 %d s2 %d maxdiff %lld\n", tod.hz, tod.multiplier, tod.s1, tod.s2, tod.maxdiff);
.
71,72c
	lf = log2(f);
	tod.s2 = lf - 25;
	if(tod.s2 < 0)
		tod.s2 = 0;
	tod.s1 = 2*lf - tod.s2 - 30;
	if(tod.s1 < 0)
		tod.s1 = 0;
	if(tod.s1 > 33)
		tod.s1 = 33;
	tod.multiplier = (TODFREQ<<tod.s1)/f;
	tod.maxdiff = 1LL<<(10 + lf);
.
67a
	int lf;

.
23a
	int	s2;		// ...
	vlong	maxdiff;	// max diff between ticks and last to avoid overflow
.
22c
	int	s1;		// time = ((ticks>>s2)*multiplier)>>(s1-s2)
.
12,17d
7a
// compute nanosecond epoch time from the fastest ticking clock
// on the system.  converting the time to nanoseconds requires
// the following formula
//
//	t = (((1000000000<<s1)/f)*(ticks>>s2))>>(s1-s2)
//
//  where
//
//	'f'		is the clock frequency
//	'ticks'		are clock ticks
//	's1' and 's2'	are shift ammounts to avoid 64 bit
//			overflows in the calculations
//
//  to avoid too much calculation in gettod(), we calculate
//
//	mult = (1000000000<<s1)/f
//
//  each time f is set.  f is normally set by a user level
//  program writing to /dev/fastclock.
//
//  To calculate s1 and s2, we have to avoid overflowing our
//  signed 64 bit calculations.  Also we wish to accomodate
//  15 minutes of ticks.  This gives us the following
//  constraints:
//
//	1) log2(1000000000<<s1) <= 63
//	   or s1 <= 33
//	2) accomodate 15 minutes of ticks without overflow
//	   or log2(((1000000000<<s1)/f)*((15*60*f)>>s2)) <= 63
//	   or log2(mult) + 12 + log2(f) - s2 <= 63
//	   or log2(mult) + log2(f) - 51 <= s2
//
//  by definition
//
//	3) log2(mult) = log2(1000000000) + s1 - log2(f)
//	   or log2(mult) = 30 + s1 - log2(f)
//
//  To balance the accuracy of the multiplier and the sampled
//  ticks we set
//
//	4) log2(mult) = log2(f>>s2)
//	   or log2(mult) = log2(f) - s2
//
//  Combining 2) and 4) we get
//
//	5) log2(f) - s2 + log2(f) - 51 <= s2
//	   or 2*log2(f) - 51 <= 2*s2
//	   or log2(f) - 25 <= s2
//
//  Combining 3) and 4)
//
//	6) 30 + s1 - log2(f) = log2(f) - s2
//	   or s1 = 2*log2(f) - s2 - 30
//
//  Since shifting ticks left doesn't increase accuracy, and
//  shifting 1000000000 right loses accuracy
//
//	7) s2 >= 0
//	8) s1 >= 0
//
//  As an example, that gives us the following
//
//	for f = 100, log2(f) = 7
//
//		s2 = 0
//		s1 = 0
//
//	for f = 267000000, log2(f) = 28
//
//		s2 = 3
//		s1 = 23
//
//	for f = 2000000000, log2(f) = 31
//
//		s2 = 6
//		s1 = 26
//
//	for f = 8000000000, log2(f) = 33
//
//		s2 = 8
//		s1 = 28
.
## diffname port/tod.c 1999/0227
## diff -e /n/emeliedump/1999/0225/sys/src/brazil/port/tod.c /n/emeliedump/1999/0227/sys/src/brazil/port/tod.c
225,233d
199a
	// add in correction
	if(tod.sstart < tod.send){
		t = MACHP(0)->ticks;
		if(t >= tod.send)
			t = tod.send;
		tod.off += tod.delta*(t - tod.sstart);
		tod.sstart = t;
	}

.
190a
	ulong t;
.
179d
177a
		if(n <= 0)
			n = 1;
		n *= HZ;
		if(delta < 0 && n > -delta)
			n = -delta;
		if(delta > 0 && n > delta)
			n = delta;
		delta /= n;
		tod.sstart = MACHP(0)->ticks;
		tod.send = tod.sstart + n;
.
172c
	tod.sstart = tod.send = 0;
.
162d
105,107c
	vlong	delta;		// add 'delta' each slow clock tick from sstart to send
	ulong	sstart;		// ...
	ulong	send;		// ...
.
## diffname port/tod.c 1999/0421
## diff -e /n/emeliedump/1999/0227/sys/src/brazil/port/tod.c /n/emeliedump/1999/0421/sys/src/brazil/port/tod.c
222,226c
	// protect against overflows (gettod is called at least once a second)
	tod.last = ticks;
	tod.off = x;
.
160d
157,158c
	if(tod.s1 > 32)
		tod.s1 = 32;
.
151c
	tod.s2 = lf - 28;
.
100d
87,88c
//		s2 = 5
//		s1 = 31
.
82,83c
//		s2 = 3
//		s1 = 29
.
77,78c
//		s2 = 0
//		s1 = 26
.
53,55c
//	5) log2(f) - s2 + log2(f) - 57 <= s2
//	   or 2*log2(f) - 57 <= 2*s2
//	   or log2(f) - 28 <= s2
.
34,38c
//	   or s1 <= 32
//	2) accomodate 1 minute of ticks without overflow
//	   or log2(((1000000000<<s1)/f)*((60*f)>>s2)) <= 63
//	   or log2(mult) + 6 + log2(f) - s2 <= 63
//	   or log2(mult) + log2(f) - 57 <= s2
.
## diffname port/tod.c 1999/0508
## diff -e /n/emeliedump/1999/0421/sys/src/brazil/port/tod.c /n/emeliedump/1999/0508/sys/src/brazil/port/tod.c
239,240c
	// once a second, make sure we don't overflow
	if((MACHP(0)->ticks % HZ) == 0)
.
224c
	// time can't go backwards
.
216,217c
	// convert to epoch, make sure calculation is unsigned
	x = (((uvlong)diff) * ((uvlong)tod.multiplier)) >> 31;
.
196c
	uvlong x;
	vlong ticks, diff;
.
149,158c
	tod.multiplier = (TODFREQ<<31)/f;
.
146c
	// the shift is an attempt to maintain precision
	// during the caculations.  the number of bits in
	// the multiplier should be log(TODFREQ) + 31 - log(f).
	//
	//	Freq		bits
	//	167 MHZ		34
	//	267 MHZ		33
	//	500 MHZ		32
	//
	// in all cases, we need to call todget() at least once
	// a second to keep the subsequent calculations from
	// overflowing.

.
138,139c
//  calculate multiplier
.
124,131d
109,120d
93,94d
89a

.
27,88d
23c
//	mult = (1000000000<<31)/f
.
18,19d
12c
//	t = (((1000000000<<31)/f)*ticks)>>31
.
## diffname port/tod.c 1999/0509
## diff -e /n/emeliedump/1999/0508/sys/src/brazil/port/tod.c /n/emeliedump/1999/0509/sys/src/brazil/port/tod.c
58,59d
## diffname port/tod.c 1999/0517
## diff -e /n/emeliedump/1999/0509/sys/src/brazil/port/tod.c /n/emeliedump/1999/0517/sys/src/brazil/port/tod.c
157a
	}
.
156c
	if(MACHP(0)->ticks - last >= HZ){
		last = MACHP(0)->ticks;
.
154a
	static ulong last;

.
136,138c
	// protect against overflows
	if(diff > tod.hz){
		tod.last = ticks;
		tod.off = x;
	}
.
132,133c
	// convert to epoch
	x = (diff * tod.multiplier) >> 31;
.
124c
	if(tod.sstart != tod.send){
.
88a
		tod.delta = 0;
		tod.sstart = tod.send;
.
84d
58,70d
32,34c
	vlong	multiplier;	// t = off + (multiplier*ticks)>>31
.
## diffname port/tod.c 1999/0531
## diff -e /n/emeliedump/1999/0517/sys/src/brazil/port/tod.c /n/emeliedump/1999/0531/sys/src/brazil/port/tod.c
158c
	x = todget(nil);
.
148c
		todget(nil);
.
132a
	if(ticksp != nil)
		*ticksp = ticks;

.
95c
todget(vlong *ticksp)
.
## diffname port/tod.c 1999/0806
## diff -e /n/emeliedump/1999/0531/sys/src/brazil/port/tod.c /n/emeliedump/1999/0806/sys/src/brazil/port/tod.c
162c
	x = x/TODFREQ;
.
120c
	x = x + tod.off;
.
114c
		tod.off = tod.off + tod.delta*(t - tod.sstart);
.
83c
		delta = delta/n;
.
## diffname port/tod.c 1999/0810
## diff -e /n/emeliedump/1999/0806/sys/src/brazil/port/tod.c /n/emeliedump/1999/0810/sys/src/brazil/port/tod.c
136d
131c
	else
		tod.lasttime = x;
.
122,125c
	if(m->machno == 0){
		ilock(&tod);

		// 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;
		}

		// protect against overflows
		if(diff > tod.hz){
			tod.last = ticks;
			tod.off = x;
		}
		iunlock(&tod);
.
109,117d
101,102d
## diffname port/tod.c 2000/0621
## diff -e /n/emeliedump/1999/0810/sys/src/brazil/port/tod.c /n/emeliedump/2000/0621/sys/src/9/port/tod.c
155d
151,153c
	ticks = fastticks(nil);

	diff = ticks - tod.last;
	if(diff > tod.hz)
.
149c
	vlong ticks, diff;
.
144c
//  called every clock tick to avoid calculation overflows
.
## diffname port/tod.c 2000/0625
## diff -e /n/emeliedump/2000/0621/sys/src/9/port/tod.c /n/emeliedump/2000/0625/sys/src/9/port/tod.c
135a
	iunlock(&tod);
.
128d
112d
104a

	/* since 64 bit ops are not atomix, we have to lock around them */
	ilock(&tod);
.
## diffname port/tod.c 2000/0626
## diff -e /n/emeliedump/2000/0625/sys/src/9/port/tod.c /n/emeliedump/2000/0626/sys/src/9/port/tod.c
156,157c
	if(diff > tod.hz){
		ilock(&tod);
	
		// convert to epoch
		diff = ticks - tod.last;
		x = (diff * tod.multiplier) >> 31;
		x = x + tod.off;
	
		// protect against overflows
		tod.last = ticks;
		tod.off = x;
	
		iunlock(&tod);
	}
.
151a
	uvlong x;
.
136a

.
114,131d
110a
	diff = ticks - tod.last;
.
109a
	// 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;
	}

.
108c
	tod.cnt++;
.
106c
	// since 64 bit loads are not atomic, we have to lock around them
.
30a
	ulong	cnt;
.
24a
//
//  We assume that the cpu's of a multiprocessor are synchronized.
//  This assumption needs to be questioned with each new architecture.
.
## diffname port/tod.c 2000/1215
## diff -e /n/emeliedump/2000/0626/sys/src/9/port/tod.c /n/emeliedump/2000/1215/sys/src/9/port/tod.c
158,159c
		x = diff * tod.multiplier;
		x = x >> 31;
.
49c
	ilock(&tod);
	tod.last = fastticks((uvlong*)&tod.hz);
	iunlock(&tod);
.
40c
	vlong	lasttime;	// last return value from todget
.
19c
//  to avoid too much calculation in todget(), we calculate
.
## diffname port/tod.c 2002/0410
## diff -e /n/emeliedump/2000/1215/sys/src/9/port/tod.c /n/emeliedump/2002/0410/sys/src/9/port/tod.c
181a
}

//  convert milliseconds to fast ticks
//
uvlong
ms2fastticks(ulong ms)
{
	if(tod.hz == 0)
		fastticks((uvlong*)&tod.hz);
	return (tod.hz*ms)/1000ULL;
.
64a

.
63a

	/* calculate multiplier for time conversion */
.
31c
#define TODFREQ	1000000000ULL
.
## diffname port/tod.c 2002/0411
## diff -e /n/emeliedump/2002/0410/sys/src/9/port/tod.c /n/emeliedump/2002/0411/sys/src/9/port/tod.c
41,43c
	vlong	delta;	// add 'delta' each slow clock tick from sstart to send
	ulong	sstart;	// ...
	ulong	send;	// ...
.
## diffname port/tod.c 2002/0928
## diff -e /n/emeliedump/2002/0411/sys/src/9/port/tod.c /n/emeliedump/2002/0928/sys/src/9/port/tod.c
148c
//  called regularly to avoid calculation overflows
.
53c
	addclock0link(todfix, 100);
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].