Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Can't map modules to indices with shared libraries #114

Merged
merged 1 commit into from

2 participants

@MartinNowak
Collaborator
  • Module may be imported from a shared library and can't
    be assigned a fixed index any longer.

  • Flag modules after having them initialized, leave
    them flags until destruction.

  • Use a simplified stack based DFO traversal to
    avoid deep recursion and limit alloca to 100KB.

  • Reduce complexity, each module must only be
    visited once.

@MartinNowak MartinNowak Can't map modules to indices with shared libraries
 - Module may be imported from a shared library and can't
   be assigned a simple index any longer.

 - Flag modules after having them initialized.

 - Use a simplified stack based DFO traversal to
   avoid deep recursion and limit alloca to 100KB.
e07f159
@complexmath complexmath merged commit 34925f2 into D-Programming-Language:master
@MartinNowak
Collaborator

Mmh, this seems to cause a Bus Error on OSX32 and a segmentation fault on OSX64.
Could be an alignment issue when using alloca memory for a struct array.
I don't have access to an OSX system. Can someone help me out with further details,
a small backtrace would be most helpful.

@complexmath
Collaborator
@MartinNowak
Collaborator

OK, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 4, 2012
  1. @MartinNowak

    Can't map modules to indices with shared libraries

    MartinNowak authored
     - Module may be imported from a shared library and can't
       be assigned a simple index any longer.
    
     - Flag modules after having them initialized.
    
     - Use a simplified stack based DFO traversal to
       avoid deep recursion and limit alloca to 100KB.
This page is out of date. Refresh to see the latest.
Showing with 167 additions and 204 deletions.
  1. +167 −204 src/rt/minfo.d
