Is the write() syscall thread safe?
Date: 14 Jun 2015Author: Erik Dubbelboer
Well of course it’s thread safe. All syscalls are thread safe. My real question is if calls to write() from multiple threads would interleave the data or write it one after the other. To test this I wrote a simple program:
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
const int bs = 1024*(1024*3+1);
int fd;
void foobar(char* x) {
// Put this on the heap cause the stack might be too small.
// On Mac OSX putting this on the stack causes a Bus error: 10
char* bar = malloc(bs);
for (int i = 0; i < 1024; i++) {
for (int j = 0; j < 1024; j++) {
bar[i*(1024*3+1)+j*3+0] = x[0];
bar[i*(1024*3+1)+j*3+1] = x[1];
bar[i*(1024*3+1)+j*3+2] = x[2];
}
bar[i*(1024*3+1)+1024*3] = '\n';
}
for (int i = 0; i < 1024; i++) { // less than 4GB
if (write(fd, bar, bs) != bs) {
exit(3);
}
}
}
void* other(void* unused) {
foobar("bar");
return 0;
}
int main() {
fd = open("test.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);
pthread_t t;
if (pthread_create(&t, 0, other, 0)) {
exit(1);
}
foobar("foo");
pthread_join(t, 0);
return 0;
}The output of the program looked like this:
$ time ./test
real 0m22.166s
user 0m0.016s
sys 0m12.418s
$ echo $?
0
$ ls -lh test.txt
-rw-r--r-- 1 erik staff 6.0G Feb 14 16:25 test.txt
$ time grep -v foo test.txt | grep -v bar
real 0m15.032s
user 0m5.749s
sys 0m4.725s
$ time grep -v -e foo -e bar test.txt
real 0m14.879s
user 0m4.822s
sys 0m3.237s
erik$
$ awk '{ print length() }' test.txt | uniq
3072
As you can see nothing went wrong and the program wrote 6GB of data in 22 seconds. Interleaving data would result in a single line containing both foo and bar. As you can see from the lacking output of grep this was not the case. Just to be sure the output of the awk line also shows that all lines are similar length meaning all writes completed fully after each other.
So the conclusion and answer to the question is that write() is completely thread safe and multiple calls to it are executed after each other.
One interesting side note is that the file did not contain interleaving lines of foo and bar as I would expect. Instead it contains large blocks of either foo or bar:
$ sed 's/\(...\).*/\1/' test.txt | uniq -c 6144 foo 7168 bar 4096 foo 2048 bar 83968 foo 1024 bar 1024 foo 1024 bar 1024 foo 1024 bar 2048 foo 29696 bar 1024 foo ^Ccomments powered by Disqus