2009-10-17

A tricky bug

Today I debugged a piece of code that looked like this:
printf("foobar\n");
if(fork() == 0)
exit(0);
When the program is run from the terminal, the output is as expected: a single line containing "foobar". However, when the output is sent to a pipe or redirected to a file, two lines appear in the output. (Actually, the problem was a bit more complicated: there were n fork() calls and there were exactly n additional copies of the output). After a bit of digging around and not finding any obvious fault, I asked about it on IRC.

It turned out that stdout is fully buffered when it is attached to a pipe or a file. What happened is that fork() duplicated also the internal I/O buffers, which got flushed after the child process exited, thus producing extra output. I solved the problem by inserting a fflush(NULL); statement before forking, which flushes buffers of all output streams.

2 comments:

Hrvoje said...

Note that a much better solution is to never use exit() in the child process. Use _exit() instead, this is exactly what it's for.

Libraries may be adding other code using atexit() behind your back, and you'll run into problems. I learned that the hard way, when a GUI program I was working on forked kept losing connections to the X server. It turned out that Xlib had an atexit() handler that notified the X server that the client was exiting. Since the child inherits all file descriptors, exit() in the child broke the parent's X connection.

In your case you probably need both if the child is doing its own printing to stdout.

zvrba said...

The documentation says that it is implementation-defined whether _exit() flushes open output streams. So, your solution won't work.