Jump to page: 1 2
Thread overview
Module init ordering bug - analysis and code example
Sep 02, 2004
pragma
Re: Module init ordering bug - analysis and code example - mod.zip
Sep 02, 2004
pragma
Sep 02, 2004
antiAlias
Sep 02, 2004
Walter
Sep 02, 2004
Walter
Sep 03, 2004
Ben Hinkle
Sep 03, 2004
pragma
Sep 07, 2004
Walter
Sep 08, 2004
pragma
Sep 10, 2004
Walter
Sep 10, 2004
pragma
Sep 11, 2004
Walter
Sep 11, 2004
pragma
September 02, 2004
Walter,
in learning how D handles module constructors, I came across the same bug that
others have stumbled across.  I did a little sleuthing to discover what was
causing this problem. ;)

My analysis (see below) showed that modules without constructors (static
this(){}) cannot be prioritized in _moduleCtor(), nor will their imports be
prioritized, since without such a constructor a module isnt't added to the
_moduleinfo_array.  Is this correct?

If so (and if you don't mind), I'd like to propose that dmd be changed to add all modules (not just those with ctors) to the _moduleinfo_array.  This would not only fix these particular init problems, but would also be very useful for reflection purposes.

Many, many thanks,
Eric



[Example]
The expected init order was: mod4, mod3, mod2, mod1 as that is the order of
import and the order of dependency by function call.  Instead the modules are
run in a different order: mod2, mod1, mod4.  Inserting a module constructor into
mod3 forces the correct init order.

//// mod1.d ////
import std.stdio;
import std.moduleinit;
import mod2;

static this(){
writefln("mod1");
}

void main(){
writefln("\n--MODULE INFO--\n");

foreach(ModuleInfo info; _moduleinfo_array){
writefln("%s", info.name);
foreach(ModuleInfo imported; info.importedModules){
writefln("\timport %s",imported.name);
}
}
}


//// mod2.d ////
import std.stdio;
import mod3;

static this(){
writefln("mod2");
mod3.foo();
}


//// mod3.d ////
import std.stdio;
import mod4;

/* Uncomment to force correct ordering
static this(){
writefln("mod3");
}
*/

static void foo(){
writefln("\tmod3.foo()");
mod4.bar();
}


//// mod4.d ////
import std.stdio;

static this(){
writefln("mod4");
}

static void bar(){
writefln("\tmod4.bar()");
}


//// Output ////
mod2
mod3.foo()
mod4.bar()
mod1
mod4

--MODULE INFO--

mod1
import mod2
mod2
mod4
format
utf
string
adi
import string
gcbits
import string
thread

- Pragma
[[ EricAnderton at (static this{}) yahoo dot com ]]
September 02, 2004
Update:
The problem gets worse with 'private import'.  Module ctor dependencies seem to
only be added if the 'private' declaration is dropped, regardless of the
presence of a module ctor.

An revised version of my example (with private imports used) is attached.  The output shows that mod1 no longer shows a dependency upon mod2.

mod1
mod2
mod3.foo()
mod4.bar()
mod4

--MODULE INFO--

mod1
mod2
mod4
format
utf
string
adi
import string
gcbits
import string
thread

-Pragma

In article <ch7s0p$mi8$1@digitaldaemon.com>, pragma says...
>
>Walter,
>in learning how D handles module constructors, I came across the same bug that
>others have stumbled across.  I did a little sleuthing to discover what was
>causing this problem. ;)
>
>My analysis (see below) showed that modules without constructors (static
>this(){}) cannot be prioritized in _moduleCtor(), nor will their imports be
>prioritized, since without such a constructor a module isnt't added to the
>_moduleinfo_array.  Is this correct?
>
>If so (and if you don't mind), I'd like to propose that dmd be changed to add all modules (not just those with ctors) to the _moduleinfo_array.  This would not only fix these particular init problems, but would also be very useful for reflection purposes.
>
>Many, many thanks,
>Eric
>
>
>
>[Example]
>The expected init order was: mod4, mod3, mod2, mod1 as that is the order of
>import and the order of dependency by function call.  Instead the modules are
>run in a different order: mod2, mod1, mod4.  Inserting a module constructor into
>mod3 forces the correct init order.
>
>//// mod1.d ////
>import std.stdio;
>import std.moduleinit;
>import mod2;
>
>static this(){
>writefln("mod1");
>}
>
>void main(){
>writefln("\n--MODULE INFO--\n");
>
>foreach(ModuleInfo info; _moduleinfo_array){
>writefln("%s", info.name);
>foreach(ModuleInfo imported; info.importedModules){
>writefln("\timport %s",imported.name);
>}
>}
>}
>
>
>//// mod2.d ////
>import std.stdio;
>import mod3;
>
>static this(){
>writefln("mod2");
>mod3.foo();
>}
>
>
>//// mod3.d ////
>import std.stdio;
>import mod4;
>
>/* Uncomment to force correct ordering
>static this(){
>writefln("mod3");
>}
>*/
>
>static void foo(){
>writefln("\tmod3.foo()");
>mod4.bar();
>}
>
>
>//// mod4.d ////
>import std.stdio;
>
>static this(){
>writefln("mod4");
>}
>
>static void bar(){
>writefln("\tmod4.bar()");
>}
>
>
>//// Output ////
>mod2
>mod3.foo()
>mod4.bar()
>mod1
>mod4
>
>--MODULE INFO--
>
>mod1
>import mod2
>mod2
>mod4
>format
>utf
>string
>adi
>import string
>gcbits
>import string
>thread
>
>- Pragma
>[[ EricAnderton at (static this{}) yahoo dot com ]]


September 02, 2004
Yep; I just posted you a reply on dsource.

The issue is with the importedModules[], which is supposed to house a list of imported modules (filtered to include only those which require static initialization) ~ per-module.

Unfortunately, if you add /any/ kind of attribute to an import statement, it is excluded from importedModules[]. Entire chains of dependency are thus lost, and static constructors are never executed. Anything project that (for example) makes use "private import" is thus inviting doom.


"pragma" <pragma_member@pathlink.com> wrote in message
news:ch84ft$r1b$1@digitaldaemon.com...
Update:
The problem gets worse with 'private import'.  Module ctor dependencies seem
to
only be added if the 'private' declaration is dropped, regardless of the
presence of a module ctor.

An revised version of my example (with private imports used) is attached.
The
output shows that mod1 no longer shows a dependency upon mod2.

mod1
mod2
mod3.foo()
mod4.bar()
mod4

--MODULE INFO--

mod1
mod2
mod4
format
utf
string
adi
import string
gcbits
import string
thread

-Pragma

In article <ch7s0p$mi8$1@digitaldaemon.com>, pragma says...
>
>Walter,
>in learning how D handles module constructors, I came across the same bug
that
>others have stumbled across.  I did a little sleuthing to discover what was causing this problem. ;)
>
>My analysis (see below) showed that modules without constructors (static
>this(){}) cannot be prioritized in _moduleCtor(), nor will their imports be
>prioritized, since without such a constructor a module isnt't added to the
>_moduleinfo_array.  Is this correct?
>
>If so (and if you don't mind), I'd like to propose that dmd be changed to
add
>all modules (not just those with ctors) to the _moduleinfo_array.  This
would
>not only fix these particular init problems, but would also be very useful
for
>reflection purposes.
>
>Many, many thanks,
>Eric
>
>
>
>[Example]
>The expected init order was: mod4, mod3, mod2, mod1 as that is the order of

>import and the order of dependency by function call.  Instead the modules
are
>run in a different order: mod2, mod1, mod4.  Inserting a module constructor
into
>mod3 forces the correct init order.
>
>//// mod1.d ////
>import std.stdio;
>import std.moduleinit;
>import mod2;
>
>static this(){
>writefln("mod1");
>}
>
>void main(){
>writefln("\n--MODULE INFO--\n");
>
>foreach(ModuleInfo info; _moduleinfo_array){
>writefln("%s", info.name);
>foreach(ModuleInfo imported; info.importedModules){
>writefln("\timport %s",imported.name);
>}
>}
>}
>
>
>//// mod2.d ////
>import std.stdio;
>import mod3;
>
>static this(){
>writefln("mod2");
>mod3.foo();
>}
>
>
>//// mod3.d ////
>import std.stdio;
>import mod4;
>
>/* Uncomment to force correct ordering
>static this(){
>writefln("mod3");
>}
>*/
>
>static void foo(){
>writefln("\tmod3.foo()");
>mod4.bar();
>}
>
>
>//// mod4.d ////
>import std.stdio;
>
>static this(){
>writefln("mod4");
>}
>
>static void bar(){
>writefln("\tmod4.bar()");
>}
>
>
>//// Output ////
>mod2
>mod3.foo()
>mod4.bar()
>mod1
>mod4
>
>--MODULE INFO--
>
>mod1
>import mod2
>mod2
>mod4
>format
>utf
>string
>adi
>import string
>gcbits
>import string
>thread
>
>- Pragma
>[[ EricAnderton at (static this{}) yahoo dot com ]]




September 02, 2004
D's only requirement for ordering module constructors between modules is that the imported module constructors must happen first. They are not ordered by the order of the import statements - or at least such order is an artifact of the implementation.

You should therefore be able to control the order of module construction by making sure that each module depending on another module constructor imports that module.

"pragma" <pragma_member@pathlink.com> wrote in message news:ch7s0p$mi8$1@digitaldaemon.com...
> Walter,
> in learning how D handles module constructors, I came across the same bug
that
> others have stumbled across.  I did a little sleuthing to discover what
was
> causing this problem. ;)
>
> My analysis (see below) showed that modules without constructors (static
> this(){}) cannot be prioritized in _moduleCtor(), nor will their imports
be
> prioritized, since without such a constructor a module isnt't added to the _moduleinfo_array.  Is this correct?
>
> If so (and if you don't mind), I'd like to propose that dmd be changed to
add
> all modules (not just those with ctors) to the _moduleinfo_array.  This
would
> not only fix these particular init problems, but would also be very useful
for
> reflection purposes.
>
> Many, many thanks,
> Eric
>
>
>
> [Example]
> The expected init order was: mod4, mod3, mod2, mod1 as that is the order
of
> import and the order of dependency by function call.  Instead the modules
are
> run in a different order: mod2, mod1, mod4.  Inserting a module
constructor into
> mod3 forces the correct init order.
>
> //// mod1.d ////
> import std.stdio;
> import std.moduleinit;
> import mod2;
>
> static this(){
> writefln("mod1");
> }
>
> void main(){
> writefln("\n--MODULE INFO--\n");
>
> foreach(ModuleInfo info; _moduleinfo_array){
> writefln("%s", info.name);
> foreach(ModuleInfo imported; info.importedModules){
> writefln("\timport %s",imported.name);
> }
> }
> }
>
>
> //// mod2.d ////
> import std.stdio;
> import mod3;
>
> static this(){
> writefln("mod2");
> mod3.foo();
> }
>
>
> //// mod3.d ////
> import std.stdio;
> import mod4;
>
> /* Uncomment to force correct ordering
> static this(){
> writefln("mod3");
> }
> */
>
> static void foo(){
> writefln("\tmod3.foo()");
> mod4.bar();
> }
>
>
> //// mod4.d ////
> import std.stdio;
>
> static this(){
> writefln("mod4");
> }
>
> static void bar(){
> writefln("\tmod4.bar()");
> }
>
>
> //// Output ////
> mod2
> mod3.foo()
> mod4.bar()
> mod1
> mod4
>
> --MODULE INFO--
>
> mod1
> import mod2
> mod2
> mod4
> format
> utf
> string
> adi
> import string
> gcbits
> import string
> thread
>
> - Pragma
> [[ EricAnderton at (static this{}) yahoo dot com ]]


September 02, 2004
"pragma" <pragma_member@pathlink.com> wrote in message news:ch84ft$r1b$1@digitaldaemon.com...
> Update:
> The problem gets worse with 'private import'.  Module ctor dependencies
seem to
> only be added if the 'private' declaration is dropped, regardless of the presence of a module ctor.

I'm glad you were able to track this down. Now I can fix it!


September 03, 2004
So are you saying the supplied example isn't a bug? How about something like

mod1.m:
module mod1;
import mod2;
static this() { mod2.run(); }

mod2.m:
module mod2;
import mod3;
void run() { mod3.run(); }

mod3.m:
module mod3;
void* never_null;
static this() { never_null = new int; }
void run(){ printf("is never_null null? %p eek!\n",never_null); }

I would think this problem makes module initialization very easy to get around. How about a default (trivial) module constructor gets generated for a module if any of its imported modules have a constructor.

Walter wrote:

> D's only requirement for ordering module constructors between modules is that the imported module constructors must happen first. They are not ordered by the order of the import statements - or at least such order is an artifact of the implementation.
> 
> You should therefore be able to control the order of module construction by making sure that each module depending on another module constructor imports that module.
> 
> "pragma" <pragma_member@pathlink.com> wrote in message news:ch7s0p$mi8$1@digitaldaemon.com...
>> Walter,
>> in learning how D handles module constructors, I came across the same bug
> that
>> others have stumbled across.  I did a little sleuthing to discover what
> was
>> causing this problem. ;)
>>
>> My analysis (see below) showed that modules without constructors (static
>> this(){}) cannot be prioritized in _moduleCtor(), nor will their imports
> be
>> prioritized, since without such a constructor a module isnt't added to
>> the
>> _moduleinfo_array.  Is this correct?
>>
>> If so (and if you don't mind), I'd like to propose that dmd be changed to
> add
>> all modules (not just those with ctors) to the _moduleinfo_array.  This
> would
>> not only fix these particular init problems, but would also be very useful
> for
>> reflection purposes.
>>
>> Many, many thanks,
>> Eric
>>
>>
>>
>> [Example]
>> The expected init order was: mod4, mod3, mod2, mod1 as that is the order
> of
>> import and the order of dependency by function call.  Instead the modules
> are
>> run in a different order: mod2, mod1, mod4.  Inserting a module
> constructor into
>> mod3 forces the correct init order.
>>
>> //// mod1.d ////
>> import std.stdio;
>> import std.moduleinit;
>> import mod2;
>>
>> static this(){
>> writefln("mod1");
>> }
>>
>> void main(){
>> writefln("\n--MODULE INFO--\n");
>>
>> foreach(ModuleInfo info; _moduleinfo_array){
>> writefln("%s", info.name);
>> foreach(ModuleInfo imported; info.importedModules){
>> writefln("\timport %s",imported.name);
>> }
>> }
>> }
>>
>>
>> //// mod2.d ////
>> import std.stdio;
>> import mod3;
>>
>> static this(){
>> writefln("mod2");
>> mod3.foo();
>> }
>>
>>
>> //// mod3.d ////
>> import std.stdio;
>> import mod4;
>>
>> /* Uncomment to force correct ordering
>> static this(){
>> writefln("mod3");
>> }
>> */
>>
>> static void foo(){
>> writefln("\tmod3.foo()");
>> mod4.bar();
>> }
>>
>>
>> //// mod4.d ////
>> import std.stdio;
>>
>> static this(){
>> writefln("mod4");
>> }
>>
>> static void bar(){
>> writefln("\tmod4.bar()");
>> }
>>
>>
>> //// Output ////
>> mod2
>> mod3.foo()
>> mod4.bar()
>> mod1
>> mod4
>>
>> --MODULE INFO--
>>
>> mod1
>> import mod2
>> mod2
>> mod4
>> format
>> utf
>> string
>> adi
>> import string
>> gcbits
>> import string
>> thread
>>
>> - Pragma
>> [[ EricAnderton at (static this{}) yahoo dot com ]]

September 03, 2004
In article <ch8c1i$tdd$1@digitaldaemon.com>, Walter says...
>
>D's only requirement for ordering module constructors between modules is that the imported module constructors must happen first. They are not ordered by the order of the import statements - or at least such order is an artifact of the implementation.
>
>You should therefore be able to control the order of module construction by making sure that each module depending on another module constructor imports that module.

Gotcha.  Thank you for confirming this, as that's pretty much where all my hacking lead me.  (Actually it was pretty much in plain D for everyone in moduleinit.d, but that's another story) :)

