This seems unnecessarily broad, since the underlying Read call itself should be safe to do concurrently. It would seem better to replace the mutex with a sync.Once which would allow concurrent reads, and also provide a fast path with an atomic op once the initialization is complete.
The text was updated successfully, but these errors were encountered:
I looked into this more as I was fixing #9205. The problem is that we use a bufio.Reader around the *os.File reading from /dev/urandom, and it's for the bufio.Reader that the mutex is important.
If we were to fix the mutex contention issue for random, we'd probably want to do a per-processor buffer of /dev/urandom data that we tried to read from first, only falling back to re-filling the per-processor buffer from a shared (without locking) *os.File that we then do ~large reads occasionally. Then we wouldn't need bufio.Reader at all. We could use sync.Pool for the per-P buffer, but using that is contentious.
we use a bufio.Reader around the *os.File reading from /dev/urandom
Except on Plan9 for some reason?
I'm not sure why the standard library should be buffering here at all, honestly. I understand that it will aid performance in the non-concurrent case, but my first instinct would be to let the caller do the buffering (if they so desire) and by default to just do an unlocked unbuffered Read on the underlying fd.
We could use sync.Pool for the per-P buffer, but using that is contentious.
Most threading libraries (pthreads etc) provide a thread-local storage API that doesn't content; since go does it's own scheduler I imagine (pure speculation here) that you pin threads to processors at the OS level to avoid scheduler fights, so you could cheat and use thread-local storage to get your per-P buffers? If I'm wildly off-base feel free to ignore this :)