Conversation
|
CLANG-FORMAT TEST - FAILED (on last commit): |
|
CMAKE-FORMAT TEST - PASSED |
|
CLANG-FORMAT TEST - PASSED |
|
CMAKE-FORMAT TEST - PASSED |
| for ( ; iter != staticHandlerMap.end(); iter++ ) { | ||
| if ( *iter == handler ) { | ||
| if ( iter->get() == handler ) { | ||
| staticHandlerMap.erase(iter); |
There was a problem hiding this comment.
I just noticed: This is one place where there was a memory leak: If you call unregisterHandler(), the old code did not delete the handler, but the new code does. Here we call erase() on an element which contains std::unique_ptr, causing it to delete the handler. This fixes a "definite" memory leak reported by Valgrind.
I suppose to minimize code changes we could simply add delete handler; before the erase() in the old code, and then most if not all of the other changes would be unnecessary, but the new code using std::unique_ptr is much cleaner and avoids having to manually delete everything in the destructor. One thing I always try to do, is: If there is a delete in a destructor, see if changing to smart pointers will make it get deleted automatically and avoid the need to do it in the destructor.
There was a problem hiding this comment.
You can't delete the handler because the clock object doesn't own it. I'm work on another PR that already changes how clock handlers are deleted, taking into account who owns them. Once that merges, we can revisit possible memory leaks. Closing this PR for now.
There was a problem hiding this comment.
Well, I'm definitely seeing memory leaks in Clock() as I pour through Valgrind reports, so you and I are both onto something about Clock ownership.
There was a problem hiding this comment.
Yea, before the checkpointing code went in, there was probably no way to avoid memory leaks because there was no way to automatically delete clock handers that were not currently registered with a Clock object. And, CoreTest_Checkpoint definitely deregisters clocks, so I suspect the deregistered clock handlers are the ones that are leaking.
With checkpointing we had to track clock handlers in BaseComponent in order to serialize everything properly. Now, BaseComponent just owns all the Clock::Handlers registered with it and "loans" them out to the Clock objects. So, BaseComponent can delete them as the simulation finishes. While working on watchpoints, which touch clock handlers, I noticed that we hadn't changed how the handlers were deleted. I already have that implemented and haven't run into any errors with the change.
|
Status Flag 'Pre-Test Inspection' - - This Pull Request Requires Inspection... The code must be inspected by a member of the Team before Testing/Merging |
|
If std::vector<std::weak_ptr<Clock::HandlerBase>> staticHandlerMap;
bool
Clock::registerHandler(const std::shared_ptr<Clock::HandlerBase>& handler)
{
staticHandlerMap.push_back(handler); // Pushes a std::weak_ptr created from a std::shared_ptr
...
}
void
Clock::execute()
{
...
std::shared_ptr<Clock::HandlerBase> handler(*sop_iter); // Create a std::shared_ptr "borrowing" from std::weak_ptr in staticHandlerMap
if ( (*handler)(currentCycle) )
sop_iter = staticHandlerMap.erase(sop_iter); // Erase the std::weak_ptr
else
++sop_iter; |
This is another Valgrind-related memory leak fix, but because it is not as straightforward, it deserves a separate PR and more careful review.
There is a "definite leak" reported by Valgrind in
Clock::staticHandlerMap, which is a vector of pointers to handlers, when running thecoreTest_Checkpointtest.To fix this leak, and to simplify the code,
staticHandlerMapwas changed from a vector of raw pointers to a vector ofstd::unique_ptr, so that when the class is destroyed and its memberstaticHandlerMapis destroyed, all of its pointers are deleted automatically.However, in
Clock::execute(), the handler is executed, and if successful, the handler member instaticHandlerMapis erased. Previously it erased the raw pointer member without deleting it, while now we have torelease()it fromstd::unique_ptrbefore the mapping entry gets erased, because the handler apparently uses the raw handler pointer elsewhere, passing it around and maybe adding it back tostaticHandlerMaplater. If we do notrelease()it and simply erase thestd::unique_ptrentry, it will getdeleted, which causes it not to work correctly (the previous code did notdeletethe raw pointer either).On the surface, this change looks like it does not change the behavior, because it uses
std::unique_ptrinstead of raw pointers, and it has the same behavior as before, adding allocated handler pointers tostaticHandlerMapand erasing them without deleting them when they are successfully executed. When theClockis destroyed, any remaining members ofstaticHandlerMapare deleted and erased. So this change superficially looks like it uses smart pointers for better, simpler code without changing its behavior.But I have seen "definite leak" Valgrind errors fixed by this change in
coreTest_Checkpoint, so it has something to do with checkpoint/restart. I am not sure this solution is right. I wish that SST used smart pointers in more places, and that ownership semantics were better defined in APIs. There is this comment in the file, which indicates thatstaticHandlerMapis not serialized, which may have something to do with the leak in thecoreTest_Checkpointtest:"
Won't serialize the handlers; they'll be re-registered at restart". Somehow turning the handler pointers into smartstd::unique_ptrs is able to get rid of one of the leaks incoreTest_Checkpoint.I suspect that when the checkpointing test does a serialization and Checkpointing, it discards the old value of
staticHandlerMap, and that by making the pointersstd::unique_ptr, they getdeleted when thestaticHandlerMapis erased/destroyed/whatever when the checkpoint/restart sequence happens, whereas before this change, the raw pointers to allocated memory simply got thrown away and caused a memory leak.