how to safely use std::function in interrupts handlers on emended system?

Viewed 207

I'm Working with STM33F103, and I'm trying to implement a callback mechanism that would call methods on a given object to handle interrupts, so far I use the following solution:

class Nvic
{
public:
   using function_pointer =void (*)(void);  

   template<typename T>
   static void get_interrupt(int interrupt_number, T* object,void (T::*methode)())
   {
       class_vector[interrupt_number]=reinterpret_cast<Default_class*>(object);
       method_vector[interrupt_number]=reinterpret_cast<default_method_type>(methode);
   }

   static void execute_interrupt(int interrupt_number)
   {
       (class_vector[interrupt_number]->*method_vector[interrupt_number])();
   }
private:
   struct Default_class{void default_method();};
   using default_method_type =void(Default_class::*)(void);

   static inline Default_class* class_vector[NUMBER_OF_INTERRUPTS];
   static inline default_method_type method_vector[NUMBER_OF_INTERRUPTS];
}

extern "C"
{
void Some_ISR(void) __attribute__ ((interrupt));    
void Some_ISR(void){Nvic::execute_interrupt(2);}
}

And if some object want to bind interrupt to itself:

class Some_class
{
public:
    Some_class(int peripherial_iterrupt)
    {
         Nvic::get_interrupt(peripherial_iterrupt,this,&Some_class::my_interrupt);
    }

private:
    my_interrupt();

};

But I want to implement this based on std::function, unfortunately it turns out that std::function can dynamically allocate memory so it is not reentrant. So is there any way to make sure that std::function is interrupt safe?

Maybe using an allocator will solve the problem, if I provide std::function, my custom allocator which allocates memory in some static memory buffer there will be no interrupt heap corruption problem.

Or maybe if I provide newlib-nano __malloc_lock and __mallock_free functions then heap memory allocation will be interrupt safe.

It also may allow me to use other STL containers and algorithms in interrupt safe environment, like this:

class Some_interface :private Some_peripherial
{
public:
    Some_interface(int peripherial_number) :Some_peripherial(peripherial_number)
    {
        int interrupt_number =peripherial_number;   
        Nvic::get_interrupt(interrupt_number,this,&Some_interface::interrupt_routine);
    }
    void send_data(void* buff, size_t size)
    {
        for(size_t i=0;i<size;i++)
        {
            data_to_send.push_back((uint8_t)buff[i]);
        }
    }

    void set_all_data_send_callback(std::function<void(void)> f){all_data_send_callback=f;}

private:
    std::vector<uint8_t> data_to_send;
    std::function<void(void)> all_data_send_callback;

    void interrupt_routine()
    {
        if(data_to_send.empty()){if(all_data_send_callback)all_data_send_callback();};
        else
        {
            Some_peripherial::send(data_to_send.back());
            data_to_send.pop_back();
        }       
    }
};

class Timer_task :private Timer_peripherial
{
public:
    Timer_task(int peripherial_number) :Timer_peripherial(peripherial_number)
    {
        int interrupt_number =peripherial_number;   
        Nvic::get_interrupt(interrupt_number,this,&Timer_task ::on_interrupt);
        Timer_peripherial::set_interrupt_on_evry_10ms();
    }

    template<typename T>
    static void set_task(T* object,void (T::*methode)())
    {
        task =std::bind(std::mem_fn(methode),object);
    }

private:
    std::function<void(void)> task;
    void on_interrupt()
    {
        if(task) task();
    }
};

Some_interface interface_0{0};
Some_interface interface_1{1};

const char global_message[] ="global_message";
Timer_task timer_task{interface_1.send_data((void*)global_message,strlen(global_message))};


int main()
{

    const char message_0[] ="123456";
    const char message_1[] ="1256";
    while(1)
    {
        interface_0.send_data((void*)message,strlen(message));
        interface_1.send_data((void*)message,strlen(message));
        delay();
    }
}

Where data_to_send are filled form main and also form other method called from another interrupt.

We can also set all_data_send_callback if we want to.

0 Answers
Related