xref: /plan9-contrib/sys/src/9k/port/random.c (revision 9ef1f84b659abcb917c5c090acbce0772e494f21)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 
7 struct Rb
8 {
9 	QLock;
10 	Rendez	producer;
11 	Rendez	consumer;
12 	ulong	randomcount;
13 	uchar	buf[1024];
14 	uchar	*ep;
15 	uchar	*rp;
16 	uchar	*wp;
17 	uchar	next;
18 	uchar	wakeme;
19 	ushort	bits;
20 	ulong	randn;
21 } rb;
22 
23 static int
rbnotfull(void *)24 rbnotfull(void*)
25 {
26 	int i;
27 
28 	i = rb.rp - rb.wp;
29 	return i != 1 && i != (1 - sizeof(rb.buf));
30 }
31 
32 static int
rbnotempty(void *)33 rbnotempty(void*)
34 {
35 	return rb.wp != rb.rp;
36 }
37 
38 static void
genrandom(void *)39 genrandom(void*)
40 {
41 	up->basepri = PriNormal;
42 	up->priority = up->basepri;
43 
44 	for(;;){
45 		for(;;)
46 			if(++rb.randomcount > 100000)
47 				break;
48 		if(anyhigher())
49 			sched();
50 		if(!rbnotfull(0))
51 			sleep(&rb.producer, rbnotfull, 0);
52 	}
53 }
54 
55 /*
56  *  produce random bits in a circular buffer
57  */
58 static void
randomclock(void)59 randomclock(void)
60 {
61 	if(rb.randomcount == 0 || !rbnotfull(0))
62 		return;
63 
64 	rb.bits = (rb.bits<<2) ^ rb.randomcount;
65 	rb.randomcount = 0;
66 
67 	rb.next++;
68 	if(rb.next != 8/2)
69 		return;
70 	rb.next = 0;
71 
72 	*rb.wp ^= rb.bits;
73 	if(rb.wp+1 == rb.ep)
74 		rb.wp = rb.buf;
75 	else
76 		rb.wp = rb.wp+1;
77 
78 	if(rb.wakeme)
79 		wakeup(&rb.consumer);
80 }
81 
82 void
randominit(void)83 randominit(void)
84 {
85 	/* Frequency close but not equal to HZ */
86 	addclock0link(randomclock, 13);
87 	rb.ep = rb.buf + sizeof(rb.buf);
88 	rb.rp = rb.wp = rb.buf;
89 	kproc("genrandom", genrandom, 0);
90 }
91 
92 /*
93  *  consume random bytes from a circular buffer
94  */
95 ulong
randomread(void * xp,ulong n)96 randomread(void *xp, ulong n)
97 {
98 	uchar *e, *p;
99 	ulong x;
100 
101 	p = xp;
102 
103 	if(waserror()){
104 		qunlock(&rb);
105 		nexterror();
106 	}
107 
108 	qlock(&rb);
109 	for(e = p + n; p < e; ){
110 		if(rb.wp == rb.rp){
111 			rb.wakeme = 1;
112 			wakeup(&rb.producer);
113 			sleep(&rb.consumer, rbnotempty, 0);
114 			rb.wakeme = 0;
115 			continue;
116 		}
117 
118 		/*
119 		 *  beating clocks will be predictable if
120 		 *  they are synchronized.  Use a cheap pseudo-
121 		 *  random number generator to obscure any cycles.
122 		 */
123 		x = rb.randn*1103515245 ^ *rb.rp;
124 		*p++ = rb.randn = x;
125 
126 		if(rb.rp+1 == rb.ep)
127 			rb.rp = rb.buf;
128 		else
129 			rb.rp = rb.rp+1;
130 	}
131 	qunlock(&rb);
132 	poperror();
133 
134 	wakeup(&rb.producer);
135 
136 	return n;
137 }
138