Tuesday, August 14, 2007

Surviving after StackOverflowException

In one of my pet projects, there is a necessity to continue execution after StackOverflowException occurs in separate AppDomain. After quick scanning MSDN, I found out that I need to obtain CLRRuntimeHost, get CLRControl after that CLRPolicyManager and finally apply appropriate policy. Of cause you cannot do this trick, when you are inside managed code, so you have to write you own non-CLR c++ module, that apply corresponding policy and run you managed code. It was not clear, how to do this, but I found a good book with variety of samples: "Customizing the Microsoft® .NET Framework Common Language Runtime" by Steven Pratschner. Unfortunately neither of the sources address my particular case with StackOverflowException. I've tried to use SetActionOnFailure() with all documented EClrFailure values, but as soon as StackOverflowException was thrown, the whole process was killed. During my research I found sources of the Common Language Infrastructure (published by Microsoft under Shared Source license), and discovered that there is another undocumented value of EClrFailure called FAIL_StackOverflow. Based on the comment in file eepolicy.cpp in method EEPolicy::HandleStackOverflow() it appears that:

  • The only possible actions for FAIL_StackOverflow are eRudeUnloadAppDomain and eRudeExitProcess
  • eRudeUnloadAppDomain will be executed only if domain to be unloaded is non-default
After that everything works like a charm. Usually AppDomainUnloadedException is thrown to the calling code. Below is a snapshot of very simple CLRHost, that specifies action on failure and run specific method in assembly:

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR szCmd, int nCmd){
ICLRRuntimeHost *runtime;

HRESULT hr = CorBindToRuntimeEx(
L"v2.0.50727",
L"wks",
STARTUP_LOADER_OPTIMIZATION_SINGLE_DOMAIN,
CLSID_CLRRuntimeHost,
IID_ICLRRuntimeHost,
(void **)&runtime);

ICLRControl *pCLRControl = NULL;
runtime->GetCLRControl(&pCLRControl);

ICLRPolicyManager* pCLRPolicyManager = NULL;
hr = pCLRControl->GetCLRManager(IID_ICLRPolicyManager,
(PVOID*)&pCLRPolicyManager);

hr = pCLRPolicyManager->SetActionOnFailure(FAIL_StackOverflow,
eRudeUnloadAppDomain);

runtime->Start();

DWORD retVal = 0;
runtime->ExecuteInDefaultAppDomain(L"Test.exe",
L"Test.Program",
L"Start",
NULL,
&retVal);
return 0;
}

3 comments:

Daniel said...

Andrew,

Thanks very much.

This is the only article on the Internet that actually resolves my problem!

Regards,

Anton Tykhyy said...

Thanks a lot! Really helpful info!

lost said...

RuntimeHelpers.EnsureSufficientExecutionStack