This is a more involved version of #10697, which instead completely gets rid of our nasty AddRef() and Release() in favor of automatic lifetime management.
Special care must be taken, though, to only delete CNodes from a single thread, and to control which thread that is. Eventually, this should move to the message processing thread, so that it’s not possible to hit cs_main from the main network thread.
In order to do this safely, this PR introduces 2 new generic smart pointer types: strong_ptr and decay_ptr. They provide a functionality that I’ve wanted for a long time: the ability to safely decay a shared_ptr to a unique_ptr. That sounds somewhat nonsensical at first, but it’s useful to be able to make copies of a pointer for a while, stop, wait until only one remains, then delete with guaranteed safety.
Please read shared_ptr.h and check out the tests before groaning. I think this is a very cool (and completely safe) pattern.
This functionality could potentially be accomplished with a shared_ptr and polling ptr.unique(), but that’s inherently racy because a new copy could be created simultaneously. Even moving to a local instance and calling .unique() on that one is not safe, as a weak_ptr could be upgraded simultaneously.
Instead, a strong_ptr is created which acts like a unique_ptr but allows shared_ptrs to be “loaned” out. Once a strong_ptr is moved into a decay_ptr, the strong_ptr is reset and no new loans may be created. The decay_ptr tracks the lifetime of the loaned copies, and knows whether or not they have all expired. This can be queried, with no race concerns, with decay_ptr::decay().
Additionally, if the loaned shared_ptrs for some reason outlive the strong_ptr/decay_ptr, they are safely deleted once the last loaned shared_ptr expires. So there is no risk of leaks.
In order to make review easier, these changes were made in a few stages:
- Where possible, make functions agnostic to the type of pointer being used
- Switch to shared_ptr but keep existing refcounting on top
- Switch to strong_ptr
- Drop existing refcounting and now-unnecessary locking.