From 7da7bd6020a8bfc79cdcd4e2b9b979f3c5dadc91 Mon Sep 17 00:00:00 2001 From: David Nadlinger Date: Sun, 11 Sep 2016 17:50:34 +0100 Subject: [PATCH] Gracefully handle assertion errors during runtime startup/teardown assert() is used throughout the various bits and pieces involved in druntime startup and teardown. However, the default assert handler (throwing an AssertError) requires the GC and exception handling to be up and running, which is not the case for failures e.g. in the module registry code. Of course, these assertions should never be triggered in shipped versions of druntime. But they might be during development, and this change ensures that a sensible error message is printed instead of hitting an infinite recursion or a GC deadlock. --- src/core/exception.d | 19 +++++++++++++++++-- src/core/internal/abort.d | 10 ++++++++-- src/rt/dmain2.d | 3 +++ test/exceptions/line_trace.exp | 10 +++++----- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/core/exception.d b/src/core/exception.d index 701dff2c30..bc43f01d4b 100644 --- a/src/core/exception.d +++ b/src/core/exception.d @@ -416,6 +416,21 @@ deprecated void setAssertHandler( AssertHandler h ) @trusted nothrow @nogc assertHandler = h; } +private __gshared bool assertEHEnabled = false; +extern(C) void _d_setAssertEHEnabled( bool enabled ) +{ + assertEHEnabled = enabled; +} + +private void defaultAssertHandler( string file, size_t line, string msg ) nothrow +{ + if ( assertEHEnabled ) + throw msg ? new AssertError( msg, file, line ) : new AssertError( file, line ); + + import core.internal.abort; + abort( file, line, "Assertion failure during runtime startup/teardown", + msg ? ": " : ".", msg ); +} /////////////////////////////////////////////////////////////////////////////// // Overridable Callbacks @@ -434,7 +449,7 @@ deprecated void setAssertHandler( AssertHandler h ) @trusted nothrow @nogc extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ ) nothrow { if( _assertHandler is null ) - throw new AssertError( file, line ); + defaultAssertHandler( file, line, null ); _assertHandler( file, line, null); } @@ -452,7 +467,7 @@ extern (C) void onAssertError( string file = __FILE__, size_t line = __LINE__ ) extern (C) void onAssertErrorMsg( string file, size_t line, string msg ) nothrow { if( _assertHandler is null ) - throw new AssertError( msg, file, line ); + defaultAssertHandler( file, line, msg ); _assertHandler( file, line, msg ); } diff --git a/src/core/internal/abort.d b/src/core/internal/abort.d index d2d0eb8724..de5d94daff 100644 --- a/src/core/internal/abort.d +++ b/src/core/internal/abort.d @@ -5,6 +5,11 @@ module core.internal.abort; * code, and druntime is -release compiled. */ void abort(string msg, string filename = __FILE__, size_t line = __LINE__) @nogc nothrow @safe +{ + abort(filename, line, msg); +} + +void abort(string filename, size_t line, const(char)[][] msgs...) @nogc nothrow @safe { import core.stdc.stdlib: c_abort = abort; // use available OS system calls to print the message to stderr @@ -23,7 +28,7 @@ void abort(string msg, string filename = __FILE__, size_t line = __LINE__) @nogc auto h = (() @trusted => GetStdHandle(STD_ERROR_HANDLE))(); if(h == INVALID_HANDLE_VALUE) // attempt best we can to print the message - assert(0, msg); + assert(0, msgs[0]); void writeStr(const(char)[][] m...) @nogc nothrow @trusted { foreach(s; m) @@ -40,6 +45,7 @@ void abort(string msg, string filename = __FILE__, size_t line = __LINE__) @nogc UnsignedStringBuf strbuff; // write an appropriate message, then abort the program - writeStr("Aborting from ", filename, "(", line.unsignedToTempString(strbuff, 10), ") ", msg); + writeStr("Aborting from ", filename, "(", line.unsignedToTempString(strbuff, 10), "): "); + writeStr(msgs); c_abort(); } diff --git a/src/rt/dmain2.d b/src/rt/dmain2.d index 59f4b4ef4e..3441a56277 100644 --- a/src/rt/dmain2.d +++ b/src/rt/dmain2.d @@ -50,6 +50,7 @@ extern (C) void rt_moduleTlsDtor(); extern (C) void thread_joinAll(); extern (C) bool runModuleUnitTests(); extern (C) void _d_initMonoTime(); +extern (C) void _d_setAssertEHEnabled(bool enabled); version (OSX) { @@ -180,6 +181,7 @@ extern (C) int rt_init() initStaticDataGC(); lifetime_init(); rt_moduleCtor(); + _d_setAssertEHEnabled(true); rt_moduleTlsCtor(); return 1; } @@ -205,6 +207,7 @@ extern (C) int rt_term() { rt_moduleTlsDtor(); thread_joinAll(); + _d_setAssertEHEnabled(false); rt_moduleDtor(); gc_term(); finiSections(); diff --git a/test/exceptions/line_trace.exp b/test/exceptions/line_trace.exp index bec4cab697..f4faca6fc9 100644 --- a/test/exceptions/line_trace.exp +++ b/test/exceptions/line_trace.exp @@ -2,8 +2,8 @@ object.Exception@src/line_trace.d(17): exception ---------------- src/line_trace.d:17 void line_trace.f1() [ADDR] src/line_trace.d:5 _Dmain [ADDR] -src/rt/dmain2.d:472 _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv [ADDR] -src/rt/dmain2.d:447 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [ADDR] -src/rt/dmain2.d:472 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() [ADDR] -src/rt/dmain2.d:447 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [ADDR] -src/rt/dmain2.d:480 _d_run_main [ADDR] +src/rt/dmain2.d:475 _D2rt6dmain211_d_run_mainUiPPaPUAAaZiZ6runAllMFZ9__lambda1MFZv [ADDR] +src/rt/dmain2.d:450 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [ADDR] +src/rt/dmain2.d:475 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).runAll() [ADDR] +src/rt/dmain2.d:450 void rt.dmain2._d_run_main(int, char**, extern (C) int function(char[][])*).tryExec(scope void delegate()) [ADDR] +src/rt/dmain2.d:483 _d_run_main [ADDR]