|  | import core.memory; | 
|  | import core.sync.condition; | 
|  | import core.sync.mutex; | 
|  | import core.thread; | 
|  |  | 
|  | __gshared Condition g_cond; | 
|  | __gshared Mutex g_mutex; | 
|  | __gshared int g_step = 0; | 
|  |  | 
|  | class C | 
|  | { | 
|  | ~this() | 
|  | { | 
|  | import core.stdc.stdlib; | 
|  | abort();    // this gets triggered although the instance always stays referenced | 
|  | } | 
|  | } | 
|  |  | 
|  | C c; | 
|  |  | 
|  | static this() | 
|  | { | 
|  | c = new C; | 
|  | } | 
|  |  | 
|  | static ~this() | 
|  | { | 
|  | import core.memory; | 
|  | GC.free(cast(void*)c); // free without destruction to avoid triggering abort() | 
|  | } | 
|  |  | 
|  | void test() | 
|  | { | 
|  | assert(c !is null); | 
|  |  | 
|  | // notify the main thread of the finished initialization | 
|  | synchronized (g_mutex) g_step = 1; | 
|  | g_cond.notifyAll(); | 
|  |  | 
|  | // wait until the GC collection is done | 
|  | synchronized (g_mutex) { | 
|  | while (g_step != 2) | 
|  | g_cond.wait(); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | void main() | 
|  | { | 
|  | g_mutex = new Mutex; | 
|  | g_cond = new Condition(g_mutex); | 
|  |  | 
|  | auto th = new Thread(&test); | 
|  | th.start(); | 
|  |  | 
|  | // wait for thread to be fully initialized | 
|  | synchronized (g_mutex) { | 
|  | while (g_step != 1) | 
|  | g_cond.wait(); | 
|  | } | 
|  |  | 
|  | // this causes the other thread's C instance to be reaped with the bug present | 
|  | GC.collect(); | 
|  |  | 
|  | // allow the thread to shut down | 
|  | synchronized (g_mutex) g_step = 2; | 
|  | g_cond.notifyAll(); | 
|  |  | 
|  | th.join(); | 
|  | } |