Thread overview
Cycle detected between modules with ctors/dtors
Mar 03, 2015
rumbu
Mar 03, 2015
rumbu
Mar 04, 2015
rumbu
Mar 04, 2015
rumbu
Mar 05, 2015
ketmar
March 03, 2015
I encountered the following error:

First-chance exception: object.Exception Aborting: Cycle detected between modules with ctors/dtors:
system.globalization -> internals.locale -> system.runtime.interopservices -> system.io -> system.globalization at src\rt\minfo.d(162)

Only one of the listed modules has a static contructor  (system.globalization) and that constructor doesn't use any information from other modules.

The message is not too helpful, I have no clue where to start searching for the error source, the source code has more than 25k LOC. In fact, I don't believe this error is true, since I have only two non-linked modules with static contructors.

Stack trace is also not helpful at all:

 	KernelBase.dll!7512f896	
 	sharp.exe!_D2rt9deh_win329throwImplFC6ObjectZv() + 0x1c bytes	D
 	sharp.exe!_D2rt5minfo11ModuleGroup9sortCtorsMFZ4sortMFKAPyS6object10ModuleInfokZv() + 0x27c bytes	D
 	sharp.exe!_D2rt5minfo13rt_moduleCtorUZ14__foreachbody1MFKS2rt14sections_win3212SectionGroupZi() + 0xf bytes	D
>	sharp.exe!typeid(const(immutable(char)[]))() + 0xb020 bytes	D


Any hint is highly appreciated, at least I want to know where to start searching.

March 03, 2015
On 3/3/15 12:40 PM, rumbu wrote:
> I encountered the following error:
>
> First-chance exception: object.Exception Aborting: Cycle detected
> between modules with ctors/dtors:
> system.globalization -> internals.locale ->
> system.runtime.interopservices -> system.io -> system.globalization at
> src\rt\minfo.d(162)
>
> Only one of the listed modules has a static contructor
> (system.globalization) and that constructor doesn't use any information
> from other modules.

It's a complex problem. Because we don't control the linker, we cannot establish an order of execution for the module system. It must be a directed acyclic graph. Although, cycles are allowed that only involve a single module with ctor/dtors, or with no ctor/dtors. But we have to detect it at runtime during startup.

two of those modules should have static ctor or dtor (they may have either to cause the error).

> The message is not too helpful, I have no clue where to start searching
> for the error source, the source code has more than 25k LOC. In fact, I
> don't believe this error is true, since I have only two non-linked
> modules with static contructors.

Just look for "static this" and "static ~this" inside those files identified.

> Stack trace is also not helpful at all:
>
>       KernelBase.dll!7512f896
>       sharp.exe!_D2rt9deh_win329throwImplFC6ObjectZv() + 0x1c bytes    D
>
>      sharp.exe!_D2rt5minfo11ModuleGroup9sortCtorsMFZ4sortMFKAPyS6object10ModuleInfokZv() + 0x27c bytes    D
>
>      sharp.exe!_D2rt5minfo13rt_moduleCtorUZ14__foreachbody1MFKS2rt14sections_win3212SectionGroupZi() + 0xf bytes    D
>>     sharp.exe!typeid(const(immutable(char)[]))() + 0xb020 bytes    D

The stack trace only shows the stack for when the DAG is being constructed, it is not a stack trace of trying to RUN those module ctors.

-Steve
March 03, 2015
On Tuesday, 3 March 2015 at 18:55:49 UTC, Steven Schveighoffer wrote:

>>
>> Only one of the listed modules has a static contructor
>> (system.globalization) and that constructor doesn't use any information
>> from other modules.
>
> It's a complex problem. Because we don't control the linker, we cannot establish an order of execution for the module system. It must be a directed acyclic graph. Although, cycles are allowed that only involve a single module with ctor/dtors, or with no ctor/dtors. But we have to detect it at runtime during startup.
>
> two of those modules should have static ctor or dtor (they may have either to cause the error).
>
>> The message is not too helpful, I have no clue where to start searching
>> for the error source, the source code has more than 25k LOC. In fact, I
>> don't believe this error is true, since I have only two non-linked
>> modules with static contructors.
>
> Just look for "static this" and "static ~this" inside those files identified.
>
>
> -Steve

One of the listed modules had a ctor. Another one - curiously - not listed in the cyclic error - had a static dtor, and this module was imported by two of them.

Anyway, reduced case scenario:

module a;
import b;
static this();
-----
module b;
import a;
static ~this();

As I supposed, this is a false error, the order of execution seems very clear to me, but not for the runtime. Even if I have two ctors, I think it's better to relax this rule, maybe the constructors are not dependent one of each other to have cyclic errors. In my case, one of the constructors loads some resources bound to the executable and the destructor is simply a winapi call to CoUninitialize, totally unrelated.


March 04, 2015
Now I see: this "bug" is 7 years old, but is filled in for D1:

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

Does this mean it will be ignored for D2?



March 04, 2015
On 3/3/15 4:24 PM, rumbu wrote:
> On Tuesday, 3 March 2015 at 18:55:49 UTC, Steven Schveighoffer wrote:
>
>>>
>>> Only one of the listed modules has a static contructor
>>> (system.globalization) and that constructor doesn't use any information
>>> from other modules.
>>
>> It's a complex problem. Because we don't control the linker, we cannot
>> establish an order of execution for the module system. It must be a
>> directed acyclic graph. Although, cycles are allowed that only involve
>> a single module with ctor/dtors, or with no ctor/dtors. But we have to
>> detect it at runtime during startup.
>>
>> two of those modules should have static ctor or dtor (they may have
>> either to cause the error).
>>
>>> The message is not too helpful, I have no clue where to start searching
>>> for the error source, the source code has more than 25k LOC. In fact, I
>>> don't believe this error is true, since I have only two non-linked
>>> modules with static contructors.
>>
>> Just look for "static this" and "static ~this" inside those files
>> identified.
>>
>>
>> -Steve
>
> One of the listed modules had a ctor. Another one - curiously - not
> listed in the cyclic error - had a static dtor, and this module was
> imported by two of them.