However, the order of operations really isn't so crucial since it visits all the dependencies recursively, and flags everything on the way out to keep from running things more than once. (its a good compact design!) We also now know what conditions dmd pays attention to (at least in 0.101) when adding nodes to this constructor list.

As to the problem at hand: I feel that having dmd add all modules to the list (rather than just those with ctors) will be a huge improvement to the language.

Thanks Walter!

- Pragma
[[ Eric Anderton at (_moduleCtor) yahoo dot com ]]
September 07, 2004
"pragma" <pragma_member@pathlink.com> wrote in message news:ch8fa0$ujm$1@digitaldaemon.com...
> As to the problem at hand: I feel that having dmd add all modules to the
list
> (rather than just those with ctors) will be a huge improvement to the
language.

The problem with that is that then two modules could not import each other.


September 08, 2004
In article <chjl73$259k$1@digitaldaemon.com>, Walter says...
>
>
>"pragma" <pragma_member@pathlink.com> wrote in message news:ch8fa0$ujm$1@digitaldaemon.com...
>> As to the problem at hand: I feel that having dmd add all modules to the
>list
>> (rather than just those with ctors) will be a huge improvement to the
>language.
>
>The problem with that is that then two modules could not import each other.

Would this be only if both modules have static ctors, just one, or none at all?

