Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linux 3.5: Safe to `read(2)` from same fd `open(2)` `/dev/urandom` from multiple threads?

Is it safe to do this:

int fd;

void thread_main()
{
    char buf[M];
    ssize_t r = read(fd, buf, M);
    assert(r == M);
    ...
}

int main()
{
    fd = open("/dev/urandom", O_RDONLY);

    for (int i = 0; i < N; i++)
         start_thread(i);

    for (int i = 0; i < N; i++)
         join_thread(i);
}

That is: After open(2)ing "/dev/urandom" from the main thread, is it safe to read(2) from it unsynchronized from different thread contexts?

Under what circumstances will the assert fire? Will two threads ever get the same data? What could go wrong?

like image 801
Andrew Tomazos Avatar asked Sep 03 '12 09:09

Andrew Tomazos


2 Answers

Your code is safe insofar as it will not crash. The assert will never trigger. No two threads will (should) ever get the same random data (it is highly unlikely but still possible that two threads get two "different" random sequences which are incidentially the same, so this cannot be 100% guaranteed).

/dev/urandom will never block or return fewer bytes than you try to read, however, if you read a large enough amount, it will eventually run out of entropy, so the quality of the random numbers will eventually degrade slightly. Usually, this is still just good enough, and it takes a whole while before that happens too, but it's something to be aware of (most people don't need to care, but it might not be acceptable, depending on what you do).

read/write are threadsafe (insofar as they won't crash or corrupt data or leave descriptors in undefined states) and in this special case also should not intermingle/split up bytes between reads/writes to/from different processes. In general, however, read/write do not guarantee this. They may intermingle data on concurrent reads/writes, on some devices.

It should however not be a problem, since random bits are still random if you get some other random bits and someone else gets some (different) bits in between.
If you think it is a problem for you, use readv which guarantees strict atomicity (no mixing/intermingling, ever). Anything that goes in/out of readv/writev is processed as one atomic unit, always (except on pipes, when exceeding a size of PIPE_BUF, as pointed out by rodrigo).

like image 135
Damon Avatar answered Sep 29 '22 08:09

Damon


read is thread safe in the sense that it is a system call, and if you do multiple reads from multiple threads, each read will be processed serially. That is to say thread A will read M bytes, thread B will read the next M bytes, and so on, rather than thread A reading a few bytes, thread B reading a few bytes, then thread A reading more bytes.

The same is true of write.

The usual issue with multiple threads doing reads and writes is that often they read/write 1 character at a time, so there's a hack of a lot of opportunity to mix up where the input goes and the output comes from - and often the confusion is added to by language runtime libraries existing on top of read/write

like image 26
Tom Tanner Avatar answered Sep 29 '22 07:09

Tom Tanner