-
-
Notifications
You must be signed in to change notification settings - Fork 411
[RFC] Add pure fail-fast assertMalloc, assertCalloc, assertRealloc #2276
Changes from all commits
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 | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -810,6 +810,106 @@ struct GC | |||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Pure variants of C's memory allocation functions `malloc`, `calloc`, and | ||||||||||||||||||
| * `realloc` that achieve purity by aborting the program on failure so | ||||||||||||||||||
| * they never visibly change errno. | ||||||||||||||||||
| * | ||||||||||||||||||
| * The functions may terminate the program using `onOutOfMemoryError` or | ||||||||||||||||||
| * `assert(0)`. These functions' purity guarantees no longer hold if | ||||||||||||||||||
| * the program continues execution after catching AssertError or | ||||||||||||||||||
| * OutOfMemoryError. | ||||||||||||||||||
| * | ||||||||||||||||||
| * A similar fail-fast variant of C's memory deallocation function `free` | ||||||||||||||||||
| * is not provided due to the lack of a portable and efficient way to test | ||||||||||||||||||
| * for failure. If purity is required `pureFree` may be used. | ||||||||||||||||||
| * | ||||||||||||||||||
| * See_Also: | ||||||||||||||||||
| * $(LREF pureMalloc), $(LREF pureCalloc), $(LREF pureRealloc), | ||||||||||||||||||
| * and $(LREF pureFree) which achieve purity by saving and restoring | ||||||||||||||||||
| * errno. | ||||||||||||||||||
| * $(LINK2 https://dlang.org/library/object/error.html, Error class documentation) | ||||||||||||||||||
| * on the danger of continued execution after catching an Error. | ||||||||||||||||||
| */ | ||||||||||||||||||
| void* assertMalloc()(size_t size) @nogc nothrow pure @trusted | ||||||||||||||||||
| { | ||||||||||||||||||
| void* ret = fakePureMalloc(size); | ||||||||||||||||||
| if (!ret && size) | ||||||||||||||||||
| { | ||||||||||||||||||
| version (D_Exceptions) | ||||||||||||||||||
| { | ||||||||||||||||||
| import core.exception : onOutOfMemoryError; | ||||||||||||||||||
| onOutOfMemoryError(); | ||||||||||||||||||
| } | ||||||||||||||||||
| else assert(0, "Memory allocation failed"); | ||||||||||||||||||
| } | ||||||||||||||||||
| return ret; | ||||||||||||||||||
| } | ||||||||||||||||||
| /// ditto | ||||||||||||||||||
| void* assertCalloc()(size_t nmemb, size_t size) @nogc nothrow pure @trusted | ||||||||||||||||||
| { | ||||||||||||||||||
| void* ret = fakePureCalloc(nmemb, size); | ||||||||||||||||||
| if (!ret && nmemb && size) | ||||||||||||||||||
| { | ||||||||||||||||||
| version (D_Exceptions) | ||||||||||||||||||
| { | ||||||||||||||||||
| import core.exception : onOutOfMemoryError; | ||||||||||||||||||
| onOutOfMemoryError(); | ||||||||||||||||||
| } | ||||||||||||||||||
| else assert(0, "Memory allocation failed"); | ||||||||||||||||||
| } | ||||||||||||||||||
| return ret; | ||||||||||||||||||
| } | ||||||||||||||||||
| /// ditto | ||||||||||||||||||
| void* assertRealloc()(void* ptr, size_t size) @nogc nothrow pure @system | ||||||||||||||||||
|
Member
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 realloc() free's memory, it cannot ever be considered pure.
Contributor
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.
Member
Author
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 is also already a Lines 840 to 847 in db2910e
|
||||||||||||||||||
| { | ||||||||||||||||||
| void* ret = fakePureRealloc(ptr, size); | ||||||||||||||||||
| if (!ret && size) | ||||||||||||||||||
| { | ||||||||||||||||||
| version (D_Exceptions) | ||||||||||||||||||
| { | ||||||||||||||||||
| import core.exception : onOutOfMemoryError; | ||||||||||||||||||
| onOutOfMemoryError(); | ||||||||||||||||||
| } | ||||||||||||||||||
| else assert(0, "Memory allocation failed"); | ||||||||||||||||||
| } | ||||||||||||||||||
| return ret; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// | ||||||||||||||||||
| @nogc nothrow pure @system unittest | ||||||||||||||||||
| { | ||||||||||||||||||
| void[] allocate(size_t n) pure | ||||||||||||||||||
| { | ||||||||||||||||||
| return assertMalloc(n)[0 .. n]; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| auto buf = allocate(100); | ||||||||||||||||||
| assert(buf.length == 100); | ||||||||||||||||||
| pureFree(buf.ptr); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| @nogc nothrow pure @system unittest | ||||||||||||||||||
| { | ||||||||||||||||||
| const int errno = fakePureErrno(); | ||||||||||||||||||
|
Contributor
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.
Edit: I mean it won't work in -betterC until
Member
Author
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. Should be fixed now. |
||||||||||||||||||
|
|
||||||||||||||||||
| void* x = assertMalloc(10); // normal allocation | ||||||||||||||||||
| assert(errno == fakePureErrno()); // errno shouldn't change | ||||||||||||||||||
| assert(x !is null); // allocation should succeed | ||||||||||||||||||
|
|
||||||||||||||||||
| x = assertRealloc(x, 20); // normal reallocation | ||||||||||||||||||
| assert(errno == fakePureErrno()); // errno shouldn't change | ||||||||||||||||||
| assert(x !is null); // allocation should succeed | ||||||||||||||||||
|
|
||||||||||||||||||
| fakePureFree(x); | ||||||||||||||||||
|
|
||||||||||||||||||
| void* y = assertCalloc(10, 1); // normal zeroed allocation | ||||||||||||||||||
| assert(errno == fakePureErrno()); // errno shouldn't change | ||||||||||||||||||
| assert(y !is null); // allocation should succeed | ||||||||||||||||||
|
|
||||||||||||||||||
| fakePureFree(y); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * Pure variants of C's memory allocation functions `malloc`, `calloc`, and | ||||||||||||||||||
| * `realloc` and deallocation function `free`. | ||||||||||||||||||
|
|
@@ -818,35 +918,38 @@ struct GC | |||||||||||||||||
| * Purity is achieved by saving and restoring the value of `errno`, thus | ||||||||||||||||||
| * behaving as if it were never changed. | ||||||||||||||||||
| * | ||||||||||||||||||
| * | ||||||||||||||||||
| * See_Also: | ||||||||||||||||||
| * $(LINK2 https://dlang.org/spec/function.html#pure-functions, D's rules for purity), | ||||||||||||||||||
| * which allow for memory allocation under specific circumstances. | ||||||||||||||||||
| * $(LREF assertMalloc), $(LREF assertCalloc), and $(LREF assertRealloc) | ||||||||||||||||||
| * which achieve purity by terminating on failure. | ||||||||||||||||||
| */ | ||||||||||||||||||
| void* pureMalloc(size_t size) @trusted pure @nogc nothrow | ||||||||||||||||||
| void* pureMalloc()(size_t size) @nogc nothrow pure @trusted | ||||||||||||||||||
| { | ||||||||||||||||||
| const errnosave = fakePureErrno(); | ||||||||||||||||||
| void* ret = fakePureMalloc(size); | ||||||||||||||||||
| fakePureErrno() = errnosave; | ||||||||||||||||||
| return ret; | ||||||||||||||||||
| } | ||||||||||||||||||
| /// ditto | ||||||||||||||||||
| void* pureCalloc(size_t nmemb, size_t size) @trusted pure @nogc nothrow | ||||||||||||||||||
| void* pureCalloc()(size_t nmemb, size_t size) @nogc nothrow pure @trusted | ||||||||||||||||||
| { | ||||||||||||||||||
| const errnosave = fakePureErrno(); | ||||||||||||||||||
| void* ret = fakePureCalloc(nmemb, size); | ||||||||||||||||||
| fakePureErrno() = errnosave; | ||||||||||||||||||
| return ret; | ||||||||||||||||||
| } | ||||||||||||||||||
| /// ditto | ||||||||||||||||||
| void* pureRealloc(void* ptr, size_t size) @system pure @nogc nothrow | ||||||||||||||||||
| void* pureRealloc()(void* ptr, size_t size) @nogc nothrow pure @system | ||||||||||||||||||
| { | ||||||||||||||||||
| const errnosave = fakePureErrno(); | ||||||||||||||||||
| void* ret = fakePureRealloc(ptr, size); | ||||||||||||||||||
| fakePureErrno() = errnosave; | ||||||||||||||||||
| return ret; | ||||||||||||||||||
| } | ||||||||||||||||||
| /// ditto | ||||||||||||||||||
| void pureFree(void* ptr) @system pure @nogc nothrow | ||||||||||||||||||
| void pureFree()(void* ptr) @nogc nothrow pure @system | ||||||||||||||||||
| { | ||||||||||||||||||
| const errnosave = fakePureErrno(); | ||||||||||||||||||
| fakePureFree(ptr); | ||||||||||||||||||
|
|
@@ -899,20 +1002,51 @@ void pureFree(void* ptr) @system pure @nogc nothrow | |||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // locally purified for internal use here only | ||||||||||||||||||
| static import core.stdc.errno; | ||||||||||||||||||
|
|
||||||||||||||||||
| private enum canLinkToErrno = __traits(getOverloads, core.stdc.errno, "errno").length == 1 | ||||||||||||||||||
| && __traits(getLinkage, core.stdc.errno.errno) == "C"; | ||||||||||||||||||
|
|
||||||||||||||||||
| version (D_BetterC) @nogc nothrow private @system | ||||||||||||||||||
| { | ||||||||||||||||||
| static if (canLinkToErrno) | ||||||||||||||||||
| { | ||||||||||||||||||
| extern(C) | ||||||||||||||||||
| pragma(mangle, __traits(identifier, core.stdc.errno.errno)) ref int fakePureErrno() pure; | ||||||||||||||||||
| } | ||||||||||||||||||
| else | ||||||||||||||||||
| { | ||||||||||||||||||
| ref int _errno()() { return core.stdc.errno.errno; } | ||||||||||||||||||
|
|
||||||||||||||||||
| private ref int fakePureErrno()() pure | ||||||||||||||||||
| { | ||||||||||||||||||
| alias FakePureErrno = ref int function() @nogc nothrow pure; | ||||||||||||||||||
| return (cast(FakePureErrno) &_errno!())(); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| else | ||||||||||||||||||
| extern (C) private @system @nogc nothrow | ||||||||||||||||||
| { | ||||||||||||||||||
| ref int fakePureErrnoImpl() | ||||||||||||||||||
| { | ||||||||||||||||||
| import core.stdc.errno; | ||||||||||||||||||
| return errno(); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| static if (canLinkToErrno) | ||||||||||||||||||
| { | ||||||||||||||||||
| // We don't need fakePureErrnoImpl but for now we'll keep it for compatibility. | ||||||||||||||||||
| pragma(mangle, __traits(identifier, core.stdc.errno.errno)) ref int fakePureErrno() pure; | ||||||||||||||||||
| } | ||||||||||||||||||
| else | ||||||||||||||||||
| { | ||||||||||||||||||
| pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno() pure; | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| extern (C) private pure @system @nogc nothrow | ||||||||||||||||||
| { | ||||||||||||||||||
| pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno(); | ||||||||||||||||||
|
|
||||||||||||||||||
| pragma(mangle, "malloc") void* fakePureMalloc(size_t); | ||||||||||||||||||
| pragma(mangle, "calloc") void* fakePureCalloc(size_t nmemb, size_t size); | ||||||||||||||||||
| pragma(mangle, "realloc") void* fakePureRealloc(void* ptr, size_t size); | ||||||||||||||||||
|
|
||||||||||||||||||
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.
I think this comment needs updating.
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.
I kept the mention of the possibility so if we later change to using
onOutOfMemoryErrorno one will be blindsided. Do you think it's better to remove it because that's too unlikely?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.
No need to change anything immediately, IMO. We can wait to see how the review goes. It just eventually needs to reflect the final implementation.