简体   繁体   中英

Race condition of buffered C++ streams when calling fork — force flush?

I have a program that spawns a writer thread with UNIX fork() . This works fine, but when a buffered C++ stream has not been flushed yet, I get a race condition where two threads output the same data. The following example shows what I mean:

extern "C" {
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
}

#define W 10

#include <iostream>

int main(void)
{
   pid_t pid;
   int status;
   for (int i = 0; i < (1 << W); i++) {

      // spawn a child after adding to the std::cout buffer
      if (i == (1 << (W - 1))) {
         // std::cout.flush(); // (1)
         pid = fork();
         if (!pid)
            break;
      }

      // join the child thread after a while
      if (i == 3 * (1 << (W - 2)))
         waitpid(pid, &status, 0);

      // print stuff to the stream
      std::cout << i << '\n';
      // std::cout << i << std::endl; // (2)
   }
   return EXIT_SUCCESS;
}

So the workaround I tried are to (1) flush std::cout by hand right before calling fork() (preferred solution), or (2) using std::endl when writing to the stream, but this adds unnecessarily many flush calls. Although this kind of works for std::cout which is globally accessible, my preferred solution (1) does not work for other buffered streams that are not globally accessible. Besides, at some point I might open another file and I would probably forget to flush it then.

Is there a better solution to this problem? Like a function that flushes all buffered C++ streams ?


EDIT

A suggested solution was to use fflush(nullptr) from the C library to flush all (C) streams. This works for std::cout and std::cerr which are kept in sync with stdout and stderr , but other C++ buffered streams will not be synced. This demonstrates the problem:

extern "C" {
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
}

#include <iostream>
#include <fstream>

#define W 10

int main(void)
{
   pid_t pid;
   int status;
   std::ofstream fout("foo");

   for (int i = 0; i < (1 << W); i++) {

      if (i == (1 << (W - 1))) {
         fflush(nullptr);  // this works for std::{cout,cerr} but not files
         pid = fork();
         if (!pid)
            return EXIT_SUCCESS;
      }

      if (i == 3 * (1 << (W - 2)))
         waitpid(pid, &status, 0);

      fout << i << '\n';
      std::cout << i << '\n';
   }
   fout.close();
   return EXIT_SUCCESS;
}

On my system I get

$ ./a.out 1>bar; wc -l foo bar
1536 foo
1024 bar

Needless to say that the number of lines should be equal.

Any further ideas?

Use fflush , and pass it nullptr .

From the man:

#include <cstdio> // adapted the include for C++

int fflush(FILE *stream);

If the stream argument is NULL, fflush() flushes all open output streams.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM