Skip to content

Commit 9ab0b70

Browse files
committed
Merge pull request #649 from dawgfoto/fix11378
fix Issue 11378 - implicit runtime initialization/finalization is broken
2 parents 191b6e4 + d3c89cf commit 9ab0b70

14 files changed

+241
-137
lines changed

posix.mak

+7-3
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ $(DRUNTIME): $(OBJS) $(SRCS)
169169
$(DMD) -lib -of$(DRUNTIME) -Xfdruntime.json $(DFLAGS) $(SRCS) $(OBJS)
170170

171171
UT_MODULES:=$(patsubst src/%.d,$(OBJDIR)/%,$(SRCS))
172-
ADDITIONAL_TESTS:=
172+
ADDITIONAL_TESTS:=test/init_fini
173173
ADDITIONAL_TESTS+=$(if $(findstring $(OS),linux),test/shared,)
174174

175175
unittest : $(UT_MODULES) $(addsuffix /.run,$(ADDITIONAL_TESTS))
@@ -215,8 +215,12 @@ $(OBJDIR)/% : $(OBJDIR)/test_runner
215215
# succeeded, render the file new again
216216
@touch $@
217217

218-
test/%/.run: test/%/Makefile $(DRUNTIMESO)
219-
$(QUIET)$(MAKE) -C test/$* MODEL=$(MODEL) OS=$(OS) DMD=$(abspath $(DMD)) DRUNTIMESO=$(abspath $(DRUNTIMESO)) QUIET=$(QUIET)
218+
test/init_fini/.run: $(DRUNTIME)
219+
test/shared/.run: $(DRUNTIMESO)
220+
221+
test/%/.run: test/%/Makefile
222+
$(QUIET)$(MAKE) -C test/$* MODEL=$(MODEL) OS=$(OS) DMD=$(abspath $(DMD)) \
223+
DRUNTIME=$(abspath $(DRUNTIME)) DRUNTIMESO=$(abspath $(DRUNTIMESO)) QUIET=$(QUIET)
220224

221225
detab:
222226
detab $(MANIFEST)

src/core/runtime.d

+29-24
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ version (Windows) extern (C) void* rt_loadLibraryW(const wchar_t* name);
2424
/// C interface for Runtime.unloadLibrary, returns 1/0 instead of bool
2525
extern (C) int rt_unloadLibrary(void* ptr);
2626

