"io"
"os"
"sync"
- "sync/atomic"
"syscall"
"time"
)
type reader struct {
f io.Reader
mu sync.Mutex
- used int32 // atomic; whether this reader has been used
+ used bool // whether this reader has been used
}
// altGetRandom if non-nil specifies an OS-specific function to get
// urandom-style randomness.
var altGetRandom func([]byte) (ok bool)
+// batched returns a function that calls f to populate a []byte by chunking it
+// into subslices of, at most, readMax bytes, buffering min(readMax, 4096)
+// bytes at a time.
+func batched(f func([]byte) error, readMax int) func([]byte) bool {
+ bufferSize := 4096
+ if bufferSize > readMax {
+ bufferSize = readMax
+ }
+ fullBuffer := make([]byte, bufferSize)
+ var buf []byte
+ return func(out []byte) bool {
+ // First we copy any amount remaining in the buffer.
+ n := copy(out, buf)
+ out, buf = out[n:], buf[n:]
+
+ // Then, if we're requesting more than the buffer size,
+ // generate directly into the output, chunked by readMax.
+ for len(out) >= len(fullBuffer) {
+ read := len(out) - (len(out) % len(fullBuffer))
+ if read > readMax {
+ read = readMax
+ }
+ if f(out[:read]) != nil {
+ return false
+ }
+ out = out[read:]
+ }
+
+ // If there's a partial block left over, fill the buffer,
+ // and copy in the remainder.
+ if len(out) > 0 {
+ if f(fullBuffer[:]) != nil {
+ return false
+ }
+ buf = fullBuffer[:]
+ n = copy(out, buf)
+ out, buf = out[n:], buf[n:]
+ }
+
+ if len(out) > 0 {
+ panic("crypto/rand batching failed to fill buffer")
+ }
+
+ return true
+ }
+}
+
func warnBlocked() {
println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
}
func (r *reader) Read(b []byte) (n int, err error) {
boring.Unreachable()
- if atomic.CompareAndSwapInt32(&r.used, 0, 1) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ if !r.used {
+ r.used = true
// First use of randomness. Start timer to warn about
// being blocked on entropy not being available.
t := time.AfterFunc(time.Minute, warnBlocked)
if altGetRandom != nil && altGetRandom(b) {
return len(b), nil
}
- r.mu.Lock()
- defer r.mu.Unlock()
if r.f == nil {
f, err := os.Open(urandomDevice)
if err != nil {