5

How do I safely delete static C++ objects in the case when multiple (Posix) threads call exit() in parallel?

It appears in my CentOS6 environment that exit() executes atexit (or on_exit) cleanup handlers by calling something like fct[--cnt]() where cnt is the number of handlers registered. When multiple threads call exit() at the same time, we have a race condition in the unprotected --cnt operation and some handlers may be skipped or called multiple times (leading to the occasional crash). So how can I ensure that just one of the exit() calling threads does the cleanup and all others stop? Note that inserting a pthread_mutex_lock() into a cleanup handler doesn't help because this handler might be skipped...

Unfortunately I can't avoid that multiple threads call exit() because that's code my users will write (I'm providing a library to them).

Looking for safe ideas, thanks!

12
  • 1
    What exactly are you worried about? Do you have something in your statically allocated objects that MUST be clenaed up (if so what)? It is generally safe to exit without cleanup in modern sophisticated OS's. If you HAVE to do cleanup, then I would suggest that you probably need to find a better solution than let your client code call exit - such as implement your code in a shared library, and use the shared library cleanup function to clean up, perhaps? Or document that "you must not call exit(), use my_safe_exit()", and then refer to that when customers complain that it doesn't work. Commented Nov 3, 2014 at 20:36
  • In addition to the above, what if your clients are crashing out (stack fault, seg fault) or debugging and exiting in the debugger without calling exit at all? Commented Nov 3, 2014 at 20:37
  • 1
    So let the cleanup do safe things like stream::flush(), but don't actively close it in the destructor? Commented Nov 3, 2014 at 22:12
  • 1
    This seems like a something you can't fix to me - other than #define exit() my_safe_exit() in some suitable place(s). Commented Nov 3, 2014 at 22:20
  • 3
    The C/C++ standard states that behavior is undefined if a program calls exit more than once. If the user is writing such code, it's their problem, and you can point them to the standard. However, if you are creating the threads within your library that my call exit, you'll need to properly clean up all your threads before exiting. Commented Nov 4, 2014 at 1:55

2 Answers 2

1

There is no portable way to handle multiple calls to exit() - because it is undefined (behaviour) what happens in that case.

But, for some particular platform you may find a way to do it. A somewhat generic solution to the "called multiple times" is to have a flag in your static objects like "am I already destructed". As usual, you may hide that in a template:

template <typename T> class StaticExitHandled {
public:
    std::unique_ptr<T> t_;
    ~StaticExitHandled() { t_.release(); }
};

Now just remember to declare all your static objects with this template. That is just the core of it, add bells and whistles as per your taste. Also, instead of std::unique_ptr<> you may use boost::optional<> or some such thing.

I don't think there is a generic solution to "not called at all".

Actually, I would advise against having non-trivial static objects in multi-threaded environment. So, only have static PODs and objects with severely constrained destructors (depending on what is safe to do at this point in your environment - i.e. closing file handles is OK in most environments).

Sign up to request clarification or add additional context in comments.

Comments

0

If you're using gcc, you can use the code below to define a cleanup procedure:

void __attribute__ ((destructor)) my_fini(void);

If this doesn't solve your problem, how about defining a single object with static duration whose destructor would take care of your cleanup?

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.