www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Order of Static Construction

reply Dru <Dru notreal.com> writes:
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.
Jan 04 2019
next sibling parent reply Rubn <where is.this> writes:
On Friday, 4 January 2019 at 13:27:29 UTC, 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.
The shared static constructor would only be able to access one of the thread-local variables (probably from the main thread). That isn't very useful. It'd be more useful to have thread local static constructors access variables that were constructed in the shared static constructor. The thread constructor is called any time a new thread is created, and you can create a new thread any time at runtime. It'd be impossible to guarantee the shared static constructor is called after all the thread local constructors unless you put a bunch of restrictions on threads.
Jan 04 2019
parent Dru <Dru notreal.com> writes:
 Rubn
yes from the main thread
I'll give a specific example
lets say every thread has its own log
so log should be a thread local object
and lets say we want to log from a shared static ctor

log module
-----------
(define Log class ...)
Log log;
static this() {
   log = new Log;
}

other module
-----------
import log_module;
(define other class ...)
shared Other other;
shared static this() {
   log.write("creating other"); //seg fault log is null!
   other = new Other;
}



 Neia Neutuladh
you're right the statement doesn't consider starting threads.
if we strictly follow the spec then yes it should error,
but I think maybe we should change the spec and mix between 
shared/non-shared
if there isn't a good reason not to
Jan 04 2019
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
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.
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 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. In https://issues.dlang.org/show_bug.cgi?id=19492, I suggested a warning or error instead of trying to do the right thing.
Jan 04 2019
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jan 04 2019
parent reply Dru <dru notreal.com> writes:
Construction order is resolved by module dependencies (import 
statements)

In the main thread
----------------------
Currently, shared and thread-local construction are separated.

Instead we could "construct a module", i.e run both shared and 
thread-local construction for that module, then continue to 
construct the next module.
Jan 05 2019
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/5/19 10:22 AM, Dru wrote:
 Construction order is resolved by module dependencies (import statements)
 
 In the main thread
 ----------------------
 Currently, shared and thread-local construction are separated.
 
 Instead we could "construct a module", i.e run both shared and 
 thread-local construction for that module, then continue to construct 
 the next module.
This is a really good idea, actually. I hadn't thought of that approach at all. I think it can be done in druntime as well, without depending on compiler changes. I will post an enhancement request to track it. Thanks! -Steve
Jan 07 2019
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/7/19 5:02 PM, Steven Schveighoffer wrote:
 On 1/5/19 10:22 AM, Dru wrote:
 Construction order is resolved by module dependencies (import statements)

 In the main thread
 ----------------------
 Currently, shared and thread-local construction are separated.

 Instead we could "construct a module", i.e run both shared and 
 thread-local construction for that module, then continue to construct 
 the next module.
This is a really good idea, actually. I hadn't thought of that approach at all. I think it can be done in druntime as well, without depending on compiler changes. I will post an enhancement request to track it. Thanks!
Note that this still doesn't solve the current problem of a thread running static ctors before the main thread's shared static ctors are done. We should deal with that as well. -Steve
Jan 07 2019
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 1/7/19 5:02 PM, Steven Schveighoffer wrote:

 I will post an enhancement request to track it. Thanks!
https://issues.dlang.org/show_bug.cgi?id=19556 -Steve
Jan 07 2019