www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - rt_init, rt_term and _initCount

reply Benjamin Thaut <code benjamin-thaut.de> writes:
This is a topic really specific to druntime, I don't know a 
better place to put it though.

rt_init increases the _initCount and rt_term decreases it and 
only terminates the runtime in case the _initCount reaches zero 
(see dmain2.d)

The problem now is as follows.
Each dynamic library that is loaded (in this case a .dll on 
windows) must ensure that druntime is intialized. This must be 
done to ensure that d dynamic libraries work with C processes 
that load them. So lets assume we have a exe and two dlls: DllA 
and DllB. The exe uses the two dlls and druntime. What will 
happen is the following


1) Exe starts up
2) The windows loader will load DllA. This will call rt_init 
_initCount will be 1. Druntime will be initialized. Module ctors 
of druntime will be called. Module Ctors of DllA will be called.
3) The windows loader will load DllB. _initCount will be 2. 
Module Ctors of DllB will be called.
4) The dmain function will be execued. It will call rt_init. 
_initCount is 3. Module ctors of exe will be called.
5) Dmain finishes and calls rt_term. _initCount is 2. Nothing 
happens
6) The windows loader will unload DllB. rt_term is called. 
_initCount is 1. DllB is forced to call its module dtors.
7) The windows loader will unload DllA. r_term is called. 
_initCount is 0. Module Dtors of exe will be called. Module Dotrs 
of DllA will be called. Module Dtors of druntime will be called.

As might observed the issue is the order in which the module 
dtors are called. The order is

Druntime Ctor
DllA Ctor
DllB Ctor
ExE Ctor
DllB Dtor
Exe Dtor
DllA Dtor
Druntime Dtor

Whereas it should be:

Druntime Ctor
DllA Ctor
DllB Ctor
ExE Ctor
Exe Dtor
DllB Dtor
DllA Dtor
Druntime Dtor

The problem is step 5) in the above list. When the main 
executable leaves dmain the runtime should be deinitialized no 
matter if any dll is still loaded or not. If this would be the 
case the module Dtors would be calle din the correct order. I 
can't remove the rt_init calls from the dll loading code however 
because that would mean when loading a d-dll into a C process 
druntime would never be initialized. So I'm thinking of adding a 
force parameter to rt_term which will always deinitialize 
druntime disregarding the _initCount. This feels like a hack 
though.

Any suggestions how to solve this problem? Who are other 
platforms doing it?

Kind Regards
Benjamin Thaut
Oct 20 2016
next sibling parent reply Ethan Watson <gooberman gmail.com> writes:
On Thursday, 20 October 2016 at 07:17:49 UTC, Benjamin Thaut 
wrote:
 Any suggestions how to solve this problem? Who are other 
 platforms doing it?
Would this also be a bigger problem if people use LoadLibrary and don't FreeLibrary after?
Oct 20 2016
parent Benjamin Thaut <code benjamin-thaut.de> writes:
On Thursday, 20 October 2016 at 08:44:09 UTC, Ethan Watson wrote:
 On Thursday, 20 October 2016 at 07:17:49 UTC, Benjamin Thaut 
 wrote:
 Any suggestions how to solve this problem? Who are other 
 platforms doing it?
Would this also be a bigger problem if people use LoadLibrary and don't FreeLibrary after?
No. This case actually works correctly. No worries I have your use case covered ;-)
Oct 20 2016
prev sibling next sibling parent Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Thursday, 20 October 2016 at 07:17:49 UTC, Benjamin Thaut 
wrote:
 This is a topic really specific to druntime, I don't know a 
 better place to put it though.

 rt_init increases the _initCount and rt_term decreases it and 
 only terminates the runtime in case the _initCount reaches zero 
 (see dmain2.d)

 The problem now is as follows.
 Each dynamic library that is loaded (in this case a .dll on 
 windows) must ensure that druntime is intialized. This must be 
 done to ensure that d dynamic libraries work with C processes 
 that load them. So lets assume we have a exe and two dlls: DllA 
 and DllB. The exe uses the two dlls and druntime. What will 
 happen is the following


 1) Exe starts up
 2) The windows loader will load DllA. This will call rt_init 
 _initCount will be 1. Druntime will be initialized. Module 
 ctors of druntime will be called. Module Ctors of DllA will be 
 called.
 3) The windows loader will load DllB. _initCount will be 2. 
 Module Ctors of DllB will be called.
 4) The dmain function will be execued. It will call rt_init. 
 _initCount is 3. Module ctors of exe will be called.
 5) Dmain finishes and calls rt_term. _initCount is 2. Nothing 
 happens
 6) The windows loader will unload DllB. rt_term is called. 
 _initCount is 1. DllB is forced to call its module dtors.
 7) The windows loader will unload DllA. r_term is called. 
 _initCount is 0. Module Dtors of exe will be called. Module 
 Dotrs of DllA will be called. Module Dtors of druntime will be 
 called.

 As might observed the issue is the order in which the module 
 dtors are called. The order is

 Druntime Ctor
 DllA Ctor
 DllB Ctor
 ExE Ctor
 DllB Dtor
 Exe Dtor
 DllA Dtor
 Druntime Dtor

 Whereas it should be:

 Druntime Ctor
 DllA Ctor
 DllB Ctor
 ExE Ctor
 Exe Dtor
 DllB Dtor
 DllA Dtor
 Druntime Dtor

 The problem is step 5) in the above list. When the main 
 executable leaves dmain the runtime should be deinitialized no 
 matter if any dll is still loaded or not. If this would be the 
 case the module Dtors would be calle din the correct order. I 
 can't remove the rt_init calls from the dll loading code 
 however because that would mean when loading a d-dll into a C 
 process druntime would never be initialized. So I'm thinking of 
 adding a force parameter to rt_term which will always 
 deinitialize druntime disregarding the _initCount. This feels 
 like a hack though.

 Any suggestions how to solve this problem? Who are other 
 platforms doing it?

 Kind Regards
 Benjamin Thaut
