Thread overview
How to initialize a globle variable nicely and properly?
Dec 15, 2018
Heromyth
Dec 15, 2018
Rubn
Dec 15, 2018
Neia Neutuladh
Dec 15, 2018
Heromyth
Dec 15, 2018
Neia Neutuladh
December 15, 2018
We have a module including many globle variables which are needed to be initialized firstly in "shared static this() {}", see here https://github.com/huntlabs/hunt/blob/master/source/hunt/time/Init.d.

The problem is that these variables are not always initialized firstly when are referenced by some others moudles in "static this() {}". Here is a demo to illustrate it.

//////////////////
// module A
//////////////////
module test.A;

import std.stdio;
import core.thread;
import core.time;

class A {
    __gshared int sharedField;

    shared static this() {
        writeln("running A in shared static this(), sharedField=", sharedField);

        Thread th = new Thread(() {  });
        th.start();

        Thread.sleep(100.msecs);
        sharedField = 2;
        writeln("running A done in shared static this(), sharedField=", sharedField);
    }

    static this() {
        writeln("running A in static this(), sharedField=", sharedField);
    }
}

//////////////////
// module B
//////////////////
module test.B;

import test.A;
import std.stdio;
import core.thread;

shared static this() {
    writeln("running in shared static this() from B");
}

class B {
    shared static this() {
        writeln("running B in shared static this(), sharedField=", A.sharedField);
    }

    static this() {
        // bug is here
        writeln("running B in static this(), sharedField=", A.sharedField);
    }
}

//////////////////
// module main
//////////////////
import std.stdio;

import test.A;
import core.thread;

void main()
{
	writeln("running in main.");
}

//////////////////
// output
//////////////////
Running ./demo
running A in shared static this(), sharedField=0
running A in static this(), sharedField=0
running B in static this(), sharedField=0  // bug is here
running A done in shared static this(), sharedField=2
running in shared static this() from B
running B in shared static this(), sharedField=2
running A in static this(), sharedField=2
running B in static this(), sharedField=2
running main.


====================
You can see the sharedField is 0 in B's static this() at first. If Thread is disabled to run in shared static this(), this problem seems to be fixed.

Some related bugs:
1) https://issues.dlang.org/show_bug.cgi?id=6114
2) https://issues.dlang.org/show_bug.cgi?id=4923

December 15, 2018
On Saturday, 15 December 2018 at 02:54:55 UTC, Heromyth wrote:
> We have a module including many globle variables which are needed to be initialized firstly in "shared static this() {}", see here https://github.com/huntlabs/hunt/blob/master/source/hunt/time/Init.d.
>
> The problem is that these variables are not always initialized firstly when are referenced by some others moudles in "static this() {}". Here is a demo to illustrate it.
>
> //////////////////
> // module A
> //////////////////
> module test.A;
>
> import std.stdio;
> import core.thread;
> import core.time;
>
> class A {
>     __gshared int sharedField;
>
>     shared static this() {
>         writeln("running A in shared static this(), sharedField=", sharedField);
>
>         Thread th = new Thread(() {  });
>         th.start();
>
>         Thread.sleep(100.msecs);
>         sharedField = 2;
>         writeln("running A done in shared static this(), sharedField=", sharedField);
>     }
>
>     static this() {
>         writeln("running A in static this(), sharedField=", sharedField);
>     }
> }
>
> //////////////////
> // module B
> //////////////////
> module test.B;
>
> import test.A;
> import std.stdio;
> import core.thread;
>
> shared static this() {
>     writeln("running in shared static this() from B");
> }
>
> class B {
>     shared static this() {
>         writeln("running B in shared static this(), sharedField=", A.sharedField);
>     }
>
>     static this() {
>         // bug is here
>         writeln("running B in static this(), sharedField=", A.sharedField);
>     }
> }
>
> //////////////////
> // module main
> //////////////////
> import std.stdio;
>
> import test.A;
> import core.thread;
>
> void main()
> {
> 	writeln("running in main.");
> }
>
> //////////////////
> // output
> //////////////////
> Running ./demo
> running A in shared static this(), sharedField=0
> running A in static this(), sharedField=0
> running B in static this(), sharedField=0  // bug is here
> running A done in shared static this(), sharedField=2
> running in shared static this() from B
> running B in shared static this(), sharedField=2
> running A in static this(), sharedField=2
> running B in static this(), sharedField=2
> running main.
>
>
> ====================
> You can see the sharedField is 0 in B's static this() at first. If Thread is disabled to run in shared static this(), this problem seems to be fixed.
>
> Some related bugs:
> 1) https://issues.dlang.org/show_bug.cgi?id=6114
> 2) https://issues.dlang.org/show_bug.cgi?id=4923

The problem here is that you are creating a new thread in the `shared static this()`.

    shared static this() {
        writeln("running A in shared static this(), sharedField=", sharedField);
        Thread th = new Thread(() {  }); // Calls `static this()` including B's `static this()`
        th.start();
        Thread.sleep(100.msecs);
        sharedField = 2;
        writeln("running A done in shared static this(), sharedField=", sharedField);
    }

    static this() {
        writeln("running A in static this(), sharedField=", sharedField);
    }

Creating a new thread causes those thread's constructor `static this()` to be called. You need to create your threads somewhere else or have the values be initialized before the threads are created.
December 15, 2018
On Sat, 15 Dec 2018 02:54:55 +0000, Heromyth wrote:
>      shared static this() {
>          writeln("running A in shared static this(),
> sharedField=", sharedField);
> 
>          Thread th = new Thread(() {  });
>          th.start();

When you start a D thread, thread-local static constructors get run. So don't start threads until your shared static constructors finish.

If I encountered something like this, I would set up a queue of actions to be run at the start of main() and fill them with static constructors. Instead of this static constructor creating a thread, it would enqueue a delegate that starts the thread.
December 15, 2018
On Saturday, 15 December 2018 at 03:48:15 UTC, Neia Neutuladh wrote:
> On Sat, 15 Dec 2018 02:54:55 +0000, Heromyth wrote:
>>      shared static this() {
>>          writeln("running A in shared static this(),
>> sharedField=", sharedField);
>> 
>>          Thread th = new Thread(() {  });
>>          th.start();
>
> When you start a D thread, thread-local static constructors get run. So don't start threads until your shared static constructors finish.
>
> If I encountered something like this, I would set up a queue of actions to be run at the start of main() and fill them with static constructors. Instead of this static constructor creating a thread, it would enqueue a delegate that starts the thread.

Yes, it's very dangerous to create a new thread in shared static this(). For a big project, it sometimes hard to identify this problem. Maybe, the compiler should do something for this, should it?
December 15, 2018
On Sat, 15 Dec 2018 13:49:03 +0000, Heromyth wrote:
> Yes, it's very dangerous to create a new thread in shared static this(). For a big project, it sometimes hard to identify this problem. Maybe, the compiler should do something for this, should it?

The runtime, more likely. There are plenty of problems in this vein that the compiler can't detect, but the runtime should be able to detect all of them.

Filed https://issues.dlang.org/show_bug.cgi?id=19492