-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Finalization support for large allocs. #310
Finalization support for large allocs. #310
Conversation
sdlib/d/gc/tcache.d
Outdated
|
||
unittest finalization { | ||
// Faux destructor which simply records most recent kill: | ||
static size_t lastKilledUsedCap = 0; |
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.
capacity
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.
A cap is how many things you do before you stop and/or saturate.
sdlib/d/gc/tcache.d
Outdated
void freeImpl(PageDescriptor pd, void* ptr) { | ||
pd.arena.free(emap, pd, ptr); | ||
} | ||
|
||
void destroyImpl(PageDescriptor pd, void* ptr) { | ||
auto e = pd.extent; | ||
if (e is null) { | ||
return; | ||
} | ||
|
||
if (pd.isSlab()) { | ||
// Slab is not yet supported | ||
return; | ||
} | ||
|
||
if (e.finalizer is null) { | ||
return; | ||
} | ||
|
||
(cast(void function(void* body, size_t size)) e.finalizer)( | ||
ptr, e.usedCapacity); | ||
} |
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 don't understand why this is destroyImpl rather than destroy itself. It has one callsite. Is it a generally useful abstraction?
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.
@deadalnix it lets us avoid having to find the pd twice (destroy and then in free)
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 still don't get why this isn't in the destroy method. What is the point of having two methods?
@@ -347,6 +377,29 @@ private: | |||
return true; | |||
} | |||
|
|||
void freeImpl(PageDescriptor pd, void* ptr) { | |||
pd.arena.free(emap, pd, ptr); |
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'm not sure this is such a great idea, as it hides the fact this is using the emap. But the emap need to be initialized before it is used.
sdlib/d/gc/tcache.d
Outdated
@@ -127,6 +173,7 @@ public: | |||
if (isLargeSize(size)) { | |||
auto npd = getPageDescriptor(newPtr); | |||
npd.extent.setUsedCapacity(size); | |||
npd.extent.setFinalizer(finalizer); |
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.
Don't do finalization magic in realloc. this is not necessary at this time, and this is untested anyways.
sdlib/d/gc/tcache.d
Outdated
freeImpl(pd, ptr); | ||
} | ||
|
||
bool setFinalizer(void* ptr, void* finalizer) { |
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.
You can't add a finalizer ex post facto because you don't know you have the room (you have it for large, but you can't know that from ptr).
You want to allocate with a finalizer.
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.
https://github.com/snazzy-d/sdc/pull/311/files#diff-14fa859f3f1ce9782b36cdb23977f43bbae0b791fac441958f71ca167c5f52fbR403 is how I propose to handle it. idea is that we can set finalizer on any small allocs which are appendable. we'll have a allocFinalizable which calls allocAppendable and then sets the finalizer.
sdlib/d/gc/tcache.d
Outdated
static size_t lastKilledUsedCapacity = 0; | ||
static void* lastKilledAddress; | ||
static uint destroyCount = 0; | ||
static void destruct(void* body, 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.
body used to be a keyword, and it poorly highlighted. ptr is better, this what it is everywhere else anyway.
sdlib/d/gc/tcache.d
Outdated
|
||
auto e = pd.extent; | ||
// Slab is not yet supported | ||
if (e is null || pd.isSlab() || e.finalizer is null) { |
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.
Invert the condition so you don't need to call the same function twice.
sdlib/d/gc/tcache.d
Outdated
return; | ||
} | ||
|
||
(cast(void function(void* ptr, size_t size)) e.finalizer)( |
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.
Shouldn't the accessor do the cast?
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.
Tried this earlier; any attempt to call a function pointer without a cast, e.g.
alias Finalizer = void function(void* ptr, size_t size);
e.finalizer(ptr, e.usedCapacity);
in destroy()
,
void* allocAppendable(size_t size, bool containsPointers, Finalizer finalizer = null)
etc.
... and using this as type for wherever finalizers are seen, bombs with core.exception.AssertError@src/d/common/type.d(375): Not a function.
on sdc, with kilometres of llvm soup.
Edit: does work if not using alias
.
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.
Fixed.
sdlib/d/gc/extent.d
Outdated
@property | ||
void function(void* ptr, size_t size) finalizer() { | ||
assert(isLarge(), "Finalizer accessed on non large!"); | ||
return cast(void function(void* ptr, size_t size)) | ||
_metadata.largeData.finalizer; | ||
} | ||
|
||
void setFinalizer(void* finalizer) { | ||
assert(isLarge(), "Cannot set finalizer on a slab alloc!"); | ||
_metadata.largeData.finalizer = finalizer; | ||
} |
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 don't understand what's up here.
It's like you put one foot forward and then don't realize that the second foot can move forward too.
If finalizer gives you a function, then setFinalizer should accept a fucntion, and the field itself almost certainly would benefit from being a function.
sdlib/d/gc/tcache.d
Outdated
@@ -1,5 +1,6 @@ | |||
module d.gc.tcache; | |||
|
|||
import d.gc.extent; |
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 for one alias? A strong sign the alias isn't defined where it should be.
Note: the extent side support split into #311