I don't know anything about the druntime code but the solution seems to look like rt_term() should kick the call to the destructor when the initcound matches with the initcount it had when it called rt_init(). But as I already said, I'm talking out of my ass here as I never even looked in the code.
Oct 20 2016
prev sibling parent reply Rainer Schuetze <r.sagitario gmx.de> writes:
On 20.10.2016 09:17, Benjamin Thaut wrote:
 This is a topic really specific to druntime, I don't know a better place
 to put it though.

 rt_init increases the _initCount and rt_term decreases it and only
 terminates the runtime in case the _initCount reaches zero (see dmain2.d)
[...]
 The problem is step 5) in the above list. When the main executable
 leaves dmain the runtime should be deinitialized no matter if any dll is
 still loaded or not. If this would be the case the module Dtors would be
 calle din the correct order. I can't remove the rt_init calls from the
 dll loading code however because that would mean when loading a d-dll
 into a C process druntime would never be initialized. So I'm thinking of
 adding a force parameter to rt_term which will always deinitialize
 druntime disregarding the _initCount. This feels like a hack though.

 Any suggestions how to solve this problem? Who are other platforms doing
 it?
I don't think the current rt_init/rt_exit function are suitable for DLLs or executables that are using a shared druntime library. The wrapper around main in the executable should work just as any DLLs' DllMain, i.e. it should register/unregister its own data segments with the GC and run its shared and TLS module constructors/decoontructors. Everything else should be handled by the druntime DLL.
Oct 21 2016
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
On Friday, 21 October 2016 at 19:40:52 UTC, Rainer Schuetze wrote:
 The wrapper around main in the executable should work just as 
 any DLLs' DllMain, i.e. it should register/unregister its own 
 data segments with the GC and run its shared and TLS module 
 constructors/decoontructors. Everything else should be handled 
 by the druntime DLL.
Yes, I also considered that option. The problem with that is, that druntime deinit joins alls threads before running the shared module dtors. If the main executable behaves exactly like the dlls do this joining of threads would be lost and different from the behavior of a fully statical linked executable.
Oct 21 2016
parent reply Rainer Schuetze <r.sagitario gmx.de> writes:
On 22.10.2016 01:21, Benjamin Thaut wrote:
 On Friday, 21 October 2016 at 19:40:52 UTC, Rainer Schuetze wrote:
 The wrapper around main in the executable should work just as any
 DLLs' DllMain, i.e. it should register/unregister its own data
 segments with the GC and run its shared and TLS module
 constructors/decoontructors. Everything else should be handled by the
 druntime DLL.
Yes, I also considered that option. The problem with that is, that druntime deinit joins alls threads before running the shared module dtors. If the main executable behaves exactly like the dlls do this joining of threads would be lost and different from the behavior of a fully statical linked executable.
The main executable wrapper could add this join-all if it is considered useful. I'm not really convinced: If there are actually threads still running on program termination, it's likely they need some signal to stop. In C++ that is usually fired in the shared destructor of some global. This is currently not possible in D as these are run after joinAll. Please also consider that the main executable might not know about DLLs being written in D. In that case all termination must be triggered by the druntime DLL.
Oct 22 2016
parent Benjamin Thaut <code benjamin-thaut.de> writes:
On Saturday, 22 October 2016 at 07:12:48 UTC, Rainer Schuetze 
wrote:
 Please also consider that the main executable might not know 
 about DLLs being written in D. In that case all termination 
 must be triggered by the druntime DLL.
The case where the main executable is not D is working fine. The problem at the moment is the case where the main executable is D. So I will be going for the dll registration approach then. As you said we can add in the thread join in case it becomes a problem.
Oct 22 2016