On Sat, 1 Apr 2006 17:33:00 +0800
Verdi March
yep, the lesson that I learned. Initially I expect that 'atomic' covers both the location pointer and the 'no interleaving of bytes up to a certain size'. Turns out that the first is not.
And the occurrance of the race condition varies among platforms. I used a shell script that repeatedly executes the program. The shell script stops until the race condition occurs. On SUSE 9.3 and FC4 (64-bit Opteron), the race condition can occur pretty fast, while on SunOS it does not occur (but maybe it's because I didn't run the script long enough). The location pointer should be atomic. I did not run a recent stress test on the 2.6 kernel. One of the problems is that you used streamIO, where the buffering is both in user space and underneath in kernel space. The system calls, open(2), dup(2), write(2), close(2) are atomic. And as I mentioned, they refer to the same single kernel open file structure. But, the streamIO functions are library functions and are not guaranteed to be atomic. There are a number of methods in Linux to guarantee atomicity. You can use file locking, fcntl(2), flock(2), lockf(3):
child:
flock(2) // set the lock
fprintf(3)
fflush(3)
flock(2) // release the lock
...
Parent does the same thing.
You still have a race condition as to whose data is going to be written
first, but that is an application decision.
Another way is to write your own function using the vsprintf(3)
function. I know that using a fixed size buffer here is unsafe, but I'm
using it for the example. In the function, below, by using write(2) you
are bypassing the stream's file structure (and its own location
pointer).
int myprintf(FILE *stream, const char *fmt, ...)
{
va_list lp;
va_start(lp, fmt);
int rc;
size_t wrc;
char buf[some size];
rc = vsprintf(buf, lp); /* move stuff into buf */
(check rc to make sure vsprintf succeeded)
wrc = write(fileno(stream), buf, strlen(buf));
(check wrc)
return 0;
}
--
Jerry Feldman