-
-
Notifications
You must be signed in to change notification settings - Fork 417
Make AssertError a singleton #1710
Changes from all commits
e144b99
b7598eb
e733c29
ed3fb00
032c00a
4531dbb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,11 @@ unittest | |
*/ | ||
class AssertError : Error | ||
{ | ||
@safe pure nothrow this() | ||
{ | ||
super(null); | ||
} | ||
|
||
@safe pure nothrow this( string file, size_t line ) | ||
{ | ||
this(cast(Throwable)null, file, line); | ||
|
@@ -63,8 +68,14 @@ class AssertError : Error | |
} | ||
} | ||
|
||
unittest | ||
@safe unittest | ||
{ | ||
{ | ||
auto ae = perThreadInstance!AssertError; | ||
assert(typeid(ae) is typeid(AssertError)); | ||
assert(ae.file is null); | ||
assert(ae.line == 0); | ||
} | ||
{ | ||
auto ae = new AssertError("hello", 42); | ||
assert(ae.file == "hello"); | ||
|
@@ -416,12 +427,65 @@ deprecated void setAssertHandler( AssertHandler h ) @trusted nothrow @nogc | |
assertHandler = h; | ||
} | ||
|
||
/* | ||
Returns a perThreadInstance object of class type `C`, i.e. repeated calls to | ||
`perThreadInstance!C` within a given thread return the same instance. For that | ||
reason, no constructor parameters are accepted; `C` must allow default | ||
construction. | ||
|
||
Different threads own different instances of `C`. | ||
|
||
The destructor of the perThreadInstance object is called during application's | ||
exit. | ||
|
||
Returns: A thread-local instance of type `C`. | ||
|
||
Throws: If the default constructor of `C` throws. | ||
*/ | ||
private C perThreadInstance(C)() if (is(C == class) && is(typeof(new C))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There already is a |
||
{ | ||
static size_t[1 + (__traits(classInstanceSize, C) - 1) / size_t.sizeof] b; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would have been simpler to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. alignment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be sufficiently paranoid, better add a check for the instance alignment not being larger than size_t then (SIMD vectors, …). |
||
static bool initialized = false; | ||
auto result = () @trusted { return cast(C) b.ptr; }(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we're druntime, I think it's worth going the extra mile (i.e. do the necessary safe-ty hacks - see line 760) in order to return |
||
if (!initialized) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not do an early exit:
Is it just because you don't want to type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm glad you're giving the same advice I often do :). I'd say when the if is short things are fine either way. |
||
{ | ||
assert(typeid(C).initializer.length == b.length * size_t.sizeof); | ||
import core.stdc.string; | ||
() @trusted | ||
{ | ||
memcpy(b.ptr, typeid(C).initializer.ptr, b.length); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory the following could be more efficient There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this code will be executed once per run really |
||
}(); | ||
static if (is(typeof(result.__ctor()))) | ||
result.__ctor; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may need to make sure that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm... this is not GC-allocated memory so it does not have block attributes. No? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @andralex correct. |
||
import core.internal.traits; | ||
static if (hasElaborateDestructor!C) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to a quick test: import std.traits, core.exception;
static assert(hasElaborateDestructor!AssertError);
// Error: static assert (hasElaborateDestructor!(AssertError)) is false So it would be nice to have this line covered. Otherwise, LGTM. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. I think we can defer coverage of that to the time we get to use this private function elsewhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm... I think just remove this static if. There is always a destructor for a class (i.e. rt_finalize2 will always call destructors, even for Object). |
||
{ | ||
import core.stdc.stdlib; | ||
static void destroy() | ||
{ | ||
(cast(C) b.ptr).__dtor; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you don't want to leverage
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, this was actually a problem with destroy before we fixed it. |
||
} | ||
atexit(&destroy); | ||
} | ||
initialized = true; | ||
} | ||
return result; | ||
} | ||
|
||
unittest | ||
{ | ||
static class C | ||
{ | ||
~this() {} | ||
} | ||
// Ensure coverage of the atexit code | ||
assert(perThreadInstance!C !is null); | ||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// Overridable Callbacks | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
|
||
/** | ||
* A callback for assert errors in D. The user-supplied assert handler will | ||
* be called if one has been supplied, otherwise an $(LREF AssertError) will be | ||
|
@@ -433,9 +497,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 ); | ||
_assertHandler( file, line, null); | ||
onAssertErrorMsg(file, line, null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That should really be done on the compiler level one day There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mathias-lang-sociomantic yes, and it will There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @WalterBright : Having two function seems unnecessary, as the case of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yah, that's what was happening anyway (with duplicated code) |
||
} | ||
|
||
|
||
|
@@ -452,7 +514,13 @@ 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 ); | ||
{ | ||
auto e = perThreadInstance!AssertError; | ||
e.msg = msg; | ||
e.file = file; | ||
e.line = line; | ||
throw e; | ||
} | ||
_assertHandler( file, line, msg ); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the standard Ddoc function documentation style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At this point the function is private so I just added a casual comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added nicer dox