www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - module initialization order issue

reply Benjamin Thaut <code benjamin-thaut.de> writes:
When modules are initialized, druntime will first run the module 
initializers before running the module tls initializers ignoring the 
dependencies between the modules. This will lead to a access of a 
unitialized module if the module ctor of one module uses tls data of 
another module. Example:

module a:

class TLSStuff
{
   public int i;
}

TLSStuff g_tlsStuff;

static this()
{
   g_tlsStuff = new TLSStuff();
}

module b:
import a;

shared static this()
{
   g_tlsStuff.i++; //null pointer access
}

Is this a bug, or is this intended beahvior? I always believed that with 
all the dependency detenction that is done such situation should not happen.

Kind Regards
Benjamin Thaut
Mar 03 2013
next sibling parent reply "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 3 March 2013 at 12:01:10 UTC, Benjamin Thaut wrote:
 Is this a bug, or is this intended beahvior? I always believed 
 that with all the dependency detenction that is done such 
 situation should not happen.
It's intended behaviour. Remember that shared static this() is only run once for the whole program, whereas static this() is run per thread. Using per-thread data inside something that's run per-program doesn't make a whole lot of sense.
Mar 03 2013
parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 03.03.2013 16:29, schrieb Peter Alexander:
 On Sunday, 3 March 2013 at 12:01:10 UTC, Benjamin Thaut wrote:
 Is this a bug, or is this intended beahvior? I always believed that
 with all the dependency detenction that is done such situation should
 not happen.
It's intended behaviour. Remember that shared static this() is only run once for the whole program, whereas static this() is run per thread. Using per-thread data inside something that's run per-program doesn't make a whole lot of sense.
Well this is of course a reduced version of a real issue I'm having. I have thread local stack allocators. Each thread has its own stack allocator and certain parts of my code use these. What happened now is, that I inserted a call into a shared static module constructor which calls a function which in turn uses a thread local stack allocator. The problem is, that the thread local stack allocator module is not fully initialized at that point. But I think I can work around it by adding a shared static constructor and initialize the thread local stack alloactor for the main thread there. Kind Regards Benjamin Thaut
Mar 03 2013
parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Sunday, 3 March 2013 at 16:07:58 UTC, Benjamin Thaut wrote:
 Am 03.03.2013 16:29, schrieb Peter Alexander:
 On Sunday, 3 March 2013 at 12:01:10 UTC, Benjamin Thaut wrote:
 Is this a bug, or is this intended beahvior? I always 
 believed that
 with all the dependency detenction that is done such 
 situation should
 not happen.
It's intended behaviour. Remember that shared static this() is only run once for the whole program, whereas static this() is run per thread. Using per-thread data inside something that's run per-program doesn't make a whole lot of sense.
Well this is of course a reduced version of a real issue I'm having. I have thread local stack allocators. Each thread has its own stack allocator and certain parts of my code use these. What happened now is, that I inserted a call into a shared static module constructor which calls a function which in turn uses a thread local stack allocator. The problem is, that the thread local stack allocator module is not fully initialized at that point. But I think I can work around it by adding a shared static constructor and initialize the thread local stack alloactor for the main thread there.
It's a tricky situation. There are bad situations no matter which order things happen. At least the order is defined. Having this kind of thing break in non-deterministic ways would be a lot less fun.
Mar 03 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 03 Mar 2013 07:01:11 -0500, Benjamin Thaut  
<code benjamin-thaut.de> wrote:

 When modules are initialized, druntime will first run the module  
 initializers before running the module tls initializers ignoring the  
 dependencies between the modules. This will lead to a access of a  
 unitialized module if the module ctor of one module uses tls data of  
 another module. Example:

 module a:

 class TLSStuff
 {
    public int i;
 }

 TLSStuff g_tlsStuff;

 static this()
 {
    g_tlsStuff = new TLSStuff();
 }

 module b:
 import a;

 shared static this()
 {
    g_tlsStuff.i++; //null pointer access
 }

 Is this a bug, or is this intended beahvior? I always believed that with  
 all the dependency detenction that is done such situation should not  
 happen.
As has been discussed, this is intended behavior. But I will go a bit further. It is reasonable to assume that each thread being allocated will be assured that the shared data has already been initialized. This makes logical sense as the shared data is initialized once, thread data is initialized whenever you start a new one. So if we had the shared data depending on the first thread local data being initialized, you couldn't have ANY thread local data depending on the shared data being initialized. I don't think this is the right decision. But how can we solve this problem? One possibility, while a bit awkward, is to identify when you are initializing the first thread. Then use that knowledge to your advantage: __gshared bool inFirstThread = true; static this() { if(inFirstThread) // shared constructor already has initialized tls stuff for us inFirstThread = false; else g_tlsStuff = new TLSStuff(); } shared static this() { // initialize main thread's tls stuff g_tlsStuff = new TLSStuff(); g_tlsStuff.i++; } If you needed this idiom elsewhere, you could set the inFirstThread to false as the first statement in main. Druntime could conceivably do something like this for you, if you are so inclined, file an enhancement. -Steve
Mar 04 2013