November 12, 2008

Stack unrolling for exceptions in managed/unmanaged C++ code

Trying to mix managed and unmanged C++ is not pleasant. At my current job we have been re-architecting some of our core business logic. One of the focuses has been to make sure the code is set up to behave well with our MySQL database using transactions.

To facilitate this we have set up an object to help manage and maintain the transactions. If the code throws an exception, the transaction object automatically gets destructed as the stack unrolls, causing a rollback. This had been tested and was working great in our unmanaged code.

One of the GUI applications we maintain runs in managed C++ mode, but still uses our underlying unmanaged C++ business logic. While testing this application we found that when the business logic would throw an exception, the code was not properly rolling back transactions in the database. After hours and hours of debugging and research we determined that the Microsoft compiler doesn't seem to unroll the stack properly when crossing the managed/unmanged code boundary. We found posts indicating other bugs with this across this boundary, some of which had been fixed in Visual Studio. The only way we were able to get the code to behave properly, was to wrap every function that uses database transactions and is called directly from the managed code in a try-catch block, that just rethrows the exception. Like this:
void DatabaseTransactionFunction()
{
try
{
TransactionHelper t();
t.Start();
//Code that may throw an exception
t.Commit();
}
catch (std::exception&)
{
//This forces the destructor on t to be called
//which rolls back the database transaction.
throw;
}
}
This is the only way the code would work. Seems unnecessary, and the code works fine if it is called by unmanaged code. So that seems to leave something with the Visual Studio compiler. Everything I've seen and read indicates that the compiler should behave like we think, The destructor on object t should be called when an exception is thrown, which would roll back the database transaction.

I would make the argument that Microsoft should fix these things, but all of the other issues I've seen with using managed and unmanged c++ code together would just make me recommend avoiding it. Use C++ for unmanged applications and stick with C# for unmanged code, and don;t mix them.

1 comment:

A Growing Coder said...

Yeah, it is a bug of visual studio 2005 and 2008 and still seems not having been fixed until now.

You can try to change the exception option of your project from /EHc to /EHa, the latter is the default for managed projects but not for unmanaged. After that, the exceptions thrown from native blocks can guarantee stack unrolling correctly.