2

I have a base class IRunnable with a protected constructor, so that no instances of the base class can be created. The class has a pure virtual method run(), which has to be implemented by derived classes. I am passing pointers of IRunnable * to some kind of java-like Executor, which controls a number of threads, and assigns those Runnables to them. My Problem is, when such a IRunnable * pointer, pointing to an object of a derived class of IRunnable, which lies on a stack of another Thread, is assigned to a worker Thread, i can not be sure, that the derived Object is not destroyed while the worker thread is still using it, since the thread that has the object on its thread could leave the scope where the object was created. For Example:

int main(void)
{
    CExecutor myExecutor(...); 
    for (UInt32 i = 0; i < 2; i++)
    {
        derivedRunnable myRunnable1; //will be destroyed after each loop iteration
        myExecutor.submit(myRunnable1);
    }
}

class derivedRunnable : public IRunnable
{
public:
    derivedRunnable(const char * name = NULL) : IRunnable(name) {}
    ~derivedRunnable() {}
    void run(void)
    {
        for (UInt32 i = 0; i < 100; i++)
        {
            char name [256] = {"\0"};

            pthread_getname_np(pthread_self(), name, 255);
            printf("%s in step %d\n", name, i);
        }
        fflush(stdout);
    }
};

I implemented a reference count in the base class IRunnable, and I do a blocking call in the destructor that will only come back when the last thread using it unregisters with it. The problem is, the destructor of the derived class get's called first, so the object will be partially destructed before base class destructed with the blocking call is called. In the above example, i get the following runtime error:
pure virtual method called
terminate called without an active exception If I insert a usleep of some usec after the .submit() call, it will work, because the thread will be finished with the runnable, before it gets destroyed

class IRunnable
{
    friend class CThread;
    friend class CExecutor;
private:
    CMutex mutx;
    CBinarySemaphore sem;
    UInt32 userCount;
    [...]
    virtual void run(void) = 0;
    IRunnable(const IRunnable & rhs); //deny
    IRunnable & operator= (const IRunnable & rhs); //deny

    void registerUser(void)
    {
        mutx.take(true);
        if (0 == userCount++)
            sem.take(true);
        mutx.give();
    }

    void unregisterUser(void)
    {
        mutx.take(true);
        if (0 == --userCount)
            sem.give();
        mutx.give();
    }

protected:
    IRunnable(const char * n = NULL)
    :mutx(true,false)
    ,sem(true,false)
    ,userCount(0)
    {
        setName(n);
    }

    ~IRunnable()
    {
        sem.take(true);
    }
    [...]

What could I do?

user2950911
  • 873
  • 3
  • 12
  • 19

3 Answers3

2

If you block in the destructor, then your submit loop won't be asynchronous, which is the point of multithreading. The lifetime has to continue past the allocation scope. To do this, you should allocate your IRunnable objects dynamically, and let your Executor take ownership. The Executor will be in charge of deleting it when the job is complete.

Even better, use shared pointers for this interface. That way the calling thread can still refer to the object (for instance to check completion). The last thread finished will delete the object.

EDIT: adding sample code:

for (UInt32 i = 0; i < 2; i++)
{
    shared_ptr< derivedRunnable > myRunnable1(new derivedRunnable);
    myExecutor.submit(myRunnable1);
}

Or saving the tasks:

list < shared_ptr < derivedRunnable > > jobs;
for (UInt32 i = 0; i < 2; i++)
{
    shared_ptr< derivedRunnable > myRunnable(new derivedRunnable);
    myExecutor.submit(myRunnable);
    jobs.push_back(myRunnable);
}
// do other stuff
// call a status function that you haven't written yet:
jobs.front()->WaitForCompletion();

By the way, you should also consider switching either to std::thread or boost::thread, depending on the vintage of your compiler.

Peter
  • 14,559
  • 35
  • 55
  • I'm aware that objects allocated on the heap will not be destroyed until an explicit delete is called, but this doesnt solve my problem. What exactly do you mean by shared pointers? – user2950911 Dec 20 '13 at 23:17
  • Why doesn't it solve your problem? The object won't be deleted until the worker is finished? – Peter Dec 20 '13 at 23:18
  • well, I'd rather have a solution that does not rely on the programmer to use dynamic allocation only, I'd like it to work with objects on the stack as well. You are of course right, that my blocking destructor will make the given example synchronous, but that is totally fine with me, the example is just to show a case where the error occurs, not to show a case how to properly use the executor – user2950911 Dec 20 '13 at 23:29
  • I come to the conclusion that what I intentionally wanted to have is simply not possible. Heap Allocation and passing of ownership, or using shared_pointers is necessary, although I do absolutely not like it. Java does that too, with its totally annoying garbage collector that periodically halts your program to clean up. – user2950911 Dec 20 '13 at 23:56
2

Your problem is that it is not clear the ownership of the object. From your example it seems clear that the object belongs to the thread, so you should pass ownership, and the thread should destroy it. And for that the IRunnable destructor has to be public. Actually there is no reason for it not to be public.

Ah! And the objects should be created dynamically:

derivedRunnable *myRunnable1 = new derivedRunnable();
myExecutor.submit(myRunnable1);

And the submit function, of course receives a pointer and passes it to the thread. When the thread is done with the runnable, it will destroy it:

void threadfunc(IRunnable *runnable)
{
    //run it
    delete runnable;
}

There are many more sofisticated solutions, but this is the simplest. My favourite, if C++11 is an option, is to use std::unique_ptr<IRunnable>. That way the destruction of the object is automatic. Or if you need to keep the runnable in the main thread, you can use std::shard_ptr<IRunnable>: automatic destruction and shared ownership.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
1

IRunnable needs to have a virtual destructor so that the right destructors are called in the right order.

thedaver64
  • 179
  • 1
  • 1
  • 5
  • Though I am actually not 100% sure what the effect of a virtual destructor is, i tried it, and the problem remains – user2950911 Dec 20 '13 at 23:09
  • This question answers as to why. Specifically why your code is still crashing I don't know. I glanced through it and noted that you don't have a virtual deconstructor in a class you're deriving other classes from. http://stackoverflow.com/questions/461203/when-to-use-virtual-destructors?lq=1 – thedaver64 Dec 20 '13 at 23:11
  • From your link i found a comment stating that ´virtual´ makes sure the destruction chain starts at the top instead of the bottom. I think this would solve my problem. I added a printf do the base class' and derived class' destructor, to see in which order they are called, but no matter if virtual or not, the order is always 1. derived 2. base - am I doing it wrong? – user2950911 Dec 20 '13 at 23:23
  • The derived destructor will get called first, then the base, because base must remain valid while the derived classes bits still exist. So for your semaphore to have worked, I guess it would have needed to be in the derived classes' destructor. – Rob Dec 20 '13 at 23:35