View
371 src/rt/minfo.d
@@ -97,6 +97,9 @@ extern (C) void rt_moduleCtor()
runModuleFuncs!((a) { return a.ictor; })(_moduleinfo_array);
// sorted module ctors
runModuleFuncs!((a) { return a.ctor; })(_sortedCtors._ctors);
+ // flag all modules as initialized
+ foreach (m; _moduleinfo_array)
+ m.flags = m.flags | MIctordone;
}
extern (C) void rt_moduleTlsCtor()
@@ -113,6 +116,10 @@ extern (C) void rt_moduleDtor()
{
runModuleFuncsRev!((a) { return a.dtor; })(_sortedCtors._ctors);
+ // clean all initialized flags
+ foreach (m; _moduleinfo_array)
+ m.flags = m.flags & ~MIctordone;
+
_sortedCtors.free();
version (OSX)
{}
@@ -220,242 +227,195 @@ void runModuleFuncsRev(alias getfp)(ModuleInfo*[] modules)
SortedCtors sortCtors(ModuleInfo*[] modules)
{
- SortedCtors result;
- result.alloc(modules.length);
- // Create an array of modules that will determine the order of construction
- // (and destruction in reverse).
- auto ctors = result._ctors;
- size_t ctoridx = 0;
+ enum AllocaLimit = 100 * 1024; // 100KB
- // this pointer will identify the module where the cycle was detected.
- ModuleInfo *cycleModule;
+ immutable size = modules.length * StackRec.sizeof;
- // allocate some stack arrays that will be used throughout the process.
- ubyte* p = cast(ubyte *)alloca(modules.length * ubyte.sizeof);
- auto reachable = p[0..modules.length];
-
- p = cast(ubyte *)alloca(modules.length * ubyte.sizeof);
- auto flags = p[0..modules.length];
-
-
- // find all the non-trivial dependencies (that is, dependencies that have a
- // ctor or dtor) of a given module. Doing this, we can 'skip over' the
- // trivial modules to get at the non-trivial ones.
- size_t _findDependencies(ModuleInfo *current, bool orig = true)
+ if (!size)
{
- auto idx = current.index;
- if(reachable[idx])
- return 0;
- size_t result = 0;
- reachable[idx] = 1;
- if(!orig && (flags[idx] & (MIctor | MIdtor)) && !(flags[idx] & MIstandalone))
- // non-trivial, stop here
- return result + 1;
- foreach (ModuleInfo *m; current.importedModules)
- {
- result += _findDependencies(m, false);
- }
+ return SortedCtors.init;
+ }
+ else if (size <= AllocaLimit)
+ {
+ auto p = cast(ubyte*).alloca(size);
+ p[0 .. size] = 0;
+ return sortCtorsImpl(modules, (cast(StackRec*)p)[0 .. modules.length]);
+ }
+ else
+ {
+ auto p = cast(ubyte*).malloc(size);
+ p[0 .. size] = 0;
+ auto result = sortCtorsImpl(modules, (cast(StackRec*)p)[0 .. modules.length]);
+ .free(p);
return result;
}
+}
- void print(string msgs[]...)
- {
- version (unittest)
- {
- if (_inUnitTest)
- return;
- }
+private:
- foreach (m; msgs)
- {
- // write message to stderr
- console(m);
- }
+void print(string m)
+{
+ // write message to stderr
+ console(m);
+}
+
+void println(string m)
+{
+ print(m);
+ version (Windows)
+ print("\r\n");
+ else
+ print("\n");
+}
+
+struct StackRec
+{
+ @property ModuleInfo* mod()
+ {
+ return _mods[_idx];
}
- void println(string msgs[]...)
+ ModuleInfo*[] _mods;
+ size_t _idx;
+}
+
+void onCycleError(StackRec[] stack)
+{
+ version (unittest)
{
- print(msgs);
- version(Windows)
- print("\r\n");
- else
- print("\n");
+ if (_inUnitTest)
+ goto Lerror;
}
- bool printCycle(ModuleInfo *current, ModuleInfo *target, bool orig = true)
+ println("Cycle detected between modules with ctors/dtors:");
+ foreach (e; stack)
{
- if(reachable[current.index])
- // already visited
- return false;
- if(current is target)
- // found path
- return true;
- reachable[current.index] = 1;
- if(!orig && (flags[current.index] & (MIctor | MIdtor)) && !(flags[current.index] & MIstandalone))
- // don't go through modules with ctors/dtors that aren't
- // standalone.
- return false;
- // search connections from current to see if we can get to target
- foreach (m; current.importedModules)
- {
- if(printCycle(m, target, false))
- {
- // found the path, print this module
- if(orig)
- println("imported from ", current.name, " containing module ctor/dtor");
- else
- println(" imported from (", current.name, ")");
- return true;
- }
- }
- return false;
+ print(e.mod.name);
+ print(" -> ");
}
+ println(stack[0].mod.name);
+ Lerror:
+ throw new Exception("Aborting!");
+}
+
+private SortedCtors sortCtorsImpl(ModuleInfo*[] modules, StackRec[] stack)
+{
+ SortedCtors result;
+ result.alloc(modules.length);
+
+ size_t stackidx;
+ bool tlsPass;
+
+ Lagain:
+
+ const mask = tlsPass ? (MItlsctor | MItlsdtor) : (MIctor | MIdtor);
+ auto ctors = tlsPass ? result._tlsctors : result._ctors;
+ size_t cidx;
- // This function will determine the order of construction/destruction and
- // check for cycles.
- bool _checkModCtors2(ModuleInfo *current)
+ ModuleInfo*[] mods = modules;
+ size_t idx;
+ while (true)
{
- // we only get called if current has a dtor or a ctor, so no need to
- // check that. First, determine what non-trivial elements are
- // reachable.
- reachable[] = 0;
- auto nmodules = _findDependencies(current);
-
- // allocate the dependencies on the stack
- ModuleInfo **p = cast(ModuleInfo **)alloca(nmodules * (ModuleInfo*).sizeof);
- auto dependencies = p[0..nmodules];
- uint depidx = 0;
- // fill in the dependencies
- foreach (i, r; reachable)
+ while (idx < mods.length)
{
- if(r)
+ auto m = mods[idx];
+ auto fl = m.flags;
+ if (fl & MIctorstart)
{
- ModuleInfo *m = modules[i];
- if(m !is current && (flags[i] & (MIctor | MIdtor)) && !(flags[i] & MIstandalone))
+ // trace back to cycle start
+ fl &= ~MIctorstart;
+ size_t start = stackidx;
+ while (start--)
{
- dependencies[depidx++] = m;
+ auto sm = stack[start].mod;
+ if (sm == m)
+ break;
+ fl |= sm.flags & MIctorstart;
+ }
+ assert(stack[start].mod == m);
+ if (fl & MIctorstart)
+ {
+ /* This is an illegal cycle, no partial order can be established
+ * because the import chain have contradicting ctor/dtor
+ * constraints.
+ */
+ onCycleError(stack[start .. stackidx]);
+ }
+ else
+ {
+ /* This is also a cycle, but the import chain does not constrain
+ * the order of initialization, either because the imported
+ * modules have no ctors or the ctors are standalone.
+ */
+ ++idx;
}
}
- }
- assert(depidx == nmodules);
-
- // ok, now perform cycle detection
- auto curidx = current.index;
- flags[curidx] |= MIctorstart;
- bool valid = true;
- foreach (m; dependencies)
- {
- auto mflags = flags[m.index];
- if(mflags & MIctorstart)
- {
- // found a cycle, but we don't care if the MIstandalone flag is
- // set, this is a guarantee that there are no cycles in this
- // module (not sure what triggers it)
- println("Cyclic dependency in module ", m.name);
- cycleModule = m;
- valid = false;
-
- // use the currently allocated dtor path to record the loop
- // that contains module ctors/dtors only.
- ctoridx = ctors.length;
- }
- else if(!(mflags & MIctordone))
- {
- valid = _checkModCtors2(m);
+ else if (fl & MIctordone)
+ { // already visited => skip
+ ++idx;
}
-
-
- if(!valid)
+ else
{
- // cycle detected, now, we must print in reverse order the
- // module include cycle. For this, we need to traverse the
- // graph of trivial modules again, this time printing them.
- reachable[] = 0;
- printCycle(current, m);
-
- // record this as a module that was used in the loop.
- ctors[--ctoridx] = current;
- if(current is cycleModule)
+ if (fl & mask)
{
- // print the cycle
- println("Cycle detected between modules with ctors/dtors:");
- foreach (cm; ctors[ctoridx..$])
- {
- print(cm.name, " -> ");
+ if (fl & MIstandalone || !m.importedModules.length)
+ { // trivial ctor => sort in
+ ctors[cidx++] = m;
+ m.flags = fl | MIctordone;
+ }
+ else
+ { // non-trivial ctor => defer
+ m.flags = fl | MIctorstart;
}
- println(cycleModule.name);
- throw new Exception("Aborting!");
}
- return false;
- }
- }
- flags[curidx] = (flags[curidx] & ~MIctorstart) | MIctordone;
- // add this module to the construction order list
- ctors[ctoridx++] = current;
- return true;
- }
+ else // no ctor => mark as visited
+ m.flags = fl | MIctordone;
- void _checkModCtors3()
- {
- foreach (m; modules)
- {
- // TODO: Should null ModuleInfo be allowed?
- if (m is null) continue;
- auto flag = flags[m.index];
- if((flag & (MIctor | MIdtor)) && !(flag & MIctordone))
- {
- if(flag & MIstandalone)
+ if (m.importedModules.length)
{
- // no need to run a check on this one, but we do need to call its ctor/dtor
- ctors[ctoridx++] = m;
+ /* Internal runtime error, dependency on an uninitialized
+ * module outside of the current module group.
+ */
+ (stackidx < modules.length) || assert(0);
+
+ // recurse
+ stack[stackidx++] = StackRec(mods, idx);
+ idx = 0;
+ mods = m.importedModules;
}
- else
- _checkModCtors2(m);
}
}
- }
- // ok, now we need to assign indexes, and also initialize the flags
- foreach (uint i, m; modules)
- {
- // TODO: Should null ModuleInfo be allowed?
- if (m is null) continue;
- m.index = i;
- ubyte flag = m.flags & MIstandalone;
- if(m.dtor)
- flag |= MIdtor;
- if(m.ctor)
- flag |= MIctor;
- flags[i] = flag;
+ if (stackidx)
+ { // pop old value from stack
+ --stackidx;
+ mods = stack[stackidx]._mods;
+ idx = stack[stackidx]._idx;
+ auto m = mods[idx++];
+ auto fl = m.flags;
+ if (fl & mask && !(fl & MIctordone))
+ ctors[cidx++] = m;
+ m.flags = (fl & ~MIctorstart) | MIctordone;
+ }
+ else // done
+ break;
}
+ // store final number
+ tlsPass ? result._tlsctors : result._ctors = ctors[0 .. cidx];
- // everything's all set up for shared ctors
- _checkModCtors3();
-
- // store the number of dtors/ctors
- result._ctors = result._ctors[0 .. ctoridx];
+ // clean flags
+ for (size_t i = 0; i < modules.length; ++i)
+ { auto m = modules[i];
+ m.flags = m.flags & ~(MIctorstart | MIctordone);
+ }
- // set up everything for tls ctors
- ctors = result._tlsctors;
- ctoridx = 0;
- foreach (i, m; modules)
+ // rerun for TLS constructors
+ if (!tlsPass)
{
- // TODO: Should null ModuleInfo be allowed?
- if (m is null) continue;
- ubyte flag = m.flags & MIstandalone;
- if(m.tlsdtor)
- flag |= MIdtor;
- if(m.tlsctor)
- flag |= MIctor;
- flags[i] = flag;
+ tlsPass = true;
+ goto Lagain;
}
- // ok, run it
- _checkModCtors3();
-
- // store the number of dtors/ctors
- result._tlsctors = result._tlsctors[0 .. ctoridx];
-
return result;
}
@@ -513,12 +473,9 @@ unittest
{
auto ptrs = [&m0, &m1, &m2];
auto sorted = sortCtors(ptrs);
- foreach (i, m; ptrs)
- {
- assert(m.index == i);
- m.index = 0;
- }
- assert(sorted._ctors == dtors);
+ foreach (m; ptrs)
+ assert(!(m.flags & (MIctorstart | MIctordone)));
+ assert(sorted._ctors == dtors);
assert(sorted._tlsctors == tlsdtors);
}
@@ -596,4 +553,10 @@ unittest
m1 = mockMI(MIctor);
m2 = mockMI(MItlsctor, &m0);
assertThrown!Throwable(checkExp());
+
+ // closed ctors cycle
+ m0 = mockMI(MIctor, &m1);
+ m1 = mockMI(MIstandalone | MIctor, &m2);
+ m2 = mockMI(MIstandalone | MIctor, &m0);
+ checkExp([&m1, &m2, &m0], []);
}
Something went wrong with that request. Please try again.