[D-runtime] DLL initialization in Druntime

Sean Kelly sean at invisibleduck.org
Mon Jan 17 15:43:35 PST 2011


On Jan 17, 2011, at 12:34 PM, Walter Bright wrote:
> 
> Sean Kelly wrote:
>> On Jan 16, 2011, at 9:54 AM, Rainer Schuetze wrote:
>> 
>>   
>> 
>>> Hi,
>>> 
>>> I have not updated to the latest version yet, but it seems that it's the first time that gc_addRange is called during initialization, and this hits the bug in the gc stub code (obviously, the return statements are missing for the "proxy is null" case).
>>> 
>>> Even if fixed, the proxy has the problem that anything that has been allocated until the proxy is switched, uses a different heap, because the C-Runtime is not shared between the DLLs. This will cause problems when trying to scan/collect objects.
>>>     
>>> 
>> 
>> Allocating memory from the GC before initializing the runtime results in undefined behavior.  If you're using gcstub though, the only thing that won't work is that allocated blocks won't be scanned for roots by the new GC, but this behavior isn't expected anyway.  The new GC doesn't own the memory allocated by the old GC.  Also, The roots and ranges are already transferred to the new GC. (see gc_setProxy), so the static data segment of the DLL will be scanned by the app's GC.
>>   
>> 
> 
> Here's the code for rt_init(), which is called by LoadLibrary(), i.e. using gcstub instead of the gc. It calls _moduleCtor() and _moduleTlsCtor(). Therefore, any memory allocated by any static constructors in the DLL will not be scanned for roots.
> 
> extern (C) bool rt_init(ExceptionHandler dg = null)
> {
>     _d_criticalInit();
> 
>     try
>     {
>         gc_init();
>         initStaticDataGC();
>         version (Windows)
>             _minit();
>         _moduleCtor();
>         _moduleTlsCtor();
>         runModuleUnitTests();
>         return true;
>     }
>     catch (Throwable e)
>     {
>         if (dg)
>             dg(e);
>         else
>             throw e;    // rethrow, don't silently ignore error
>     }
>     _d_criticalTerm();
>     return false;
> }

Darnit, you're right.  So the ideal scenario would be if the GC proxy could be set before the DLL load event was fired, which I think is impossible.  I also think it's important that a D DLL be usable by both a D and a non-D host app without a recompile, so we can't rely on gc_setProxy() ever being called (which is why I'm not terribly fond of gcstub to begin with).  This problem applies to any GC in the DLL though, so it still needs a solution.

I can think of two solutions, though neither are terribly appealing.  The first would be to require that D DLLs not create threads in their module ctors, and have gc_setProxy() call the module dtors, set the proxy, then re-construct the modules.  The second would be for gc_setProxy() to suspend all threads in the DLL, have the GC pass all of its allocated blocks as roots/ranges to the proxy, and then restart threads in the DLL.  The second seems far more robust, but imposes a rather large functionality requirement on the GC (a pseudo-collection).

Thoughts?


More information about the D-runtime mailing list