ref: 79213052a219d8c7ab85629807d07f7365ef9bec
dir: /sys/src/9/port/random.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "../port/error.h" struct Rb { QLock; Rendez producer; Rendez consumer; ulong randomcount; uchar buf[128]; uchar *ep; uchar *rp; uchar *wp; uchar next; uchar wakeme; ushort bits; ulong randn; } rb; static int rbnotfull(void*) { int i; i = rb.rp - rb.wp; return i != 1 && i != (1 - sizeof(rb.buf)); } static int rbnotempty(void*) { return rb.wp != rb.rp; } static void genrandom(void*) { up->basepri = PriNormal; up->priority = up->basepri; while(waserror()) ; for(;;){ if(++rb.randomcount <= 100000) continue; if(anyhigher()) sched(); if(!rbnotfull(0)) sleep(&rb.producer, rbnotfull, 0); } } /* * produce random bits in a circular buffer */ static void randomclock(void) { if(rb.randomcount == 0 || !rbnotfull(0)) return; rb.bits = (rb.bits<<2) ^ rb.randomcount; rb.randomcount = 0; rb.next++; if(rb.next != 8/2) return; rb.next = 0; *rb.wp ^= rb.bits; if(rb.wp+1 == rb.ep) rb.wp = rb.buf; else rb.wp = rb.wp+1; if(rb.wakeme) wakeup(&rb.consumer); } void randominit(void) { /* Frequency close but not equal to HZ */ addclock0link(randomclock, MS2HZ+3); rb.ep = rb.buf + sizeof(rb.buf); rb.rp = rb.wp = rb.buf; kproc("genrandom", genrandom, 0); } /* * consume random bytes from a circular buffer */ ulong randomread(void *xp, ulong n) { uchar *e, *p; ulong x; p = xp; if(waserror()){ qunlock(&rb); nexterror(); } qlock(&rb); for(e = p + n; p < e; ){ if(rb.wp == rb.rp){ rb.wakeme = 1; wakeup(&rb.producer); sleep(&rb.consumer, rbnotempty, 0); rb.wakeme = 0; continue; } /* * beating clocks will be predictable if * they are synchronized. Use a cheap pseudo- * random number generator to obscure any cycles. */ x = rb.randn*1103515245 ^ *rb.rp; *p++ = rb.randn = x; if(rb.rp+1 == rb.ep) rb.rp = rb.buf; else rb.rp = rb.rp+1; } qunlock(&rb); poperror(); wakeup(&rb.producer); return n; }