Question

I am trying to simulate race conditions in writing to a file. This is what I am doing.

  1. Opening a.txt in append mode in process1
  2. writing "hello world" in process1
  3. prints the ftell in process1 which is 11
  4. put process1 in sleep
  5. open a.txt again in append mode in process2
  6. writing "hello world" in process2 (this correctly appends to the end of the file)
  7. prints the ftell in process2 which is 22 (correct)
  8. writing "bye world" in process2 (this correctly appends to the end of the file).
  9. process2 quits
  10. process1 resumes, and prints its ftell value, which is 11.
  11. writing "bye world" by process1 --- i assume as the ftell of process1 is 11, this should overwrite the file.

However, the write of process1 is writing to the end of the file and there is no contention in writing between the processes.

I am using fopen as fopen("./a.txt", "a+)

Can anyone tell why is this behavior and how can I simulate the race condition in writing to the file?

The code of process1:

#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
#include "time.h"
using namespace std;
int main()
{

    FILE *f1= fopen("./a.txt","a+");
    cout<<"opened file1"<<endl;
    string data ("hello world");
    fwrite(data.c_str(), sizeof(char), data.size(),  f1);
    fflush(f1);
    cout<<"file1 tell "<<ftell(f1)<<endl;
    cout<<"wrote file1"<<endl;
    sleep(3);
    string data1 ("bye world");;
    cout<<"wrote file1 end"<<endl;
    cout<<"file1 2nd tell "<<ftell(f1)<<endl;
    fwrite(data1.c_str(), sizeof(char),  data1.size(),  f1);
    cout<<"file1 2nd tell "<<ftell(f1)<<endl;
    fflush(f1);
    return 0;
}

In process2, I have commented out the sleep statement.

I am using the following script to run:

./process1 &
sleep 2
./process2 &

Thanks for your time.

Was it helpful?

Solution

The writer code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLOCKSIZE 1000000

int main(int argc, char **argv)
{
    FILE *f = fopen("a.txt", "a+");
    char *block = malloc(BLOCKSIZE);

    if (argc < 2)
    {
    fprintf(stderr, "need argument\n");
    }
    memset(block, argv[1][0], BLOCKSIZE);
    for(int i = 0; i < 3000; i++)
    {
    fwrite(block, sizeof(char), BLOCKSIZE, f);
    }
    fclose(f);
}

The reader function:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BLOCKSIZE 1000000

int main(int argc, char **argv)
{
    FILE *f = fopen("a.txt", "r");
    int c;
    int oldc = 0;
    int rl = 0;

    while((c = fgetc(f)) != EOF)
    {
    if (c != oldc)
    {
        if (rl)
        {
        printf("Got %d of %c\n", rl, oldc);
        }
        oldc = c;
        rl = 0;
    }
    rl++;
    }

    fclose(f);
}

I ran ./writefile A & ./writefile B then ./readfile

I got this:

Got 1000999424 of A
Got 999424 of B
Got 999424 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A
Got 4096 of B
Got 4096 of A
Got 995328 of B
Got 995328 of A

As you can see, there are nice long runs of A and B, but they are not exactly 1000000 characters long, which is the size I wrote them. The whole file, after a trialrun with a smaller size in the first run is just short of 7GB.

For reference: Fedora Core 16, with my own compiled 3.7rc5 kernel, gcc 4.6.3, x86-64, and ext4 on top of lvm, AMD PhenomII quad core processor, 16GB of RAM

OTHER TIPS

Writing in append mode is an atomic operation. This is why it doesn't break.

Now... how to break it?

Try memory mapping the file and writing in the memory from the two processes. I'm pretty sure this will break it.

I'm pretty sure you can't RELY on this behaviour, but it may well work reliably on some systems. Writing to the same file from two different processes is likely to cause problems sooner or later, if you "try hard enough". And sod's law says that that's exactly when your boss is checking if the software works, when your customer takes delivery of the system you've sold, or when you are finalizing your report that took ages to produce, or some other important time.

The behavior you're trying to break or see depends on which OS you are working on, as writing in a file is a system call. On what you told us about the first file descriptor to not overwrite what the second process wrote, the fact you opened the file in append mode in both process may have actualized the ftell value before actually writing in it.

Did you try to do the same with the standard open and write functions? Might be interesting as well.

EDIT: The C++ Reference doc explains about the fopen append option here: "append/update: Open a file for update (both for input and output) with all output operations writing data at the end of the file. Repositioning operations (fseek, fsetpos, rewind) affects the next input operations, but output operations move the position back to the end of file." This explains the behavior you observed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top