[Iostreams] Segmentation fault when stream is flushed or closed
Hello, I'm trying to open a boost::iostreams::stream in the constructor of a class and to close the stream in the destructor. This works fine but when I pass a reference to a std::stringstream in that same constructor I get a segmentation fault in the destructor when I flush or close the stream. This happens irrespective if the passed std::stringstream reference takes part in the boost::iostream::stream or not. I'm using gcc 4.6.3 with boost 1.53 on Linux x86_64. Below I provided a small example that exhibits this behaviour. Any ideas anyone? #include "test.h" int main(int argc, char **argv) { std::stringstream logStream; Test test(logStream); return 0; } where test.h contains: #include <fstream> #include <sstream> #include <boost/iostreams/stream.hpp> #include <boost/iostreams/tee.hpp> typedef boost::iostreams::tee_device<std::stringstream, std::ofstream> TeeDevice; typedef boost::iostreams::stream<TeeDevice> TeeStream; class Test { public: Test(const std::stringstream& logStream) { std::ofstream dummyFileHandle("/dev/null", std::ios_base::out | std::ios_base::app); std::stringstream logStream2; TeeDevice myTee (logStream2, dummyFileHandle); m_out.open(myTee); } virtual ~Test() { if(m_out.is_open()) { m_out.flush(); m_out.close(); } }; protected: TeeStream m_out; };
On Mon, 25 Nov 2013 11:48:19 +0100, Steven Maenhout <Steven.Maenhout@hogent.be> wrote:
Hello,
I'm trying to open a boost::iostreams::stream in the constructor of a class and to close the stream in the destructor. This works fine but when I pass a reference to a std::stringstream in that same constructor I get a segmentation fault in the destructor when I flush or close the stream. This happens irrespective if the passed std::stringstream reference takes part in the boost::iostream::stream or not.
I'm using gcc 4.6.3 with boost 1.53 on Linux x86_64.
Below I provided a small example that exhibits this behaviour.
Any ideas anyone?
#include "test.h"
int main(int argc, char **argv) {
std::stringstream logStream; Test test(logStream); return 0; }
where test.h contains:
#include <fstream> #include <sstream> #include <boost/iostreams/stream.hpp> #include <boost/iostreams/tee.hpp>
typedef boost::iostreams::tee_device<std::stringstream, std::ofstream> TeeDevice; typedef boost::iostreams::stream<TeeDevice> TeeStream;
class Test {
public: Test(const std::stringstream& logStream) {
std::ofstream dummyFileHandle("/dev/null", std::ios_base::out | std::ios_base::app); std::stringstream logStream2; TeeDevice myTee (logStream2, dummyFileHandle); m_out.open(myTee); } virtual ~Test() {
if(m_out.is_open()) { m_out.flush(); m_out.close(); } }; protected: TeeStream m_out; };
Hi Steven, I don't know boost::iostream, but valgrind shows that failure occurs because destructor attempts to dereference the memory, which was allocated on the stack. Moving dummyFileHandle and logStream2 into global scope fixes the crash. I guess, you cannot pass temporaries into TeeDevice constructor and have to hold dummyFileHandle and logStream2 alive as long as they are referenced in TeeDevice (making them class members accessed through shared_ptr perhaps?). Regards, Slava
On 11/25/2013 12:43 PM, Slava wrote:
I guess, you cannot pass temporaries into TeeDevice constructor and have to hold dummyFileHandle and logStream2 alive as long as they are referenced in TeeDevice (making them class members accessed through shared_ptr perhaps?).
Indeed, that was also my first conclusion but if I explicitly create temporary sinks by scoping but then flush and close the stream in the same constructor all works fine and Valgrind reports no issues: { std::ofstream dummyFileHandle("/dev/null", std::ios_base::out | std::ios_base::app); std::stringstream logStream2; TeeDevice myTee (logStream2, dummyFileHandle); m_out.open(myTee); } if(m_out.is_open()) { m_out.flush(); m_out.close(); } In fact, my earlier code also works fine if you do not pass the unused logStream to the constructor, which I find very confusing...
On Mon, 25 Nov 2013 13:21:33 +0100, Steven Maenhout <Steven.Maenhout@hogent.be> wrote:
On 11/25/2013 12:43 PM, Slava wrote:
I guess, you cannot pass temporaries into TeeDevice constructor and have to hold dummyFileHandle and logStream2 alive as long as they are referenced in TeeDevice (making them class members accessed through shared_ptr perhaps?).
Indeed, that was also my first conclusion but if I explicitly create temporary sinks by scoping but then flush and close the stream in the same constructor all works fine and Valgrind reports no issues:
{ std::ofstream dummyFileHandle("/dev/null", std::ios_base::out | std::ios_base::app); std::stringstream logStream2; TeeDevice myTee (logStream2, dummyFileHandle); m_out.open(myTee); }
if(m_out.is_open()) { m_out.flush(); m_out.close(); }
In fact, my earlier code also works fine if you do not pass the unused logStream to the constructor, which I find very confusing...
Generated assembly for Test::Test() and Test::Test(const std::stringstream& logStream) is identical up to saving the parameter passed in rsi/esi in stack (in debug version). When I change the constructor parameter to for example int, the crash occurs as well. So I would conclude, the crash is caused by dereferencing destroyed local variable. It just does not show itself in case of no parameter because the stack memory conflict occasionally does not happen under these circumstances and valgrind is not able to detect it properly. Regards, Slava
On 11/26/2013 08:55 AM, Slava wrote:
Generated assembly for Test::Test() and Test::Test(const std::stringstream& logStream) is identical up to saving the parameter passed in rsi/esi in stack (in debug version). When I change the constructor parameter to for example int, the crash occurs as well. So I would conclude, the crash is caused by dereferencing destroyed local variable. It just does not show itself in case of no parameter because the stack memory conflict occasionally does not happen under these circumstances and valgrind is not able to detect it properly.
that makes sense I suppose. You are definitely right that maintaining the sink streams solves my problems so I should not further complicate matters by finding examples that 'appear' to work with temporaries. thank you kindly for your help Slava, Steven
participants (2)
-
Slava
-
Steven Maenhout