I have a multithreaded application that uses standard cout for logging, for example,
cout << "some text" << endl;
The problem is that the program is facing an occasional crash because of non-thread-safe access to shared cout by multiple threads.
Say, my program is named prg.exe and we run as prog.exe > t.log
All logs generated by individual thread gets mingled up, even that's ok, the bad part is the crash whenever there is contention by the competing threads in accessing cout, one trying to flush, other trying yo put something there, resulting in the crash.
Since there are are lot many uses of cout in existing code, used by different threads, this is difficult to change all cout to something else. So, I am trying to adopt the following:
std::streambuf * redirect_output(char * filenm, std::ofstream& filestr)
{
std::streambuf *newsb, *oldsb;
filestr.open(filenm);
oldsb = std::cout.rdbuf(); // back up cout's streambuf
newsb = filestr.rdbuf(); // get file's streambuf
std::cout.rdbuf(newsb); // assign streambuf to cout
return oldsb;
}
void restore_output(std::streambuf * oldsb, std::ofstream& filestr)
{
std::cout.rdbuf(oldsb); // restore cout's original streambuf
filestr.close();
}
void showFileContent(char *filenm)
{
std::ifstream infile;
infile.open(filenm);
//read data from file
cout << "------------- " << filenm << " -------------" << endl;
cout << infile.rdbuf();
infile.close();
}
Each thread, on start, tries to call redirect_output to redirect cout to a file, separate file per thread. So, for example, if we have 3 threads, we have t1.log, t2.log, t3.log and at the end we call restore_output by each thread and eventually in main, we merge the individual log files through
showFileContent("t1.log");
showFileContent("t2.log");
showFileContent("t3.log");
My question is, is it wise and safe to redirect to individual log files per thread and merge at the end of the main function? Can we merge the individual log files at logical sync points by individual threads?
Other option could be to have a thread-safe singleton class encapsulating built-in i/o and use that singleton object instead of cout e.g. SPCL_IOSTREAM::getOStream()