5

Recently I am following the tutorials on rastertek and find that they suggest use a Shutdown() method for cleaning up instead of the class own destructor.The reason they mention is that the destructor is not guaranteed to be executed when calling some unsafe function like ExitThread().

However, I doubt if that method would get executed when even the destructor cannot be called. Indeed you can always call Shutdown() before you call ExitThread() but why not the same for the destructor? If I can do something before calling ExitThread(), I can certainly call the destructor as well.

Isn't placing the clean up code in the destructor more or less safer than using another method to do the trick? I know that releasing some vital resources like closing a file may need this separate method to do the trick. But are there any reasons other than that since this does not seem to be the case in the tutorials?

For the record, I know there is a similar question out there. But that one got no answer.

Community
  • 1
  • 1
EternalWind
  • 137
  • 9
  • 1
    To put it in simple words: If you're dealing with `C` code, you have practically no other option. If it's pure `C++` code, kill it with fire! – stefan Mar 12 '14 at 16:24
  • The point with a "shutdown" or "cleanup" function is to destroy the objects you have created, thereby letting their destructor run. So it's not a choice of *either* a cleanup function *or* destructors, it's *both* cleanup function *and* destructors. And yes it's a good idea if you are not leaving a thread by just returning from the threads top function. – Some programmer dude Mar 12 '14 at 16:27
  • @stefan: If you're dealing with C code, then write a RAII wrapper for it. – Mike Seymour Mar 12 '14 at 17:01
  • @MikeSeymour The other way round: If C code needs to handle C++ code, there is no way around manual cleanup. (E.g. for supporting dynamically linked libraries on linux) – stefan Mar 12 '14 at 18:03
  • @stefan How about using C code within C++ code which is the case of the tutorials? I cannot see the absolute need for manual cleanup. – EternalWind Mar 13 '14 at 01:56
  • possible duplicate of [Cleaning up in Shutdown() method instead of destructor](http://stackoverflow.com/questions/21505014/cleaning-up-in-shutdown-method-instead-of-destructor) – BЈовић Mar 13 '14 at 07:49
  • @BЈовић I am aware of that but by the time I posted my question that question still had no answer yet. – EternalWind Mar 13 '14 at 09:16
  • @EternalWind As Mike said: Dealing with `C` inside `C++` is ugly, but easy as you can build an RAII wrapper. – stefan Mar 13 '14 at 09:30

4 Answers4

4

Isn't placing the clean up code in the destructor more or less safer than using another method to do the trick?

The problem here is that while ExitThread (and other functions like it) is a perfect API for C, with C++ code, it breaks stack unwinding.

The correct solution for C++ is to make sure you do not call ExitThread (and such) in code using anything with destructors.

Problem:

void thread_function()
{
    raii_resource r { acquire_resource() };
    ExitThread();
    // problem: r.~raii_resource() not called
}

The shutdown solution:

void thread_function()
{
    raii_resource r { acquire_resource() };
    r.shutdown(); // release resources here
    ExitThread();
    // r.~raii_resource() still not called
}

The shutdown solution is not obvious at all in client code. As @stefan said, kill it with fire.

Better solution (than the Shutdown thing):

void thread_function()
{
    { // artificial scope where RAII objects live
        raii_resource r { acquire_resource() };
    }
    // this space does not support RAII life
    ExitThread();
}

RAII works fine here, but the artificial scope is not very elegant. On top, it's as inelegant as the shutdown solution (it requires a non-obvious artifice in client code).

Better (cleaner) solution:

template<typename F>
void run_thread(F functor)
{
    functor(); // all RAII resources inside functor; this is simple and 
               // obvious from client code
    ExitThread();
}
utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • Thanks for the reply. Could you explain a bit about the functor solution? Which one is supposed to be the client code? The functor, run_thread() itself or the caller of run_thread()? Besides, under such situation, are there any reasons to use Shutdown() over destructor at all? – EternalWind Mar 13 '14 at 01:37
2

The only advantage to moving initialization out of the constructor, and for removing cleanup out of the destructor is when you've got a base class framework where you want to reliably call virtual methods during these stages.

Since the vtable is changing during construction/destruction calls to virtual functions don't resolve to the most derived instance. By having explicit Initialize/Shutdown methods you can be sure the virtual functions dispatch correctly.

Please note, this isn't an answer that advocates this approach, just one that is trying to work out why they've suggested it!

Sean
  • 60,939
  • 11
  • 97
  • 136
0

destructors are guaranteed to be called when an object is destroyed however thread clean up can require a tad more than just object destruction. Generally speaking cleanup methods are added when you need to handle releasing of shared resources, dealing with ancient libraries, etc.

Specifically you're dealing with the Win32 API which qualifies as an ancient c-style library considering ExitThread has been around longer than I have ...

AJG85
  • 15,849
  • 13
  • 42
  • 50
  • But isn't that I can always make the destructor get called before I call such unsafe APIs?(For example, by using delete) What's the necessity for the Shutdown method to exist? – EternalWind Mar 13 '14 at 01:42
0

With such approach you'll need to call Shutdown in all cases where the object should be destroyed - each time when it leaves the scope. In the case of exceptions (if they are used) you'll cannot call it. Destructor will be called automatically in these cases.

"I can certainly call the destructor as well" - calling the destructor explicitly is highly not recommended because it will be called automatically in any case. This should be avoided except only in special cases. If you mean this code from the tutorial:

System->Shutdown();
delete System;

then I don't see the difference because delete System; will call the destructor anyway.

In any case I would prefer

{
    // work with System
    ...
}

mentioned in @utnapistim answer. I do not see any minuses in such way of coding, and also it is common way to specify scope. Even not going deep in the details of legacy ::ExitThread you gain auto cleanup. It is possible to work with WinApi with C++ RAII technique, look for instance at my code: multithreading sample project. Initial commit was bare WinAPI, and next commit introduced resource wrappers. You can compare both variants - second is much clearer, imho.

Spock77
  • 3,256
  • 2
  • 30
  • 39
  • By saying "call the destructor myself", I mean something like this: delete ptr of which of course it is better to use a smart pointer instead. I just wanted to express that if there is the circumstance that the destructor would not be called automatically which the tutorial suggests as the reason to use the Shutdown method, then why not just call the destructor explicitly instead. That's why I want to know the reasons to use Shudtown method over the destructor. – EternalWind Mar 13 '14 at 01:51
  • @EternalWind - I expanded the answer. But be careful with expressions, I understood what you mean but in this case would be better "delete the object my myself" - this is what you've really wrote. "Calling destructor by myself" likely means explicit `obj.~System();` call in the program. – Spock77 Mar 13 '14 at 03:00