-
Type: Bug
-
Resolution: Fixed
-
Priority: Major - P3
-
Affects Version/s: None
-
Component/s: None
-
None
-
Service Arch
-
Fully Compatible
-
ALL
-
v8.0
-
Programmability 2024-06-24, Programmability 2024-07-08
-
0
The TransactionCoordinator constructor sets up a bunch of continuation chains that capture this rather than an owning reference to this (e.g., a shared_ptr). One of those continuation chains calls this->_done (this is the last thing does), which fulfills this->_completionPromise, which under certain circumstances can trigger another thread to (almost) immediately destroy the TransactionCoordinator. This destroys _completionPromise and if _completionPromise is the last reference to its SharedState, it destroys the SharedState too. With bad luck, this can happen before the end of this->_completionPromise.setError() while the SharedState is in transitionToFinished somewhere after this line. The SharedState then tries to read children, which has been destroyed.
Some possible fixes are:
1. Make the continuations in TransactionCoordinator capture an owning reference to this (recommended anyway, but possibly a bigger change than is desired as shared_from_this can't be used in the constructor)
2. Give the TransactionCoordinator access to its catalog so it can execute the removal itself rather than requiring the catalog to schedule it on an executor via TransactionCoordinator::onCompletion and _completionPromise.
3. Instead of using a promise and future for onCompletion, just use a callback.
4. In TransactionCoordinator::_done, move _completionPromise into a local variable before fulfilling it. That way, even though this may be destroyed out from under us, the promise and its SharedState won't until setFrom/setError have finished.