English 中文(简体)
Explicitly calling a destructor in a signal handler
原标题:

I have a destructor that performs some necessary cleanup (it kills processes). It needs to run even when SIGINT is sent to the program. My code currently looks like:

typedef boost::shared_ptr<PidManager> PidManagerPtr
void PidManager::handler(int sig)
{
  std::cout << "Caught SIGINT
";
  instance_.~PidManagerPtr();  //PidManager is a singleton
  exit(1);
}
//handler registered in the PidManager constructor

This works, but there seem to be numerous warnings against explicitly calling a destructor. Is this the right thing to do in this situation, or is there a "more correct" way to do it?

最佳回答

Turns out that doing this was a very bad idea. The amount of weird stuff going on is tremendous.

What was happening

The shared_ptr had a use_count of two going into the handler. One reference was in PidManager itself, the other was in the client of PidManager. Calling the destructor of the shared_ptr (~PidManager() ) reduced the use_count by one. Then, as GMan hinted at, when exit() was called, the destructor for the statically initialized PidManagerPtr instance_ was called, reducing the use_count to 0 and causing the PidManager destructor to be called. Obviously, if PidManager had more than one client, the use_count would not have dropped to 0, and this wouldn t have worked at all.

This also gives some hints as to why calling instance_.reset() didn t work. The call does indeed reduce the reference count by 1. But the remaining reference is the shared_ptr in the client of PidManager. That shared_ptr is an automatic variable, so its destructor is not called at exit(). The instance_ destructor is called, but since it was reset(), it no longer points to the PidManager instance.

The Solution

I completely abandoned the use of shared_ptrs and decided to go with the Meyers Singleton instead. Now my code looks like this:

void handler(int sig)
{
     exit(1);
}

typedef PidManager * PidManagerPtr
PidManagerPtr PidManager::instance()
{
    static PidManager instance_;
    static bool handler_registered = false;
    if(!handler_registered)
    {
        signal(SIGINT,handler);
        handler_registered = true;
    }
    return &instance_;
 }

Explicitly calling exit allows the destructor of the statically initialized PidManager instance_ to run, so no other clean up code need be placed in the handler. This neatly avoids any issues with the handler being called while PidManager is in an inconsistent state.

问题回答

If that object is a singleton, you don t need to use a shared-pointer. (There s only one!)

If you switch it to auto_ptr you can call release() on it. Or perhaps scoped_ptr, calling reset().

This all said, I m 99% certain that exit() will destruct statically constructed objects. (Which singletons tend to be.) What I do know is that exit() calls the registered atexit() functions.

If your singleton is not destructed automatically by exit, the proper thing to do in your case is to make an atexit hook:

void release_singleton(void)
{
    //instance_.release();
    instance_.reset();
}

// in main, probably
atexit(release_singleton);

Never explicitly call destructor unless object was constructed with placement new. Move cleanup code into separate function and call it instead. The same function is to be called from the destructor.

You really don t want to do much of anything in a signal handler. The safest the thing to do is just set a flag (e.g. a global volatile bool), and then have your program s regular event loop check that flag every so often, and if it has become true, call the cleanup/shutdown routine from there.

Because the signal handler runs asynchronously with the rest of the application, doing much more than that from inside the signal handler is unsafe -- whatever data you might want to interact with might be in an inconsistent state. (and you re not allowed to use mutexes or other synchronization from a signal handler, either -- signals are pretty evil that way)

However, if you don t like the idea of having to poll a boolean all the time, one other thing you can do from within a signal handler (at least on most OS s) is send a byte on a socket. So you could set up a socketpair() in advance, and have your normal event loop select() (or whatever) on the other end of the socket pair; when it receives a byte on that socket, it knows your signal handler must have sent that byte, and therefore it s time to clean up.

One other way could be to have the singleton dynamically allocated (on first use or in main), and delete it for cleanup.

Eh. I guess your PidManagerPtr actually points to a dynamically allocated object ... But doesn t boost::shared_ptr actually clean up on reallocation? So it should be enough to:

instance_ = 0;

?

Just call reset() on the shared_ptr and it ll remove your instance for you.





相关问题
Undefined reference

I m getting this linker error. I know a way around it, but it s bugging me because another part of the project s linking fine and it s designed almost identically. First, I have namespace LCD. Then I ...

C++ Equivalent of Tidy

Is there an equivalent to tidy for HTML code for C++? I have searched on the internet, but I find nothing but C++ wrappers for tidy, etc... I think the keyword tidy is what has me hung up. I am ...

Template Classes in C++ ... a required skill set?

I m new to C++ and am wondering how much time I should invest in learning how to implement template classes. Are they widely used in industry, or is this something I should move through quickly?

Print possible strings created from a Number

Given a 10 digit Telephone Number, we have to print all possible strings created from that. The mapping of the numbers is the one as exactly on a phone s keypad. i.e. for 1,0-> No Letter for 2->...

typedef ing STL wstring

Why is it when i do the following i get errors when relating to with wchar_t? namespace Foo { typedef std::wstring String; } Now i declare all my strings as Foo::String through out the program, ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

Window iconification status via Xlib

Is it possible to check with the means of pure X11/Xlib only whether the given window is iconified/minimized, and, if it is, how?

热门标签