I would think that the latter two cases would never be a problem given the way moduleinit.d is already coded: it's already assuming that there are modules in the list w/o ctors as it is checking for that condition (lines 111 and 121). Just put modules on the list with null ctors/dtors if none are declared and it should work fine (IMO).

And you already know the first case is already being handled quite nicely. :)

Or have I missed the point completely and there is some other case that's causing a problem?

- Pragma
[[Eric Anderton at yahoo dot com]]
September 10, 2004
"pragma" <pragma_member@pathlink.com> wrote in message news:chn51k$r3a$1@digitaldaemon.com...
> In article <chjl73$259k$1@digitaldaemon.com>, Walter says...
> >
> >
> >"pragma" <pragma_member@pathlink.com> wrote in message news:ch8fa0$ujm$1@digitaldaemon.com...
> >> As to the problem at hand: I feel that having dmd add all modules to
the
> >list
> >> (rather than just those with ctors) will be a huge improvement to the
> >language.
> >
> >The problem with that is that then two modules could not import each
other.
>
> Would this be only if both modules have static ctors, just one, or none at
all?
>
> I would think that the latter two cases would never be a problem given the
way
> moduleinit.d is already coded: it's already assuming that there are
modules in
> the list w/o ctors as it is checking for that condition (lines 111 and
121).
> Just put modules on the list with null ctors/dtors if none are declared
and it
> should work fine (IMO).
>
> And you already know the first case is already being handled quite nicely.
:)
>
> Or have I missed the point completely and there is some other case that's causing a problem?

The problem is if A imports B, and B imports A, whose initialization code gets run first? The imports imply a dependency, so we have "A requires that B's initializers run first" coupled with "B requires that A's initializers run first." There's no resolution. The only one I can think of is that a module with no initializers doesn't have a requirement that the imports get initialized first. And I think that is always correct - that if there *is* such a dependency in the code, then there's something else wrong.


« First   ‹ Prev
1 2