Add pure wrappers for C style allocation#1746
Conversation
src/core/stdc/stdlib.d
Outdated
| /// | ||
| void* pureMalloc(size_t size) pure | ||
| { | ||
| const errno = pureGetErrno(); |
There was a problem hiding this comment.
Why not use void* or auto here? malloc's return type isn't going to change anytime soon .
There was a problem hiding this comment.
Changed return type to void*.
src/core/stdc/stdlib.d
Outdated
| cast(void)pureSetErrno(errno); | ||
| return ret; | ||
| } | ||
| extern (C) pure pragma(mangle, "getErrno") int pureGetErrno(); // for internal use |
There was a problem hiding this comment.
Checking in with LDC developers @kinke @JohanEngelen, does this work on LDC?
BTW, I bumped into a related extern (C) issue on LDC - #1728 (comment)
There was a problem hiding this comment.
Also, since those functions are for internal use, why not make them private or package, or declare them inside pureMalloc? They should be used only in a controlled manner, and reviewed case-by-case.
There was a problem hiding this comment.
Also, since those functions are for internal use,
I think the plan is to make this public
There was a problem hiding this comment.
does this work on LDC?
Multiple extern (C) forward-declarations with the same mangled name aren't a problem. This works.
There was a problem hiding this comment.
So are you saying I should rename these declarations to getErrno() and setErrno()?
There was a problem hiding this comment.
Well just saying that ldc-developers/ldc#1020 doesn't apply to these extern (C) fwd declarations. While declaring them with their real name should work too, I think the explicit pure in the function name makes a lot of sense in this case.
There was a problem hiding this comment.
Ok. I keep it as that for now.
There was a problem hiding this comment.
This currently fails to compile as
stdlib.d(166,23): Error: pure function 'core.stdc.stdlib.pureMalloc' cannot call impure function 'core.stdc.stdlib.malloc'
I guess I have to use a local pure declaration of malloc as _pureMalloc then?
|
Andrei stated this should go in core/memory.d |
9ca974d to
c410c29
Compare
|
So, should I move it to |
|
Should I add similar wrappers for |
Yes
|
81c7849 to
0ee3f5b
Compare
|
Done. |
f20b859 to
3500531
Compare
|
What about potential race conditions when restoring
And here:
|
|
Pretty sure errno is thread local. |
src/core/memory.d
Outdated
| { | ||
| const errno = _pureGetErrno(); | ||
| void* ret = _pureMalloc(size); | ||
| cast(void)_pureSetErrno(errno); |
src/core/memory.d
Outdated
| /// Pure variant of `malloc` that doesn't affect global variable `errno`. | ||
| void* pureMalloc(size_t size) pure | ||
| { | ||
| const errno = _pureGetErrno(); |
There was a problem hiding this comment.
Does druntime use this convention of prefixing private symbols with one underscore?
| pragma(mangle, "malloc") void* _pureMalloc(size_t); | ||
| pragma(mangle, "calloc") void* _pureCalloc(size_t nmemb, size_t size); | ||
| pragma(mangle, "realloc") void* _pureRealloc(void* ptr, size_t size); | ||
| } |
There was a problem hiding this comment.
please add one unittest to cover all, or at best short ddoc unittests showing the use of each inside a pure function
|
OK, this is good progress. Thanks! |
|
One idea for documentation: simply group all three together with documentation such as "Pure wrappers for the C stdlib memory allocation primitives |
|
I suspect |
src/core/memory.d
Outdated
| } | ||
| } | ||
|
|
||
| /// Pure variant of `malloc` that doesn't affect global variable `errno`. |
There was a problem hiding this comment.
Please replace this with the following:
/**
* Pure variants of C's memory allocation functions. Purity is achieved via
* resetting the `errno` to it's value prior to being called, removing the
* function's global state mutation.
*
* See_Also:
* $(LINK2 https://dlang.org/spec/function.html#pure-functions, D's rules for purity),
* which allow for memory allocation under specific circumstances.
*/
IMO it addresses the intuitiveness of a pure memory allocation function for the reader.
There was a problem hiding this comment.
The teleological description is better than the description in terms of implementation details. I.e. if a platform offered access to an errno-less malloc (as e.g. would be easy to hack with jemalloc), pureMalloc would use that.
src/core/memory.d
Outdated
| { | ||
| const errno = fakePureGetErrno(); | ||
| void* ret = fakePureMalloc(size); | ||
| cast(void)fakePureSetErrno(errno); |
There was a problem hiding this comment.
I'd say it's more efficient to guard the call like this:
if (!ret || errno != ERR_OK) cast(void)fakePureSetErrno(errno);The common case is malloc succeeds and the previous state was ERR_OK so no need to reset errno.
There was a problem hiding this comment.
-
ERR_OKis not defined. I guess you mean0, right? -
I can't find any good docs on
{set,get}Errno. Shouldn't we just read and write theerrnodirectly instead of calling{set,get}Errno? Provided thaterrnois thread-local.
Otherwise, LGTM.
|
Ready for squashing? |
|
lgtm |
b588fbd to
c1c578c
Compare
|
Squashed. |
|
BTW: Is manual squashing still needed by PR authors? I heard somewhere that Github had added an automatic squash option upon merge. |
I forgot. cc @MartinNowak @CyberShadow |
|
One more thing - please add a doc unittest featuring the use of all three. Thanks! |
GitHub squashing is already enabled and the dlang bot supports squashed merges. @CyberShadow had some concerns related to digger and PR number inference, but IIRC they have been resolved as the PR number is part of the commit message? |
1b4d9ba to
9c778d4
Compare
src/core/memory.d
Outdated
| { | ||
| void* p = pureMalloc(n); | ||
| enforce(n == 0 || p !is null); | ||
|
|
There was a problem hiding this comment.
Vertical space in a five-liner is a net negative (ironically your implementations themselves don't have useless vertical space, and indeed they shouldn't). There is no value added by it.
src/core/memory.d
Outdated
| void* p = pureMalloc(n); | ||
| enforce(n == 0 || p !is null); | ||
|
|
||
| scope(failure) free(p); |
There was a problem hiding this comment.
Use p = pureRealloc(p, 0); to free memory in a pure manner.
src/core/memory.d
Outdated
| import std.exception : enforce; | ||
| import core.stdc.stdlib : free; | ||
|
|
||
| ubyte[] fun(size_t n) // TODO pure? |
There was a problem hiding this comment.
one way or another the example must be pure!
src/core/memory.d
Outdated
|
|
||
| p = pureRealloc(p, n *= 2); | ||
| enforce(n == 0 || p !is null); | ||
|
|
There was a problem hiding this comment.
space, the final frontier - let's not
a07e188 to
67918c3
Compare
src/core/memory.d
Outdated
| { | ||
| import core.stdc.stdlib : free; | ||
|
|
||
| ubyte[] fun(size_t n) // TODO pure? |
|
Needed to use assert instead. enforce is in Phobos :) |
src/core/memory.d
Outdated
| { | ||
| void* p = pureMalloc(n); | ||
| p !is null || n == 0 || assert(0); | ||
| scope(failure) { p = pureRealloc(p, 0); } |
There was a problem hiding this comment.
braces are redundant - think of an inlined if, would one put braces?
67918c3 to
78bead0
Compare
|
Ok now, @andralex ? |
|
thx! |
|
Great! |
|
Why is pureFree disallowed when pureRealloc can be used to free aswell? |
|
Ask @schveiguy :) |
|
It's a good question. I would question it the other way, why is Technically, void foo(immutable int *ptr) pure { ... }Using So while technically IMO, |
|
There are no auto-tester builds for druntime anymore? |
|
Wow, the autotester did not report any status for the last commit. I'm going to enable the autotester being green as a requirement for merging Druntime PRs. |
|
Oops, sorry about that. Should we just remove this test case, @andralex? |
|
Done. @rainers Can you code up a fix or should we revert? |
|
I can remove the part where |
|
Subtracting 2 works, too: #1754 |
|
Thanks. |
The merge was done without auto-tester requirement (i.e. merge button was pushed, not auto-merge).
This is probably a good requirement, but I'd much rather the auto-merge feature be used, since there is usually a lag between some other merge happening and github deciding that it's no longer green. |
|
I see... so the problem is that @andralex did not use auto-merge.
We might be able to fix this by enabling the GitHub-side requirement that PR branches can only be merged if they are rebased off the latest commit of the target branch, and have the autotester do that rebase on top of the target branch commit it tested with before attempting to merge the PR. Not sure it would be worth the effort, though - have we ever had breakages due to this? |
|
I saw "All tests passed" so I merged directly. It looks like a race condition. |
Yes, this is the lag I spoke of. github can take some time to sync with the status of the auto tester, or the auto tester could possibly not report the "invalid" status quickly enough. In any case, using the auto-tester auto-merge feature is the safest path -- if all tests have passed, it will merge immediately, so it's no different. |
In response to dlang/phobos#4832 (comment)