Thread safety in iostreams
In attempting to learn more about iostreams I have been mucking around
with some classes which I have derived from streambuf classes to
provide output to some external representations. By overloading
virtual fucntions overflow, and sync and providing functionaliy to
flush a buffer as required I am able to create streambufs which work
quite nicely. Nicely that is as long as I restrict their usage to
single threaded applications. As soon as I move to multithreaded apps
all sorts of problems arise. The following is a simpified version of
code that is heavily based on book based examples I have reveiwed set
up in this case to create a console window and write to that...
// streambuf derived object
class consolebuf : public std::streambuf {
public:
consolebuf() {
setp( buffer_, buffer_+(buffer_size_-1));
AllocConsole();
stdo_ = GetStdHandle( STD_OUTPUT_HANDLE );
SetConsoleTitleA( "Console Log Window" );
Sleep(50);
}
~consolebuf() {
sync();
}
protected:
virtual int_type overflow( int_type c ) {
if ( c != EOF )
{
*pptr() = c;
pbump(1);
}
if( flushbuffer() == EOF )
{
return EOF;
}
return c;
}
virtual int sync() {
if( flushbuffer() == EOF )
{
return -1;
}
return 0;
}
int flushbuffer() {
int num = pptr() - pbase();
DWORD cw;
WriteConsole( stdo_, buffer_, num, &cw, NULL );
pbump( -num );
return num;
}
private:
consolebuf( const consolebuf& );
consolebuf& operator=( const consolebuf& );
static const int buffer_size_ = 128;
char buffer_[buffer_size_];
HANDLE stdo_;
};
// ostream derived object - uses streambuf above template<typename T>
class mystream : public std::ostream {
public:
mystream() : std::ostream(0)
{
rdbuf( &buf_ );
}
~mystream(){};
mystream( const mystream& );
mystream& opeartor=( const mystream& );
T buf_;
};
So,
mystream<console> consolestream;
will create a stream object which will write output to a console
window. All is hunky dory until I test in a multi threadded app when
it immediately starts crashing. I can see that the problem is that
the write buffer is incorrectly placed and memory is trashed, I assume
this is as a second thread calls the streambuf flushbuffer method and
recalculates the 'num' value whilst another thread is in the middle of
a flushbuffer call also. I.e. thread one is writing a 10 char
string, calculates the offset num, writes the string and then before
the write pointer is reset another thread two recalculates the offset
to be for example 100 causing the write pointer to reset to an address
way before the start of the buffer. And I can stop this behaviour by
using a locking mechanism in the flush buffer method. My question
is... is this the correct approach to take? I understood that the
iostream objects handled their own locking on write operations through
their nested senrty objects, making the stream objects safe for
multithreaded use. Where is my understanding of this iostream locking
wrong? Also, if I correct the behaviour I see by adding the locking
mechanism then I still get intermixed outputs in the output stream. I
can see why this is the case but it is annoying. Is it possible to
prevent this mixing?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]