Avoid Deadlocks in your Script Host when Tearing down JScript

Heath Stewart

A couple teams recently reported an issue where their script host hung due to deadlocks. Since jscript.dll was in the call stack for the hung thread it didn’t take long for the bug to wind up in our lap, since CPX also sustains Windows Script.

The problem was that as one thread was exiting and DLL_THREAD_DETACH was passed to the DllMain callback in jscript.dll, the JScript garbage collector was releasing objects that were not yet released. If any of these objects in their IUnknown::Release implementations or the destructors — when applicable — wait on other threads to be torn down, a deadlock occurs in the OS loader lock, which prevents multiple threads or processes from attempting to load DLLs at the same time. Read the article “Mixed DLL Loading Problem” for more details about possible deadlocks in the OS loader lock.

To resolve the issue, script hosts need a way to force the garbage collector to run before tearing down the host thread. It so happens that the JScript engine already implements such an API.

typedef enum tagSCRIPTGCTYPE {
	SCRIPTGCTYPE_NORMAL           = 0,
	SCRIPTGCTYPE_EXHAUSTIVE       = 1,
} SCRIPTGCTYPE;
[
	object,
	uuid(6AA2C4A0-2B53-11d4-A2A0-00104BD35090),
	pointer_default(unique)
]
interface IActiveScriptGarbageCollector : IUnknown
{
	HRESULT CollectGarbage(
		SCRIPTGCTYPE scriptgctype
	);
}

To force garbage collection you must call IUnknown::QueryInterface, or “QI”, for IID_IActiveScriptGarbageCollector — as defined in ActivScp.h in the Platform SDK — to get the IActiveScriptGarbageCollector interface, and then you can call CollectGarbage. Pass SCRIPTGCTYPE_NORMAL to release and reclaim marked objects as would normally happen when DLL_THREAD_DETACH is passed to DllMain, or pass SCRIPTGCTYPE_EXHAUSTIVE to first scavenge all roots to mark them for garbage collection and then reclaim them.

As Eric Lippert notes, you shouldn’t force garbage collection for no good reason. Even the .NET Framework SDK warns about forcing garbage collection when calling GC.Collect. Garbage collection can be expensive. JScript’s implementation of IActiveScriptGarbageCollector reclaims objects rooted in the current thread, so you must call it synchronously. To avoid deadlocks in your script host you might consider it, though.

You do not need to force garbage collection for VBScript, however. Not only does VBScript release every object immediately after an item goes out of scope or is otherwise discarded, but the VBScript engine doesn’t even implement IActiveScriptGarbageCollector. Read “How Do The Script Garbage Collectors Work?” for more information about the differences between the garbage collectors for JScript and VBScript.

0 comments

Discussion is closed.

Feedback usabilla icon