1

I am trying to capture the content written into std::cout stream to redirect it to a text file. For that, I intent to just follow the solution given in https://stackoverflow.com/questions/10150468/how-to-redirect-cin-and-cout-to-files:

    std::ofstream out("out.txt");
    std::cout.rdbuf(out.rdbuf()); //redirect std::cout to out.txt!

    std::cout << "blabla" << std::endl;  //output to the file out.txt

I understand from https://en.cppreference.com/w/cpp/io/cout that it is safe for two threads to write at the same time in std::cout. I am fully aware it does not mean that interleaving of text may not happen. My question: is this redirection of std::cout to std::ofstream still thread safe in a multi threading context ? My understanding is that std::ofstream is not thread safe by itself.

I did a test with my own implementation of the std::streambuf injected in std::cout and 5 threads writing on std:cout:

#include <iostream>
#include <thread>
#include <atomic>

using namespace std;

class StreamToLogRedirector:public
  std::streambuf
{
public:
  StreamToLogRedirector ():
  counter (0)
  {
  }

  std::streamsize
  xsputn (char_type const *s, std::streamsize count)
    override
  {

    counter++;
    if (counter > 1)
      {
    std::cerr << "Counter " << counter << std::endl;
      }
    counter--;
    return count;
  }

  int
  overflow (int c)
    override
  {
    counter++;
    if (counter > 1)
      {
    std::cerr << "Counter " << counter << std::endl;
      }
    counter--;
    return c;
  }

private:
  std::atomic < int >
    counter;
};

int
main ()
{
  StreamToLogRedirector
    redirector;
  std::cout.rdbuf (&redirector);

  auto
    a = std::thread ([](){
             while (true) std::cout << "Write from A\n";}
  );
  auto
    b = std::thread ([](){
             while (true) std::cout << "Write from B\n";}
  );
  auto
    c = std::thread ([](){
             while (true) std::cout << "Write from C\n";}
  );
  auto
    d = std::thread ([](){
             while (true) std::cout << "Write from D\n";}
  );
  auto
    e = std::thread ([](){
             while (true) std::cout << "Write from E\n";}
  );
  a.join ();
  b.join ();
  c.join ();
  d.join ();
  e.join ();

  return 0;
}

I can see that the counter is reaching 5, meaning that the functions StreamToLogRedirector are called in parallel.

My conclusion is that the thread safety aspect is not handled at the level of the std::cout object std::ostream, but at the level of the underlying std::streambuf. If std::ofstream is not thread safe, that mean that redirecting std::cout to std::ofstream is now making the usage of std:cout not thread safe by itself.

Is my conclusion correct or I am missing something ?

Thank you, Simon

Simon
  • 11
  • 2
  • streams are thread_safe, in that outputting from different threads won't break them, but doing so will almost certainly not do what you want – Neil Butterworth Nov 04 '22 at 15:39
  • @ThomasWeller Sorry to be lazy about joining all the thread, I did not do it as I don't care about how the program ends, it does not contribute to my test. But I will correct that as I understand that it is a bad example and can distract the reader. For the increment, I am using an atomic, is it not enough for the point I want to test ? – Simon Nov 04 '22 at 15:52
  • @Simon: sorry, my fault. I must have missed atomic. Sorry for my complaints. – Thomas Weller Nov 04 '22 at 15:54
  • @NeilButterworth Could you elaborate your answer ? My understanding of the std library is that nothing is thread safe unless specified. If the documentation tells about thread safety for the stream like cout/cerr, I see nothing for ofstream about thread safety. – Simon Nov 06 '22 at 10:48

0 Answers0