Replace legacy strand with executor-templated strand#10901
Replace legacy strand with executor-templated strand#10901jschmidt-icinga wants to merge 1 commit into
Conversation
bd7aa1a to
ca03f7c
Compare
ca03f7c to
238e6b0
Compare
238e6b0 to
fad5f60
Compare
| ); | ||
| void NewClientHandlerInternal( | ||
| boost::asio::yield_context yc, const Shared<boost::asio::io_context::strand>::Ptr& strand, | ||
| boost::asio::yield_context yc, const std::shared_ptr<IoStrand>& strand, |
There was a problem hiding this comment.
Added a section to the PR description about the reason. In short, it's because on older boost versions boost::asio::spawn() don't know what to do with the Shared<strand>& it gets as a result of dereferencing the pointer.
There was a problem hiding this comment.
In my opinion we should consider deprecating it and just use std::shared_ptr
Absolutely not. I introduced it specifically to save memory. We already had enough memory problems in the past, didn't we?
There was a problem hiding this comment.
What memory does Shared<> save vs. std::shared_ptr<>? 🤔
It will save an additional allocation for the reference count in case someone doesn't use std::make_shared() as one should anyways. And in the rare cases where std::shared_from_this is actually needed, I think it stores an additional std::weak_ptr, but that's negligible for most our classes. At first glance I don't even see anything that would require the latter... we mostly use Shared<> for Stream objects, SSL context and things like that. std::shared_ptr should be perfectly fine for those, or am I missing something.
To be clear: I'm not saying deprecate boost::intrusive_ptr for our Object::Ptr, just shoehorning it into a generic shared pointer, which it was never meant to be.
This replaces the old
boost::asio::io_context::strandwith the more modern executor-templatedboost::asio::strand<boost::asio::io_context_executor_type>. This should prevent issues like #10825 to occur in the future, because the modern strand does not share implementation pointers between individual strand objects.Detailed Description
The issue with the legacy strand was that there is a chance for two strand users (like a
PerfdataWriterConnectionand aJsonRpcConnection) to share an implementation object. This means that even though both objects hold distinct strand objects, one can block the execution of the other. So when one objects holds a lock, waits on an IO-Operation on that strand and the other object resumes on the strand and tries to acquire the same lock, both objects are deadlocked.The modern strand type is never shared between users and so should never run into this kind of deadlock. It's available for all boost versions we currently support and I've checked the changelog and git history carefully to see if there are any critical bugs that have been fixed since it was introduced. Technically boost-1.66 was the version this new strand has been introduced, but it seems there have been relatively few changes outside of some refactoring since then.
Currently this is all in one commit, because it's hard to switch this over gradually with some critical pieces (
TimeoutandGracefulDisconnectmost notably) using the strand type explicitly and make incompatible calls on it which can't easily be generalized. And I didn't want to go through the effort of adding an overload either, just so it becomes pointless after switching everything over.Notes
Use of
std::shared_ptr<strand>instead ofShared<strand>::PtrWhen a
Shared<Foo>::Ptris dereferenced, the object is of typeSharedthat inherits fromFoo. When passed toSpawnCoroutine(), this keeps older boost versions from using their dedicatedboost::asio::spawn(const strand< Executor > & ex, ...)overloads and falls back to the generic Executor based overloads, which then fail somewhere in the machinery (the reason the overload exists).Newer boost versions don't have this problem and the strand can just be treated as a generic executor, but for now we'll have to use
std::shared_ptr<strand>for shared strands, because that correctly dereferences to the original type. The alternative would have been explicit object slicing withstatic_cast<strand&>at each call-site.Honestly, I hadn't looked that closely at
Shared<>::Ptr, but this makes it a giant ball of anti-patterns in my eyes. Most egregious of all, unconstrained deriving from arbitrary types isn't a good idea and might break all kinds of things. In my opinion we should consider deprecating it and just usestd::shared_ptrwithstd::make_sharedandstd::shared_from_thisin new code.Closes #10861.