27+
/// C interface for Runtime.initialize, returns 1/0 instead of bool
28+
extern(C) int rt_init();
29+
/// C interface for Runtime.terminate, returns 1/0 instead of bool
30+
extern(C) int rt_term();
31+
2732
private
2833
{
2934
alias bool function() ModuleUnitTester;
@@ -37,8 +42,6 @@ private
3742
extern (C) TraceHandler rt_getTraceHandler();
3843

3944
alias void delegate( Throwable ) ExceptionHandler;
40-
extern (C) bool rt_init( ExceptionHandler dg = null );
41-
extern (C) bool rt_term( ExceptionHandler dg = null );
4245

4346
extern (C) void* thread_stackBottom();
4447

@@ -101,40 +104,42 @@ struct Runtime
101104
* Initializes the runtime. This call is to be used in instances where the
102105
* standard program initialization process is not executed. This is most
103106
* often in shared libraries or in libraries linked to a C program.
104-
*
105-
* Params:
106-
* dg = A delegate which will receive any exception thrown during the
107-
* initialization process or null if such exceptions should be
108-
* discarded.
107+
* If the runtime was already successfully initialized this returns true.
108+
* Each call to initialize must be paired by a call to $(LREF, terminate).
109109
*
110110
* Returns:
111-
* true if initialization succeeds and false if initialization fails.
111+
* true if initialization succeeded or false if initialization failed.
112112
*/
113-
static bool initialize( ExceptionHandler dg = null )
113+
static bool initialize()
114+
{
115+
return !!rt_init();
116+
}
117+
118+
deprecated("Please use the overload of Runtime.initialize that takes no argument.")
119+
static bool initialize(ExceptionHandler dg = null)
114120
{
115-
return rt_init( dg );
121+
return !!rt_init();
116122
}
117123

118124

119125
/**
120-
* Terminates the runtime. This call is to be used in instances
121-
* where the standard program termination process will not be not
122-
* executed. This is most often in shared libraries or in
123-
* libraries linked to a C program. All non-daemon threads must be
124-
* joined or detached prior to calling this function. See also
125-
* $(CXREF thread, thread_joinAll) and $(CXREF thread, thread_detachThis).
126-
*
127-
* Params:
128-
* dg = A delegate which will receive any exception thrown during the
129-
* termination process or null if such exceptions should be
130-
* discarded.
126+
* Terminates the runtime. This call is to be used in instances where the
127+
* standard program termination process will not be not executed. This is
128+
* most often in shared libraries or in libraries linked to a C program.
129+
* If the runtime was not successfully initialized the function returns false.
131130
*
132131
* Returns:
133-
* true if termination succeeds and false if termination fails.
132+
* true if termination succeeded or false if termination failed.
134133
*/
135-
static bool terminate( ExceptionHandler dg = null )
134+
static bool terminate()
135+
{
136+
return !!rt_term();
137+
}
138+
139+
deprecated("Please use the overload of Runtime.terminate that takes no argument.")
140+
static bool terminate(ExceptionHandler dg = null)
136141
{
137-
return rt_term( dg );
142+
return !!rt_term();
138143
}
139144

140145

src/rt/dmain2.d

+53-83
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,25 @@ extern (C) __gshared bool rt_trapExceptions = true;
148148

149149
alias void delegate(Throwable) ExceptionHandler;
150150

151+
/**
152+
* Keep track of how often rt_init/rt_term were called.
153+
*/
154+
shared size_t _initCount;
155+
151156
/**********************************************
152157
* Initialize druntime.
153158
* If a C program wishes to call D code, and there's no D main(), then it
154159
* must call rt_init() and rt_term().
155-
* If it fails, call dg. Except that what dg might be
156-
* able to do is undetermined, since the state of druntime
157-
* will not be known.
158-
* This needs rethinking.
159160
*/
160-
extern (C) bool rt_init(ExceptionHandler dg = null)
161+
extern (C) int rt_init()
161162
{
163+
/* @@BUG 11380 @@ Need to synchronize rt_init/rt_term calls for
164+
version (Shared) druntime, because multiple C threads might
165+
initialize different D libraries without knowing about the
166+
shared druntime. Also we need to attach any thread that calls
167+
rt_init. */
168+
if (_initCount++) return 1;
169+
162170
_STI_monitor_staticctor();
163171
_STI_critical_init();
164172

@@ -169,68 +177,45 @@ extern (C) bool rt_init(ExceptionHandler dg = null)
169177
initStaticDataGC();
170178
rt_moduleCtor();
171179
rt_moduleTlsCtor();
172-
return true;
180+
return 1;
173181
}
174-
catch (Throwable e)
182+
catch (Throwable t)
175183
{
176-
/* Note that if we get here, the runtime is in an unknown state.
177-
* I'm not sure what the point of calling dg is.
178-
*/
179-
if (dg)
180-
dg(e);
181-
else
182-
throw e; // rethrow, don't silently ignore error
183-
/* Rethrow, and the two STD functions aren't called?
184-
* This needs rethinking.
185-
*/
184+
_initCount = 0;
185+
printThrowable(t);
186186
}
187187
_STD_critical_term();
188188
_STD_monitor_staticdtor();
189-
return false;
189+
return 0;
190190
}
191191

