I am using RxCpp in a model-view setting. A view update method is subscribed to an observable (via lambda capturing this
). Undefined memory access would ensue if the subscription were to outlive the view instance. I do not want the subscription to keep the view alive. Therefore, I need the subscription to end deterministically on the view's destructor. This sounds like a case for RAII.
Is this ever dangerous? Is it somehow a misuse of rx? I have read to prefer take_until
in similar settings. Why might that be better, and how could it be used here?
Thank you!
#include "rxcpp/rx.hpp"
class MyView : public View
{
public:
MyView(rxcpp::observable<int> obs) : obs (obs)
{
sub = obs.subscribe ([this] (int i) { update(i); });
}
~MyView()
{
sub.unsubscribe();
}
void update(int i)
{
number = i;
repaint();
}
private:
rxcpp::observable<int> obs;
rxcpp::subscription sub;
int number;
};
take_until
is the best answer when the next action is composed after it. That would ensure the correct ordering.
The issue is that cancellation is always a race. The code in the question does unsubscribe()
in the destructor, and that starts the race to cancel. The destructor does not wait until the subscription ends however. This leaves a race between the exit of the destructor and the end of the subscription that is expecting this
to be valid.
The solution is to add a wait of some kind after the unsubscribe()
.
This could be a busy-wait on an atomic bool that is set in a finally()
on the original subscription in the constructor.
Or it could be a heavier wait using a mutex
and a condition_variable
(that uses finally()
to signal the condition_variable
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With