curiously not listed? That doesn't make any sense. If there is not a cycle in the listed modules, where at least 2 modules have a static ctor or static dtor, then there is a bug in the cycle detection code.

>
> Anyway, reduced case scenario:
>
> module a;
> import b;
> static this();
> -----
> module b;
> import a;
> static ~this();
>
> As I supposed, this is a false error, the order of execution seems very
> clear to me, but not for the runtime. Even if I have two ctors, I think
> it's better to relax this rule, maybe the constructors are not dependent
> one of each other to have cyclic errors. In my case, one of the
> constructors loads some resources bound to the executable and the
> destructor is simply a winapi call to CoUninitialize, totally unrelated.
>
>

Clearly, the above is not an issue, but at the moment, the code treats having a ctor and having a dtor as the same thing. How to fix it? I'm not exactly sure, you need to have the modules sorted according to ctor and also according to dtor. The runtime is supposed to run dtors in reverse order from ctor calls, and I think we should not break that. But there is definitely room to allow for reordering when ctors are not affected.

I'll have to think about it some more.

-Steve
March 04, 2015
On Wednesday, 4 March 2015 at 16:08:00 UTC, Steven Schveighoffer wrote:

>
> curiously not listed? That doesn't make any sense. If there is not a cycle in the listed modules, where at least 2 modules have a static ctor or static dtor, then there is a bug in the cycle detection code.

Source code is available here: https://github.com/rumbu13/sharp

The runtime error chain was "system.globalization -> internals.locale ->
system.runtime.interopservices -> system.io ->
system.globalization

Three of these modules import "internals.resources" containing a static constructor. The static dtor is in system.runtime.interopservices.

> Clearly, the above is not an issue, but at the moment, the code treats having a ctor and having a dtor as the same thing. How to fix it? I'm not exactly sure, you need to have the modules sorted according to ctor and also according to dtor. The runtime is supposed to run dtors in reverse order from ctor calls, and I think we should not break that. But there is definitely room to allow for reordering when ctors are not affected.
>
> I'll have to think about it some more.
>
> -Steve

Why bother? I'll leave this to the programmer, he can take full responsibility to not mess up module ctors/dtors. I remember the old Delphi days where this was assumed from the start: initialization and finalization order of execution (equivalent of static ctor/dtor) were not guaranteed. It was better to obtain an AccessViolation or a StackOverflow and correct the circular reference, instead of a NEVER running executable.

March 05, 2015
On Wed, 04 Mar 2015 11:06:50 -0500, Steven Schveighoffer wrote:

> Clearly, the above is not an issue, but at the moment, the code treats having a ctor and having a dtor as the same thing. How to fix it? I'm not exactly sure, you need to have the modules sorted according to ctor and also according to dtor. The runtime is supposed to run dtors in reverse order from ctor calls, and I think we should not break that. But there is definitely room to allow for reordering when ctors are not affected.
> 
> I'll have to think about it some more.

maybe do simply the thing you wrote, and have two independent lists. or, strictly saying, four independent lists: for shared ctors, for static ctors, for static dtors, for shared dtors.

March 06, 2015
On 3/4/15 4:15 PM, rumbu wrote:
> On Wednesday, 4 March 2015 at 16:08:00 UTC, Steven Schveighoffer wrote:
>
>>
>> curiously not listed? That doesn't make any sense. If there is not a
>> cycle in the listed modules, where at least 2 modules have a static
>> ctor or static dtor, then there is a bug in the cycle detection code.
>
> Source code is available here: https://github.com/rumbu13/sharp
>
> The runtime error chain was "system.globalization -> internals.locale ->
> system.runtime.interopservices -> system.io ->
> system.globalization
>
> Three of these modules import "internals.resources" containing a static
> constructor. The static dtor is in system.runtime.interopservices.

I'll take a look. importing a module that has static ctor/dtor doesn't count, it must be in the module itself to be part of the cycle.

>
>> Clearly, the above is not an issue, but at the moment, the code treats
>> having a ctor and having a dtor as the same thing. How to fix it? I'm
>> not exactly sure, you need to have the modules sorted according to
>> ctor and also according to dtor. The runtime is supposed to run dtors
>> in reverse order from ctor calls, and I think we should not break
>> that. But there is definitely room to allow for reordering when ctors
>> are not affected.
>>
>> I'll have to think about it some more.
>
> Why bother? I'll leave this to the programmer, he can take full
> responsibility to not mess up module ctors/dtors. I remember the old
> Delphi days where this was assumed from the start: initialization and
> finalization order of execution (equivalent of static ctor/dtor) were
> not guaranteed. It was better to obtain an AccessViolation or a
> StackOverflow and correct the circular reference, instead of a NEVER
> running executable.

Well, 1) because we can fix it, and it's the correct thing to do, and 2) because it's an interesting problem (I remember when I fixed the old cycle detection code, which was buggy and just printed "cycle detected" or some such nonsense, it was a very interesting difficult problem to solve).

-Steve