This is exactly the case here - i.e. I'm using IRC (hence the reading thread is blocked on read()) and I want to close the application.
What happens if you don't use a thread, not even a gui, you just write a plain old sequential command line application that prints IRC input to stdout. And then you close it with CTRL-C while it's hanging in a read(). Isn't that the same situation?
Ok, then please write an application using Qt that reads text from a socket in a worker thread and displays in on a widget.
Actually I did that with a similar technique like I described. A worker thread reads data from a serial connection and notifies an object using signals and slots that new data arrived. The data is passed as argument to the slot. The object builds a widget from the data and addWidget()s it to the gui. You wouldn't call it valid, becuase it uses signals and slots, but nevertheless it worked great so far. So what exactly is wrong with this approach?
Besides that, in what I was suggesting as solution for the IRC example the gui thread is only _reading_ from the buffer. How can a segfault occur like that? The worst thing that can happen is that you read corrupt half old half new data. And against that you can do this:
dataBuffer1[];
dataBuffer2[];
*readPointer = dataBuffer1;
*writePointer = dataBuffer2;
*tempPointer;
wroker_thread()
{
forever()
{
// retreive data from somewhere
data = readDataFromSocket();
// write data into the current write buffer
writeData(writePointer, data);
// flip pointers
tempPointer = readPointer;
readPointer = writePointer; // this needs to be atomic
writePointer = tempPointer;
}
main_thread()
{
*copyPointer;
forever()
{
// obtain a copy of the read pointer
copyPointer = readPointer; // this needs to be atomic
// read the data
data = readData(copyPointer);
/* Even if the worker thread flips the readPointer while we are reading here,
* it doesn't matter because we have a copy. */
}
}
dataBuffer1[];
dataBuffer2[];
*readPointer = dataBuffer1;
*writePointer = dataBuffer2;
*tempPointer;
wroker_thread()
{
forever()
{
// retreive data from somewhere
data = readDataFromSocket();
// write data into the current write buffer
writeData(writePointer, data);
// flip pointers
tempPointer = readPointer;
readPointer = writePointer; // this needs to be atomic
writePointer = tempPointer;
}
main_thread()
{
*copyPointer;
forever()
{
// obtain a copy of the read pointer
copyPointer = readPointer; // this needs to be atomic
// read the data
data = readData(copyPointer);
/* Even if the worker thread flips the readPointer while we are reading here,
* it doesn't matter because we have a copy. */
}
}
To copy to clipboard, switch view to plain text mode
No, it is not bound to be although most architectures assure that, but this is irrelevant here...
I think it's very relevant. If flipping a pointer is atomic, then the code above is safe up to the point where the processes run in such a weird asynchronous way, that the worker thread is executed twice while the main thread is still reading, so that it starts to write into the buffer the main thread is reading from.
All theory aside, I stress tested this approach on Linux and on Windows using C and Java, because I really wanted to know. I had a worker thread writing to the buffer at 100Hz and I spawned first one and then 100 reading threads reading the buffer over and over again with randomized sleeps and some of them at maximum capacity. I recorded how many times the writing of the worker thread and the reading of the main thread overlapped. It was 0. Without the double buffering it occured like 1 out of 1000 times, with it no accidents happened in 1 million read and write operations (over an hour of testing).
If flipping a buffer is not atomic, a segfault can occur because you could get "half" a pointer.
And for the case where two threads need to write to the same buffer, which I yet have to encounter, there are mutexes. What's wrong with them?
Bookmarks