Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxCpp RAII observable subscription

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;
};
like image 943
Jonathan Zrake Avatar asked Nov 08 '22 10:11

Jonathan Zrake


1 Answers

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).

like image 90
Kirk Shoop Avatar answered Nov 14 '22 20:11

Kirk Shoop