Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

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

Merged
merged 1 commit into from over 2 years ago

2 participants

Martin Nowak Sean Kelly
Martin Nowak
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.

Martin Nowak 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
Sean Kelly complexmath merged commit 34925f2 into from January 04, 2012
Sean Kelly complexmath closed this January 04, 2012
Martin Nowak
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.

Sean Kelly
Collaborator
Martin Nowak
Collaborator

OK, thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Jan 04, 2012
Martin Nowak 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
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 167 additions and 204 deletions. Show diff stats Hide diff stats

  1. 371  src/rt/minfo.d
371  src/rt/minfo.d
@@ -97,6 +97,9 @@ extern (C) void rt_moduleCtor()
97 97
     runModuleFuncs!((a) { return a.ictor; })(_moduleinfo_array);
98 98
     // sorted module ctors
99 99
     runModuleFuncs!((a) { return a.ctor; })(_sortedCtors._ctors);
  100
+    // flag all modules as initialized
  101
+    foreach (m; _moduleinfo_array)
  102
+        m.flags = m.flags | MIctordone;
100 103
 }
101 104
 
102 105
 extern (C) void rt_moduleTlsCtor()
@@ -113,6 +116,10 @@ extern (C) void rt_moduleDtor()
113 116
 {
114 117
     runModuleFuncsRev!((a) { return a.dtor; })(_sortedCtors._ctors);
115 118
 
  119
+    // clean all initialized flags
  120
+    foreach (m; _moduleinfo_array)
  121
+        m.flags = m.flags & ~MIctordone;
  122
+
116 123
     _sortedCtors.free();
117 124
     version (OSX)
118 125
     {}
@@ -220,242 +227,195 @@ void runModuleFuncsRev(alias getfp)(ModuleInfo*[] modules)
220 227
 
221 228
 SortedCtors sortCtors(ModuleInfo*[] modules)
222 229
 {
223  
-    SortedCtors result;
224  
-    result.alloc(modules.length);
225  
-    // Create an array of modules that will determine the order of construction
226  
-    // (and destruction in reverse).
227  
-    auto ctors = result._ctors;
228  
-    size_t ctoridx = 0;
  230
+    enum AllocaLimit = 100 * 1024; // 100KB
229 231
 
230  
-    // this pointer will identify the module where the cycle was detected.
231  
-    ModuleInfo *cycleModule;
  232
+    immutable size = modules.length * StackRec.sizeof;
232 233
 
233  
-    // allocate some stack arrays that will be used throughout the process.
234  
-    ubyte* p = cast(ubyte *)alloca(modules.length * ubyte.sizeof);
235  
-    auto reachable = p[0..modules.length];
236  
-
237  
-    p = cast(ubyte *)alloca(modules.length * ubyte.sizeof);
238  
-    auto flags = p[0..modules.length];
239  
-
240  
-
241  
-    // find all the non-trivial dependencies (that is, dependencies that have a
242  
-    // ctor or dtor) of a given module.  Doing this, we can 'skip over' the
243  
-    // trivial modules to get at the non-trivial ones.
244  
-    size_t _findDependencies(ModuleInfo *current, bool orig = true)
  234
+    if (!size)
245 235
     {
246  
-        auto idx = current.index;
247  
-        if(reachable[idx])
248  
-            return 0;
249  
-        size_t result = 0;
250  
-        reachable[idx] = 1;
251  
-        if(!orig && (flags[idx] & (MIctor | MIdtor)) && !(flags[idx] & MIstandalone))
252  
-            // non-trivial, stop here
253  
-            return result + 1;
254  
-        foreach (ModuleInfo *m; current.importedModules)
255  
-        {
256  
-            result += _findDependencies(m, false);
257  
-        }
  236
+        return SortedCtors.init;
  237
+    }
  238
+    else if (size <= AllocaLimit)
  239
+    {
  240
+        auto p = cast(ubyte*).alloca(size);
  241
+        p[0 .. size] = 0;
  242
+        return sortCtorsImpl(modules, (cast(StackRec*)p)[0 .. modules.length]);
  243
+    }
  244
+    else
  245
+    {
  246
+        auto p = cast(ubyte*).malloc(size);
  247
+        p[0 .. size] = 0;
  248
+        auto result = sortCtorsImpl(modules, (cast(StackRec*)p)[0 .. modules.length]);
  249
+        .free(p);
258 250
         return result;
259 251
     }
  252
+}
260 253
 
261  
-    void print(string msgs[]...)
262  
-    {
263  
-        version (unittest)
264  
-        {
265  
-            if (_inUnitTest)
266  
-                return;
267  
-        }
  254
+private:
268 255
 
269  
-        foreach (m; msgs)
270  
-        {
271  
-            // write message to stderr
272  
-            console(m);
273  
-        }
  256
+void print(string m)
  257
+{
  258
+    // write message to stderr
  259
+    console(m);
  260
+}
  261
+
  262
+void println(string m)
  263
+{
  264
+    print(m);
  265
+    version (Windows)
  266
+        print("\r\n");
  267
+    else
  268
+        print("\n");
  269
+}
  270
+
  271
+struct StackRec
  272
+{
  273
+    @property ModuleInfo* mod()
  274
+    {
  275
+        return _mods[_idx];
274 276
     }
275 277
 
276  
-    void println(string msgs[]...)
  278
+    ModuleInfo*[] _mods;
  279
+    size_t         _idx;
  280
+}
  281
+
  282
+void onCycleError(StackRec[] stack)
  283
+{
  284
+    version (unittest)
277 285
     {
278  
-        print(msgs);
279  
-        version(Windows)
280  
-            print("\r\n");
281  
-        else
282  
-            print("\n");
  286
+        if (_inUnitTest)
  287
+            goto Lerror;
283 288
     }
284 289
 
285  
-    bool printCycle(ModuleInfo *current, ModuleInfo *target, bool orig = true)
  290
+    println("Cycle detected between modules with ctors/dtors:");
  291
+    foreach (e; stack)
286 292
     {
287  
-        if(reachable[current.index])
288  
-            // already visited
289  
-            return false;
290  
-        if(current is target)
291  
-            // found path
292  
-            return true;
293  
-        reachable[current.index] = 1;
294  
-        if(!orig && (flags[current.index] & (MIctor | MIdtor)) && !(flags[current.index] & MIstandalone))
295  
-            // don't go through modules with ctors/dtors that aren't
296  
-            // standalone.
297  
-            return false;
298  
-        // search connections from current to see if we can get to target
299  
-        foreach (m; current.importedModules)
300  
-        {
301  
-            if(printCycle(m, target, false))
302  
-            {
303  
-                // found the path, print this module
304  
-                if(orig)
305  
-                    println("imported from ", current.name, " containing module ctor/dtor");
306  
-                else
307  
-                    println("   imported from (", current.name, ")");
308  
-                return true;
309  
-            }
310  
-        }
311  
-        return false;
  293
+        print(e.mod.name);
  294
+        print(" -> ");
312 295
     }
  296
+    println(stack[0].mod.name);
  297
+ Lerror:
  298
+    throw new Exception("Aborting!");
  299
+}
  300
+
  301
+private SortedCtors sortCtorsImpl(ModuleInfo*[] modules, StackRec[] stack)
  302
+{
  303
+    SortedCtors result;
  304
+    result.alloc(modules.length);
  305
+
  306
+    size_t stackidx;
  307
+    bool tlsPass;
  308
+
  309
+ Lagain:
  310
+
  311
+    const mask = tlsPass ? (MItlsctor | MItlsdtor) : (MIctor | MIdtor);
  312
+    auto ctors = tlsPass ? result._tlsctors : result._ctors;
  313
+    size_t cidx;
313 314
 
314  
-    // This function will determine the order of construction/destruction and
315  
-    // check for cycles.
316  
-    bool _checkModCtors2(ModuleInfo *current)
  315
+    ModuleInfo*[] mods = modules;
  316
+    size_t idx;
  317
+    while (true)
317 318
     {
318  
-        // we only get called if current has a dtor or a ctor, so no need to
319  
-        // check that.  First, determine what non-trivial elements are
320  
-        // reachable.
321  
-        reachable[] = 0;
322  
-        auto nmodules = _findDependencies(current);
323  
-
324  
-        // allocate the dependencies on the stack
325  
-        ModuleInfo **p = cast(ModuleInfo **)alloca(nmodules * (ModuleInfo*).sizeof);
326  
-        auto dependencies = p[0..nmodules];
327  
-        uint depidx = 0;
328  
-        // fill in the dependencies
329  
-        foreach (i, r; reachable)
  319
+        while (idx < mods.length)
330 320
         {
331  
-            if(r)
  321
+            auto m = mods[idx];
  322
+            auto fl = m.flags;
  323
+            if (fl & MIctorstart)
332 324
             {
333  
-                ModuleInfo *m = modules[i];
334  
-                if(m !is current && (flags[i] & (MIctor | MIdtor)) && !(flags[i] & MIstandalone))
  325
+                // trace back to cycle start
  326
+                fl &= ~MIctorstart;
  327
+                size_t start = stackidx;
  328
+                while (start--)
335 329
                 {
336  
-                    dependencies[depidx++] = m;
  330
+                    auto sm = stack[start].mod;
  331
+                    if (sm == m)
  332
+                        break;
  333
+                    fl |= sm.flags & MIctorstart;
  334
+                }
  335
+                assert(stack[start].mod == m);
  336
+                if (fl & MIctorstart)
  337
+                {
  338
+                    /* This is an illegal cycle, no partial order can be established
  339
+                     * because the import chain have contradicting ctor/dtor
  340
+                     * constraints.
  341
+                     */
  342
+                    onCycleError(stack[start .. stackidx]);
  343
+                }
  344
+                else
  345
+                {
  346
+                    /* This is also a cycle, but the import chain does not constrain
  347
+                     * the order of initialization, either because the imported
  348
+                     * modules have no ctors or the ctors are standalone.
  349
+                     */
  350
+                    ++idx;
337 351
                 }
338 352
             }
339  
-        }
340  
-        assert(depidx == nmodules);
341  
-
342  
-        // ok, now perform cycle detection
343  
-        auto curidx = current.index;
344  
-        flags[curidx] |= MIctorstart;
345  
-        bool valid = true;
346  
-        foreach (m; dependencies)
347  
-        {
348  
-            auto mflags = flags[m.index];
349  
-            if(mflags & MIctorstart)
350  
-            {
351  
-                // found a cycle, but we don't care if the MIstandalone flag is
352  
-                // set, this is a guarantee that there are no cycles in this
353  
-                // module (not sure what triggers it)
354  
-                println("Cyclic dependency in module ", m.name);
355  
-                cycleModule = m;
356  
-                valid = false;
357  
-
358  
-                // use the currently allocated dtor path to record the loop
359  
-                // that contains module ctors/dtors only.
360  
-                ctoridx = ctors.length;
361  
-            }
362  
-            else if(!(mflags & MIctordone))
363  
-            {
364  
-                valid = _checkModCtors2(m);
  353
+            else if (fl & MIctordone)
  354
+            {   // already visited => skip
  355
+                ++idx;
365 356
             }
366  
-
367  
-
368  
-            if(!valid)
  357
+            else
369 358
             {
370  
-                // cycle detected, now, we must print in reverse order the
371  
-                // module include cycle.  For this, we need to traverse the
372  
-                // graph of trivial modules again, this time printing them.
373  
-                reachable[] = 0;
374  
-                printCycle(current, m);
375  
-
376  
-                // record this as a module that was used in the loop.
377  
-                ctors[--ctoridx] = current;
378  
-                if(current is cycleModule)
  359
+                if (fl & mask)
379 360
                 {
380  
-                    // print the cycle
381  
-                    println("Cycle detected between modules with ctors/dtors:");
382  
-                    foreach (cm; ctors[ctoridx..$])
383  
-                    {
384  
-                        print(cm.name, " -> ");
  361
+                    if (fl & MIstandalone || !m.importedModules.length)
  362
+                    {   // trivial ctor => sort in
  363
+                        ctors[cidx++] = m;
  364
+                        m.flags = fl | MIctordone;
  365
+                    }
  366
+                    else
  367
+                    {   // non-trivial ctor => defer
  368
+                        m.flags = fl | MIctorstart;
385 369
                     }
386  
-                    println(cycleModule.name);
387  
-                    throw new Exception("Aborting!");
388 370
                 }
389  
-                return false;
390  
-            }
391  
-        }
392  
-        flags[curidx] = (flags[curidx] & ~MIctorstart) | MIctordone;
393  
-        // add this module to the construction order list
394  
-        ctors[ctoridx++] = current;
395  
-        return true;
396  
-    }
  371
+                else    // no ctor => mark as visited
  372
+                    m.flags = fl | MIctordone;
397 373
 
398  
-    void _checkModCtors3()
399  
-    {
400  
-        foreach (m; modules)
401  
-        {
402  
-            // TODO: Should null ModuleInfo be allowed?
403  
-            if (m is null) continue;
404  
-            auto flag = flags[m.index];
405  
-            if((flag & (MIctor | MIdtor)) && !(flag & MIctordone))
406  
-            {
407  
-                if(flag & MIstandalone)
  374
+                if (m.importedModules.length)
408 375
                 {
409  
-                    // no need to run a check on this one, but we do need to call its ctor/dtor
410  
-                    ctors[ctoridx++] = m;
  376
+                    /* Internal runtime error, dependency on an uninitialized
  377
+                     * module outside of the current module group.
  378
+                     */
  379
+                    (stackidx < modules.length) || assert(0);
  380
+
  381
+                    // recurse
  382
+                    stack[stackidx++] = StackRec(mods, idx);
  383
+                    idx  = 0;
  384
+                    mods = m.importedModules;
411 385
                 }
412  
-                else
413  
-                    _checkModCtors2(m);
414 386
             }
415 387
         }
416  
-    }
417 388
 
418  
-    // ok, now we need to assign indexes, and also initialize the flags
419  
-    foreach (uint i, m; modules)
420  
-    {
421  
-        // TODO: Should null ModuleInfo be allowed?
422  
-        if (m is null) continue;
423  
-        m.index = i;
424  
-        ubyte flag = m.flags & MIstandalone;
425  
-        if(m.dtor)
426  
-            flag |= MIdtor;
427  
-        if(m.ctor)
428  
-            flag |= MIctor;
429  
-        flags[i] = flag;
  389
+        if (stackidx)
  390
+        {   // pop old value from stack
  391
+            --stackidx;
  392
+            mods    = stack[stackidx]._mods;
  393
+            idx     = stack[stackidx]._idx;
  394
+            auto m  = mods[idx++];
  395
+            auto fl = m.flags;
  396
+            if (fl & mask && !(fl & MIctordone))
  397
+                ctors[cidx++] = m;
  398
+            m.flags = (fl & ~MIctorstart) | MIctordone;
  399
+        }
  400
+        else // done
  401
+            break;
430 402
     }
  403
+    // store final number
  404
+    tlsPass ? result._tlsctors : result._ctors = ctors[0 .. cidx];
431 405
 
432  
-    // everything's all set up for shared ctors
433  
-    _checkModCtors3();
434  
-
435  
-    // store the number of dtors/ctors
436  
-    result._ctors = result._ctors[0 .. ctoridx];
  406
+    // clean flags
  407
+    for (size_t i = 0; i < modules.length; ++i)
  408
+    {   auto m = modules[i];
  409
+        m.flags = m.flags & ~(MIctorstart | MIctordone);
  410
+    }
437 411
 
438  
-    // set up everything for tls ctors
439  
-    ctors = result._tlsctors;
440  
-    ctoridx = 0;
441  
-    foreach (i, m; modules)
  412
+    // rerun for TLS constructors
  413
+    if (!tlsPass)
442 414
     {
443  
-        // TODO: Should null ModuleInfo be allowed?
444  
-        if (m is null) continue;
445  
-        ubyte flag = m.flags & MIstandalone;
446  
-        if(m.tlsdtor)
447  
-            flag |= MIdtor;
448  
-        if(m.tlsctor)
449  
-            flag |= MIctor;
450  
-        flags[i] = flag;
  415
+        tlsPass = true;
  416
+        goto Lagain;
451 417
     }
452 418
 
453  
-    // ok, run it
454  
-    _checkModCtors3();
455  
-
456  
-    // store the number of dtors/ctors
457  
-    result._tlsctors = result._tlsctors[0 .. ctoridx];
458  
-
459 419
     return result;
460 420
 }
461 421
 
@@ -513,12 +473,9 @@ unittest
513 473
     {
514 474
         auto ptrs = [&m0, &m1, &m2];
515 475
         auto sorted = sortCtors(ptrs);
516  
-        foreach (i, m; ptrs)
517  
-        {
518  
-            assert(m.index == i);
519  
-            m.index = 0;
520  
-        }
521  
-        assert(sorted._ctors == dtors);
  476
+        foreach (m; ptrs)
  477
+            assert(!(m.flags & (MIctorstart | MIctordone)));
  478
+        assert(sorted._ctors    == dtors);
522 479
         assert(sorted._tlsctors == tlsdtors);
523 480
     }
524 481
 
@@ -596,4 +553,10 @@ unittest
596 553
     m1 = mockMI(MIctor);
597 554
     m2 = mockMI(MItlsctor, &m0);
598 555
     assertThrown!Throwable(checkExp());
  556
+
  557
+    // closed ctors cycle
  558
+    m0 = mockMI(MIctor, &m1);
  559
+    m1 = mockMI(MIstandalone | MIctor, &m2);
  560
+    m2 = mockMI(MIstandalone | MIctor, &m0);
  561
+    checkExp([&m1, &m2, &m0], []);
599 562
 }
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.