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_untilis 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 expectingthisto 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
mutexand acondition_variable(that usesfinally()to signal thecondition_variable).