I’m working with some multithreaded code for a game project, and got a bit tired of sorting through the stdout vomit created by two threads using cout for debuging messages at the same time. I did some research and stared at a wall for an hour or two before coming up with “something”. The following code uses SFML for time keeping and threading. SFML mutexes are just wrapped critical sections in windows.
Header:
#include <SFML\System.hpp>
#include <iostream>
class OutputStreamHack
{
public:
OutputStreamHack();
~OutputStreamHack();
ostream& outputHijack(ostream &os);
private:
sf::Clock myRunTime;
sf::Mutex myMutex;
};
static OutputStream OUTHACK;
ostream& operator<<(ostream& os, const OutputStreamHack& inputValue);
Implementation:
#include <SFML\System.hpp>
#include <iostream>
#include "OutputStreamHack.h"
using namespace std;
OutputStreamHack::OutputStreamHack()
{
myMutex.Unlock();
myRunTime.Reset();
}
OutputStreamHack::~OutputStreamHack()
{
myMutex.Unlock();
myRunTime.Reset();
}
ostream& OutputStreamHack::outputHijack(ostream &os)
{
sf::Lock lock(myMutex);
os<<"<"<<myRunTime.GetElapsedTime()<<","<<GetCurrentThreadId()<<"> "<<flush;
return os;
}
ostream& operator<<(ostream& os, const OutputStreamHack& inputValue)
{
OUTHACK.outputHijack(os);
return os;
}
Usage:
cout<<OUTHACK<<val1<<val2<<val3....<<endl;
Ok, so the way this works is through an overloaded insertion operator that imposes thread safety by locking an iterator in a static object, then flushing the buffer. If I understand the process correctly (I am mostly a self taught programmer), cout processes elements of its insertion chain from the end to the beginning, passing an ostream variable down the chain for each element to be prepended to the stream. Once it reaches the OUTHACK element, the overloaded operator is called, the mutex is locked, and the stream is flushed.
I’ve added some time/thread id debugging info to the output for verification purposes. So far, my testing shows that this method works. I have several threads pounding cout with multiple arguments, and everything is coming out in the right order.
From what I have read while researching this issue, lack of thread safety in cout seems to be a pretty common problem that people run into while venturing into threaded programming. What I am trying to figure out is if the technique I am using is a simple solution to the problem, or me thinking that I am clever but missing something important.
In my experience, the word clever when used to describe programming is just a code word for delayed pain. Am I on to something here, or just chasing lousy hacks around in circles?
Thanks!
What is not threadsafe here is not
coutper se. It’s calling two function calls in sequence.std::cout << a << bis roughly equivalent to callingoperator<<(std::cout, a)followed byoperator<<(std::cout, b). Calling two functions in sequence carries no guarantee that they will be executed in an atomic fashion.As is, only the output of the time and thread id is protected by the mutex. It’s perfectly possible to get another thread sneak in between the insertion of
OUTHACKandval1, because the lock is no longer held afterOUTHACKis inserted.You can have
operator<<for yourOutputStreamHackreturn by value an object that unlocks in the destructor. Since temporaries live until the end of each full expression, the code would hold the lock “until the semicolon”. However, because copies may be involved, this could be problematic without a move constructor (or a custom copy constructor in C++03, similar toauto_ptr‘s gasp).Another option is to use the existing thread-safety of
cout(guaranteed by the language in C++11, but many implementations were threadsafe before). Make an object that streams everything into astd::stringstreammember and then write it all out at once when it is destroyed.