192192
/**********************************************
193193
* Terminate use of druntime.
194-
* If it fails, call dg. Except that what dg might be
195-
* able to do is undetermined, since the state of druntime
196-
* will not be known.
197-
* This needs rethinking.
198194
*/
199-
extern (C) bool rt_term(ExceptionHandler dg = null)
195+
extern (C) int rt_term()
200196
{
197+
if (!_initCount) return 0; // was never initialized
198+
if (--_initCount) return 1;
199+
201200
try
202201
{
203-
/* Check that all other non-daemon threads have finished
204-
* execution before calling the shared module destructors.
205-
* Calling thread_joinAll here would be too late because other
206-
* shared libraries might have already been
207-
* destructed/unloaded.
208-
*/
209-
import core.thread : Thread;
210-
auto tthis = Thread.getThis();
211-
foreach (t; Thread)
212-
{
213-
if (t !is tthis && t.isRunning && !t.isDaemon)
214-
assert(0, "Can only call rt_term when all non-daemon threads have been joined or detached.");
215-
}
216-
217202
rt_moduleTlsDtor();
203+
thread_joinAll();
218204
rt_moduleDtor();
219205
gc_term();
220206
finiSections();
221-
return true;
207+
return 1;
222208
}
223-
catch (Throwable e)
209+
catch (Throwable t)
224210
{
225-
if (dg)
226-
dg(e);
211+
printThrowable(t);
227212
}
228213
finally
229214
{
230215
_STD_critical_term();
231216
_STD_monitor_staticdtor();
232217
}
233-
return false;
218+
return 0;
234219
}
235220

236221
/***********************************
@@ -392,29 +377,6 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
392377

393378
void tryExec(scope void delegate() dg)
394379
{
395-
static void print(Throwable t)
396-
{
397-
void sink(const(char)[] buf) nothrow
398-
{
399-
printf("%.*s", cast(int)buf.length, buf.ptr);
400-
}
401-
402-
for (; t; t = t.next)
403-
{
404-
t.toString(&sink); sink("\n");
405-
406-
auto e = cast(Error)t;
407-
if (e is null || e.bypassedException is null) continue;
408-
409-
sink("=== Bypassed ===\n");
410-
for (auto t2 = e.bypassedException; t2; t2 = t2.next)
411-
{
412-
t2.toString(&sink); sink("\n");
413-
}
414-
sink("=== ~Bypassed ===\n");
415-
}
416-
}
417-
418380
if (trapExceptions)
419381
{
420382
try
@@ -423,7 +385,7 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
423385
}
424386
catch (Throwable t)
425387
{
426-
print(t);
388+
printThrowable(t);
427389
result = EXIT_FAILURE;
428390
}
429391
}
@@ -441,33 +403,18 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
441403
// the user's main function. If main terminates with an exception,
442404
// the exception is handled and then cleanup begins. An exception
443405
// thrown during cleanup, however, will abort the cleanup process.
444-
void runMain()
445-
{
446-
if (runModuleUnitTests())
447-
tryExec({ result = mainFunc(args); });
448-
else
449-
result = EXIT_FAILURE;
450-
451-
tryExec({thread_joinAll();});
452-
}
453-
454-
void runMainWithInit()
406+
void runAll()
455407
{
456408
if (rt_init() && runModuleUnitTests())
457409
tryExec({ result = mainFunc(args); });
458410
else
459411
result = EXIT_FAILURE;
460412

461-
tryExec({thread_joinAll();});
462-
463413
if (!rt_term())
464414
result = (result == EXIT_SUCCESS) ? EXIT_FAILURE : result;
465415
}
466416

467-
version (linux) // initialization is done in rt.sections_linux
468-
tryExec(&runMain);
469-
else
470-
tryExec(&runMainWithInit);
417+
tryExec(&runAll);
471418

472419
// Issue 10344: flush stdout and return nonzero on failure
473420
if (.fflush(.stdout) != 0)
@@ -481,3 +428,26 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc)
481428

482429
return result;
483430
}
431+
432+
private void printThrowable(Throwable t)
433+
{
434+
void sink(const(char)[] buf) nothrow
435+
{
436+
printf("%.*s", cast(int)buf.length, buf.ptr);
437+
}
438+
439+
for (; t; t = t.next)
440+
{
441+
t.toString(&sink); sink("\n");
442+
443+
auto e = cast(Error)t;
444+
if (e is null || e.bypassedException is null) continue;
445+
446+
sink("=== Bypassed ===\n");
447+
for (auto t2 = e.bypassedException; t2; t2 = t2.next)
448+
{
449+
t2.toString(&sink); sink("\n");
450+
}
451+
sink("=== ~Bypassed ===\n");
452+
}
453+
}

0 commit comments

Comments
 (0)