Order of Static Construction

Steven Schveighoffer schveiguy at gmail.com
Fri Jan 4 20:08:57 UTC 2019


On 1/4/19 11:48 AM, Neia Neutuladh wrote:
> On Fri, 04 Jan 2019 13:27:29 +0000, Dru wrote:
>> from the spec:
>> "Shared static constructors on all modules are run before any non-shared
>> static constructors."
>>
>> Is there a specific reason, or is it just for simplicity?
>>
>> There could be a situation where a shared ctor depends on a thread local
>> variable.
>> Preferably, the variable is only initialized in a non-shared ctor.

Unfortunately, that's not how it can work. Shared constructors run once 
per program. So trivially, any thread run after main() has started will 
be run after the shared ctors have run. Some thread-local constructors 
depend on this, so you can't do it the other way around.

What you could do is initialize the logger in a function, then call that 
function from either the shared or thread local constructor, depending 
on whether it's already initialized or not (you can check without 
locking, since it's a thread-local).

> 
> This part of the spec isn't entirely correct. Consider:
> 
> ---
> shared static this()
> {
>    writeln("shared ctor start");
>    new Thread({}).start;
>    Thread.sleep(10.seconds);
>    writeln("shared ctor done");
> }
> static this()
> {
>    writeln("non-shared ctor");
> }
> ---
> 
> This prints:
> 
> shared ctor start
> non-shared ctor
> shared ctor done

Right. This supersedes the normal order, and can potentially cause issues.

> In order to fix this, the compiler would have to defer running threads
> until static constructors finish. But a static constructor that starts a
> thread could easily be waiting for it to finish and yield a result before
> continuing, leading to deadlocks.

Exactly right. There has been some abuse of what shared static 
constructors are for in a lot of libraries. Until recently, vibe.d 
encouraged putting the HTTP server setup into a static constructor, 
including initializing the socket listeners. This kind of thing is prone 
to errors, because you can't truly be certain of static construction of 
everything in the first thread.

This would not be as bad of a problem if the shared constructors were 
only allowed to access data initialized by shared constructors. But 
there's no designation in D for that.

> 
> In https://issues.dlang.org/show_bug.cgi?id=19492, I suggested a warning
> or error instead of trying to do the right thing.
> 

Might be the right thing. Another possibility to still allow starting 
threads from shared ctors is to wait for the shared ctors to finish, but 
only yield an error if they don't finish in a certain amount of time 
(like 4 seconds). That way, a thread can still be started if the shared 
ctors don't depend on the execution of the thread progressing.

Should have some documentation to go along with this.

-Steve


More information about the Digitalmars-d mailing list