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