Monday, April 23, 2007

RAII vs. exit()

A very popular programing technique in C++ is RAII: you have an automatic, i.e. stack local object and let its destructor do some cleanup which has to be done no matter what happens. Additionally within the constructor some initialisation might take place.

A typicall application for this technique is locking and unlocking a mutex with the help of a so called guard:
extern mutex global_lock;

static void f()
  boost::details::pool::guard<mutex> g(global_lock);
  // g's constructor locks "global_lock"

  ... // do anything:
      //   throw exceptions
      //   return
      //   or just fall through
} // g's destructor unlocks "global_lock"

I have experienced enough C++ to know that there is no rule without an exception. In this case the exception is the exit function which breaks the RAII concept. The ISO C++ standard says in section
Calling the function "void exit(int);" declared in <cstdlib> terminates the program without leaving the current block and hence without destroying any objects with automatic storage duration.
So, for instance, if you call exit() from within the scope of a guard object the mutex which is managed by this guard is not unlocked as the destructor of the guard object is not called. You could argue now that the program is going to exit anyway so who cares about a not-unlocked mutex? The problem is that calling exit does not quit the program immediately. ISO C++, section 12.4.10 says:
Destructors are invoked implicitly for a constructed object with static storage duration at program termination.
This means that if in the above example the mutex is a member of an object with static storage duration the destructor of the mutex is called while it is locked. If the destructor of the mutex class calls pthread_mutex_destroy you run into the following problem: (phtread_mutex_destroy manpage):
Attempting to destroy a locked mutex results in undefined behavior.
traps, traps, traps....

To make things worse the ISO C++ standard finishes section with:
If exit is called to end a program during the destruction of an object with static storage duration, the program has undefined behavior.
Thanks to Roker for digging out this issue.

No comments:

Post a Comment