-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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
Delegate Cleanables #1711
Delegate Cleanables #1711
Changes from 1 commit
c43e0bc
7255c30
3c72b36
c495ea9
8d1ef37
056784e
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 |
---|---|---|
|
@@ -16,7 +16,7 @@ namespace rocksdb { | |
// PinnedIteratorsManager will be notified whenever we need to pin an Iterator | ||
// and it will be responsible for deleting pinned Iterators when they are | ||
// not needed anymore. | ||
class PinnedIteratorsManager { | ||
class PinnedIteratorsManager : public Cleanable { | ||
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. now the cleanup code will be called in the destructor, I think we must change that so that it's called in 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 feel it's better to have a Cleanable data 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. We can go either way but it would be great if we discuss it and have pros and cons listed. The pros of inheritance are:
What would be the cons of this approach, or what would the pros of data member approach? 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 just felt it's better to have it as a data member because everywhere in the code Cleanable was doing Cleanup in the destructor. now we support to do the cleanup and reuse the Cleanable object. It just felt to me that having this behavior more explicit (having a data member instead of inheritance) is better. 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 do not have a strong opinion here. Let's go with your instinct and make it a data 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. On second thought it seems easier if we keep the current inheritance scheme. The reason is that currently we can easily pass PinnedIteratorManager as a Cleanable to DelegateCleanupsTo function:
Without having PinnedIteratorManager manager we would have two choices:
|
||
public: | ||
PinnedIteratorsManager() : pinning_enabled(false) {} | ||
~PinnedIteratorsManager() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1559,15 +1559,8 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key, | |
break; | ||
} else { | ||
BlockIter stack_biter; | ||
if (pin_blocks) { | ||
// We need to create the BlockIter on heap because we may need to | ||
// pin it if we encounterd merge operands | ||
biter = static_cast<BlockIter*>( | ||
NewDataBlockIterator(rep_, read_options, iiter.value())); | ||
} else { | ||
biter = &stack_biter; | ||
NewDataBlockIterator(rep_, read_options, iiter.value(), biter); | ||
} | ||
biter = &stack_biter; | ||
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. let's use remove |
||
NewDataBlockIterator(rep_, read_options, iiter.value(), biter); | ||
|
||
if (read_options.read_tier == kBlockCacheTier && | ||
biter->status().IsIncomplete()) { | ||
|
@@ -1596,22 +1589,12 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key, | |
} | ||
s = biter->status(); | ||
|
||
if (pin_blocks) { | ||
if (get_context->State() == GetContext::kMerge) { | ||
// Pin blocks as long as we are merging | ||
pinned_iters_mgr->PinIterator(biter); | ||
} else { | ||
delete biter; | ||
} | ||
biter = nullptr; | ||
} else { | ||
// biter is on stack, Nothing to clean | ||
if (pin_blocks && get_context->State() == GetContext::kMerge) { | ||
// Pin blocks as long as we are merging | ||
biter->DelegateCleanupsTo(pinned_iters_mgr); | ||
} | ||
} | ||
} | ||
if (pin_blocks && biter != nullptr) { | ||
delete biter; | ||
} | ||
if (s.ok()) { | ||
s = iiter.status(); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,37 @@ Cleanable::~Cleanable() { | |
} | ||
} | ||
|
||
// TODO(myabandeh): if the list is too long we should maintain a tail pointer | ||
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. Let's add the possible optimizations/bottlenecks we discussed offline in the comments here |
||
void Cleanable::DelegateCleanupsTo(Cleanable* other) { | ||
assert(other != nullptr); | ||
if (cleanup_.function == nullptr) { | ||
return; | ||
} | ||
Cleanup* c = &cleanup_; | ||
other->RegisterCleanup(c->function, c->arg1, c->arg2); | ||
c = c->next; | ||
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 think it's better to remove line 64,65 and just enter the loop directly 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. But the process for delegating the first Cleanup is different from the rest: the first is an embedded object and its values needs to be copied while the rest are on heap and they can be simply be added to the destination linked list. 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. yes, my bad |
||
while (c != nullptr) { | ||
Cleanup* next = c->next; | ||
other->RegisterCleanup(c); | ||
c = next; | ||
} | ||
cleanup_.function = nullptr; | ||
cleanup_.next = nullptr; | ||
} | ||
|
||
void Cleanable::RegisterCleanup(Cleanable::Cleanup* c) { | ||
assert(c != nullptr); | ||
if (cleanup_.function == nullptr) { | ||
cleanup_.function = c->function; | ||
cleanup_.arg1 = c->arg1; | ||
cleanup_.arg2 = c->arg2; | ||
delete 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. I think the caller should be responsible for cleanup, what do you think ? 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 went back and forth between these two approaches and finally went with doing the delete inside RegisterCleanup. My argument was that there are two cases:
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. Sounds good to me |
||
} else { | ||
c->next = cleanup_.next; | ||
cleanup_.next = c; | ||
} | ||
} | ||
|
||
void Cleanable::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) { | ||
assert(func != nullptr); | ||
Cleanup* c; | ||
|
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.
looks to me this is not needed