Jump to content

  • Log In with Google      Sign In   
  • Create Account


Boost::thread - problems with inheritance


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • This topic is locked This topic is locked
11 replies to this topic

#1 Mercenarey   Banned   -  Reputation: 103

Like
0Likes
Like

Posted 24 June 2010 - 12:46 AM

I have made a class to launch threads like this:


#include <boost/thread.hpp>
class BaseThreadObject
{
protected:
//---------- Protected Properties -----------------
boost::thread* m_thread;
//------------ Protected Methods ------------------
public:
//-- Constructors, Destructor & Support Methods ---
BaseThreadObject();
virtual ~BaseThreadObject();

void Startup();
void Shutdown();
//-------- Get's --------------------------
//-------- Set's --------------------------
//---------- Public Methods -----------------------
void operator()();

virtual void ThreadMain() = 0;
};

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::operator()()
{
ThreadMain();
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::Startup()
{
m_thread = new boost::thread(boost::ref(*this));
// m_thread = new boost::thread(*this);
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::Shutdown()
{
m_thread->join();
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::ThreadMain()
{
// whatever
}





The Startup launches the thread with the class as a ref, to prevent it from being copied (boost takes 10(!!) copies when launching). Also, I hoped that with a ref, that I could have preserved the inheritance, so I could call derived classes through virtual methods.

However, when I call a virtual method, even if it is implemented in the base class, like the operator()() calls ThreadMain() (which I hoped I could then freely derive for new classes, while preserving basic threading functionality in this baseclass alone), it will fail with this message:
"Unhandled exception at 0x00000000 in [prog-name].exe: 0xC0000005: Access violation reading location 0x00000000"

I have similar problems when I don't use the ref and allows it to copy. It will only copy the class, that is directly inputted, not the classes it derives from.

Is there any way I can create a class like this with boost, that retains an inheritance structure?

Sponsor:

#2 Katie   Members   -  Reputation: 1243

Like
0Likes
Like

Posted 24 June 2010 - 01:20 AM

I don't know why you're having type problems, but what I do is have the class contain a "work()" function.

Then go;

typedef shared_ptr<boost::thread> ThrP;
ThrP thr;

thr=ThrP(new boost::thread(boost::bind(&CMyClass::work,this)));

I don't care then if boost starts copying the bind output about the place, because it's copying a dinky class and not my class.


#3 Red Ant   Members   -  Reputation: 447

Like
0Likes
Like

Posted 24 June 2010 - 02:11 AM

I'm suspecting that no matter how much you try to cheat by using boost::ref to pass a functor to boost::thread's constructor, internally it will still at some point make a copy of the functor, and when that happens, it won't have a clue that you're really passing a derived thread object and not a BaseThreadObject. Hence it will store a copy of the BaseThreadObject part of the class instance you're passing it. The result -> slicing (google it).


I suggest the following.



struct MyFunctor
{
BaseThreadObject* m_ptrToThreadObject;

MyFunctor( BaseThreadObject* ptrToThreadObject ) : m_ptrToThreadObject( ptrToThreadObject ) {}

void operator () ()
{
m_ptrToThreadObject->ThreadMain();
}
};

void BaseThreadObject::Startup()
{
m_thread = new boost::thread( MyFunctor( this ) );
}




But beware that you may run into object life time issues if your BaseThreadObject instance gets deleted while your thread is still running (and hence potentially accessing your BaseThreadObject instance).

#4 Mercenarey   Banned   -  Reputation: 103

Like
0Likes
Like

Posted 24 June 2010 - 06:02 AM

Red Ant, good suggestion.

I tried it, and put a breakpoint in MyFunctor::operator()(), to inspect the values. m_ptrToThreadObject had the same address as the BaseThreadObject, but the values inside BaseThreadObject were wrong (uninitialized). The same was true when another breakpoint stopped in BaseThreadObject::ThreadMain() (which I implemented directly in BaseThreadObject, to not have inheritance make it even more complex).

How is this possible? Since there is only one class, how can it be sliced?
Im completely lost at what is going on with boost::thread...

Edit:
Just for the record, I created a test variable (an integer) and gave it a value in Startup(), before launching. It is this value that does not show up in the threaded version.

#5 SiS-Shadowman   Members   -  Reputation: 359

Like
0Likes
Like

Posted 24 June 2010 - 11:49 PM

I've just skimmed over, but wouldn't

new boost::thread(boost::bind(&BaseThreadObject::operator(), this));

be a safe way to address all your issues?

*edit*
And just out of curiosity, why are you storing the boost::thread instance on the heap? You don't need to initialize a boost::thread in its constructor, so the following will work as well (and you don't have to call delete in your destructor):

class Whatever
{
boost::thread m_thread;

Whatever()
{
// some code
m_thread = boost::thread(boost::bind(&Whatever::operator(), this));
}
};


Another thing that bugs me (although this may never be an issue in real cases) is that what if operator() gets called BEFORE the derrived class is constructed. I am not sure but I think the overloaded thread main function pointer will not be written into the vtable until the construction of the derrived class. Therefore your code might not work in those cases.

*edit2*
I just saw this got suggested before. If this doesn't work for you, then the issue is something else. We use boost::thread at work in exactly that way and it works perfectly.

#6 Mercenarey   Banned   -  Reputation: 103

Like
0Likes
Like

Posted 25 June 2010 - 12:19 AM

I don't know what happened yesterday, but when I tested the code again today with a fresh machine and compilation, it worked as it is supposed to, with Red Ant's implementation.

So now it works perfectly!

SiS-Shadowman, Bind() is a possibility as well, if you don't like the operator()(), and creating thread on the heap is also a possibility. Personally I prefer to create it dynamically, I feel I have more control over it, as I can call Shutdown() and have it destroyed, instead of relying on the destructor (maybe I want to reuse the object and use Shutdown() to reset the object for a new Startup()?).

I have tested it with inheritance as well, and it works fine.

Final implementation:

// ------------------------------------------------
// ------------------------------------------------
class BaseThreadObject
{
// ----------------------------------------
// Red Ant's version modified to my code
// standard. Inlined as implementation is
// irrelevant to outsiders.
// ----------------------------------------
struct BaseThreadObjectFunctor
{
BaseThreadObject* m_basethreadobject;
BaseThreadObjectFunctor(BaseThreadObject* a_basethreadobject);

void operator()();
};
protected:
//------------ Protected Properties ---------------
int m_testdata;
boost::thread* m_thread;
MyFunctor* m_myfunctor;
//------------ Protected Methods ------------------
public:
//-- Constructors, Destructor & Support Methods ---
BaseThreadObject();
virtual ~BaseThreadObject();

void Startup();
void Shutdown();
//---- Get's ----------------------
//---- Set's ----------------------
//-------------- Public Methods -------------------
virtual void ThreadMain();
};


// ------------------------------------------------
// ------------------------------------------------
class TestThreadObject : public BaseThreadObject
{
protected:
//------------ Protected Properties ---------------
//------------ Protected Methods ------------------
public:
//-- Constructors, Destructor & Support Methods ---
TestThreadObject();
virtual ~TestThreadObject();
//---- Get's ----------------------
//---- Set's ----------------------
//-------------- Public Methods -------------------
virtual void ThreadMain();
};


// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
BaseThreadObject::BaseThreadObjectFunctor::BaseThreadObjectFunctor(BaseThreadObject* a_basethreadobject) : m_basethreadobject(a_basethreadobject)
{
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::BaseThreadObjectFunctor::operator()()
{
m_basethreadobject->ThreadMain();
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::Startup()
{
m_testdata = 11;

m_myfunctor = new MyFunctor(this);
m_thread = new boost::thread(boost::ref(*m_myfunctor));
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::Shutdown()
{
m_thread->join();
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void BaseThreadObject::ThreadMain()
{
// m_testdata will correctly be 11 in the thrown thread
int dd = m_testdata;
}

// ------------------------------------------------
// ------------------------------------------------
// ------------------------------------------------
void TestThreadObject::ThreadMain()
{
// m_testdata will correctly be 11 in the thrown thread - also here
// in the derived thread
int dd = BaseThreadObject::m_testdata;
}






[Edited by - Mercenarey on June 25, 2010 9:19:56 AM]

#7 Red Ant   Members   -  Reputation: 447

Like
0Likes
Like

Posted 25 June 2010 - 04:30 AM

Quote:
Original post by SiS-Shadowman

*edit*
And just out of curiosity, why are you storing the boost::thread instance on the heap? You don't need to initialize a boost::thread in its constructor, so the following will work as well (and you don't have to call delete in your destructor):

class Whatever
{
boost::thread m_thread;

Whatever()
{
// some code
m_thread = boost::thread(boost::bind(&Whatever::operator(), this));
}
};



Does that actually work? Does boost::thread really provide the proper assigment / copying semantics for that to work?


#8 SiS-Shadowman   Members   -  Reputation: 359

Like
0Likes
Like

Posted 25 June 2010 - 04:43 AM

Yes. As far as I know this does a simple swap.
Documentation

I stumpled upon this by accident when I created the thread in an initializer list, and it started modifying my class before the constructor was finished.

#9 Mercenarey   Banned   -  Reputation: 103

Like
0Likes
Like

Posted 25 June 2010 - 05:05 AM

Thanks alot for the help guys. This has been hands-down my most severe problem to date. Took me two full days of work to finally crack.

GameDev to the rescue every time :)

#10 Red Ant   Members   -  Reputation: 447

Like
0Likes
Like

Posted 25 June 2010 - 01:38 PM

Quote:
Original post by SiS-Shadowman
Yes. As far as I know this does a simple swap.
Documentation


Well, that's why I'm asking. I've looked at the documentation and didn't find anything in there that says operator = invokes the swap() method. If it really does, then it appears to be an undocumented feature, which means it's probably best not to rely on it.



#11 larryp7639   Members   -  Reputation: 100

Like
0Likes
Like

Posted 25 June 2010 - 05:13 PM

Quote:
Original post by Red Ant
I'm suspecting that no matter how much you try to cheat by using boost::ref to pass a functor to boost::thread's constructor, internally it will still at some point make a copy of the functor, and when that happens, it won't have a clue that you're really passing a derived thread object and not a BaseThreadObject. Hence it will store a copy of the BaseThreadObject part of the class instance you're passing it. The result -> slicing (google it).


I suggest the following.


*** Source Snippet Removed ***

But beware that you may run into object life time issues if your BaseThreadObject instance gets deleted while your thread is still running (and hence potentially accessing your BaseThreadObject instance).


Thanks you for the post.

__________________
Watch Grown Ups Online Free

#12 GoldenArm   Members   -  Reputation: 149

Like
0Likes
Like

Posted 03 August 2013 - 01:02 PM

You guys are genius !!! Was struggling with getting my thread encapsulation done properly, that functor method in a struct of a base class did the job !!!

 

thanks






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS