views:

84

answers:

3

In many cases I have classes that act like active objects (have a thread). And to avoid access violations I always have to wait for join in the destructor. Which is usually not a problem.

However imagine a release build with some bug (deadlock, livelock etc.) that causes join() not to return on time or at all, this would cause the entire application to become non functional while waiting for an object that will not be used again. If this happens at a customer it becomes a problem.

I would rather be notified by the problem and skip the join. And leak the thread and it's resources.

Skipping the join could be achieved through something like this.

   class MyActiveObject
    {
    public:
        MyActiveObject();
        ~MyActiveObject(){}
    private
        struct Implementation;
        std::shared_ptr<Implementation> pImpl_;
    };

    struct MyActiveObject::Implementation : std::enable_shared_from_this<Implementation >
    {
        Implementation() : thread_([=]{Run();})
        {
        }

        ~Implementation()
        {
            #ifdef _DEBUG
                thread_.join();
            #else
                if(!thread_.timed_join(SOME_TIMEOUT))
                    ALERT_SOME_HOW();
            #endif
        }

        void Dispose()
        {
            isRunning_ = false;
        }

        void Run()
        {
        #ifndef _DEBUG
            auto pKeepAlive = shared_from_this(); // Won't be destroyed until thread finishes
        #endif   
            isRunning_ = true;
            while(isRunning_)
            {
                 /* ... */
            }
        }

        boost::thread thread_;
        tbb::atomic<bool> isRunning_;
    };

MyActiveObject::MyActiveObject() : pImpl_(new Implementation()){}
MyActiveObject::~MyActiveObject() { pImpl_->Dispose(); }

Is this a good idea? Or are there better strategies?

+1  A: 

If you are writing a client application and the lifetime of the application is short after this destruction sequence then it seems a reasonable pragmatic solution. I would encourage you to add some logging and to attempt to collect release logs (at least from your internal testing).

If you are writing a server application and this destruction is not during the shutdown of the server then I would discourage this behaviour as your server frequently running out of resources is unlikely to be good.

In both cases any known deadlock issues should be chased with a high priority by the development team - even if the effect is hidden from the customer.

morechilli
+1  A: 

It would be better, if feasible, to activate some kind of structure analysis to find the cycle and break the deadlock.

It might miss some deadlocks on destruction, but then again it might also be applicable to catch deadlocks outside that context.

Potatoswatter
Would you care to be a bit more detailed regarding "some kind of structure analysis"?
ronag
@ronag: If you have two thread objects that aren't terminating and have obvious pointers to each other, you could try to detect that and do something. It depends on a lot.
Potatoswatter
+3  A: 

Odd question: 'I have a problem if I have a bug in my code'. Well, yes. Fix the bug, don't try to paper it over. That kind of paper only ever produces two bugs because you can't test it until you know what the first bug is.

Hans Passant
+1 - you are going to have issues anyway if your threads are leaving locks lying around. Fix the real issue
pm100
Altough i agree with you. There is still the problem of certain kind of bugs (especially when talking multi threading) are not always detected during testing.
ronag
Well, don't do too much, yell and crash loudly so you'll know.
Hans Passant
This kind of timer is just what's needed to start yelling.
Potatoswatter
@ronag: the issue isn't whether or not the bugs are detected during testing, but rather that when they occur,your application isn't going to work correctly *no matter what* you do to work around it and paper it over. If you have a lock that's never released, it's going to cause havoc whether or not you call `join` on the thread.
jalf