In VC++ 8.0, while code compiled with /clr or /clr:pure, static destructors sometimes would not being properly called before process exites in multiple threads.
CRT incorrectly set a lock at _global_unlock which resulted in such issue.
In CLR-mixed mode, during the inialization of static local object, CRT would call _atexit_m(_CPVFV func) in msilexit.cpp to register a special __clrcall callback function which would be called back to destroy such static object when the current AppDomain quited.
In the multithread environment, _atexit_helper which was invoked by _atexit_m, could register such callbace function successfully because it had been guarded by __global_lock() and __global_unlock(). But in the same environment, the _atexit_m would fail to assign the correct value to __onexitbegin_m and __onexitend_m.
__onexitbegin_m and __onexitend_m were shared by the different threads; It's the key point of such issue. For example, the following statements,
__onexitbegin_m = (_CPVFV *)_encode_pointer(onexitbegin_m);
__onexitend_m = (_CPVFV *)_encode_pointer(onexitend_m);
should also guarded by __global_lock() and __global_unlock() or other syn primitives.
__global_lock();
__onexitbegin_m = (_CPVFV *)_encode_pointer(onexitbegin_m);
__onexitend_m = (_CPVFV *)_encode_pointer(onexitend_m);
__global_unlock();
extern "C" int __clrcall _atexit_m(_CPVFV func)
{
MANAGED_ASSERT(AppDomain::CurrentDomain->IsDefaultAppDomain(), "This fuction must be called in the default domain");
__global_lock();
_CPVFV* onexitbegin_m = (_CPVFV*)_decode_pointer(__onexitbegin_m);
_CPVFV* onexitend_m = (_CPVFV*)_decode_pointer(__onexitend_m);
__global_unlock();
int retval = _atexit_helper((_CPVFV)_encode_pointer(func), &__exit_list_size, &onexitend_m, &onexitbegin_m);
__global_lock();
__onexitbegin_m = (_CPVFV*)_encode_pointer(onexitbegin_m);
__onexitend_m = (_CPVFV*)_encode_pointer(onexitend_m);
__global_unlock();
return retval;
}