Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use volatile class members for interrupt handling

Lets suppose it is embedded development of some ARM controller. Lets suppose we have some variable, which can be assigned from an interrupt or "mainThread" - (is it a "main loop" or RTOS thread). In C world volatile keyword should be used in this case and code may look like this:

/* Some subsystem .C file */
static volatile uint8_t state;

void on_main_thread(void) {
    state = 1;     /* Changing state in this context */
}

void on_interrupt(void) {
    state = 0;     /* Changing state from interrupt */
}

uint8_t get_state(void) {
    return state;  /* Getting the state in whatever context */
}

volatile keyword is essential in this situation. Now our company rewrites some code to C++ and the same subsystem example looks like this (I use enum here to emphasize the problem)

class SomeSubsystem
{
public:
    enum class States
    {
        Off,
        Idle,
        Up,
        Down,
    };


    States getState() const { return mState; }

    void onMainThread(void) {
           mState = States::Idle;     // Changing state in this context
    }

    // Somehow this function is called from the interrupt
    void onInterrupt(void) {
           mState = States::Up;     // Changing state from interrupt
    }
private:
    States mState;   // <-- Here! Volatile? 
//...
};

Now States mState should be volatile because it is shared among different contexts. But If one sets it as volatile... Then volatile works like plague for C++ class and one have to volatilize everything around. Like volatile enum class States, getState() volatile etc. Which doesn't look good for me (am I wrong?)

So. What is the right way to handle this situation in C++?

P.S. I would try to define "this situation" as: "possible usage of a class members from different contexts like interrupts and normal code execution"

like image 720
MajesticRa Avatar asked Oct 30 '22 23:10

MajesticRa


1 Answers

This could work if you only need a single instance of SomeSubsystem in your program (which I assume as per the c code you posted.

If you need multiple instances, then maybe you could modify mState to be a States array or some similar structure instead.

class SomeSubsystem
{
public:
    enum class States
    {
        Off,
        Idle,
        Up,
        Down,
    };


    States getState() const { return mState; }

    void onMainThread(void) {
           mState = States::Idle;     // Changing state in this context
    }

    // Somehow this function is called from the interrupt
    void onInterrupt(void) {
           mState = States::Up;     // Changing state from interrupt
    }
// Make mState public in order to access it from the rest of your code
// Otherwise, keep it private and create static set/get functions
public:
    static volatile States mState;   // <-- Here! Volatile? 
//...
};

Then define mState somewhere (eg. in SomeSubsystem.cpp)

volatile SomeSubsystem::States SomeSubsystem::mState = SomeSubsystem::States::Off;

Now you are able to access mState from anywhere in your code like this

SomeSubsystem::mState = SomeSubsystem::States::Off;
like image 123
Santos Avatar answered Nov 15 '22 04:11

Santos