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