Structure of an (interrupt) handler that does a callback

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
BramPeeters
Posts: 54
Joined: Wed Jan 31, 2018 3:10 pm

Structure of an (interrupt) handler that does a callback

Post by BramPeeters » Mon Apr 16, 2018 4:23 pm

Hi,
Looking at the stm32 can code, the general structure of an interrupt handler that does a callback seems to be

Code: Select all

int handler ( )
{
	int l_nResult = e_RETURNVALUE_Success;
	pyb_obj_t * self = xxx;
	mp_sched_lock();
        gc_lock();
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_call_function_x( l_pSelf->callback, ... );
            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            pyb_setbareventcallback(l_pSelf, mp_const_none);
            printf("uncaught exception in handler\n");
            mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
            l_nResult = e_RETURNVALUE_Failure;
        }
        gc_unlock();        
        mp_sched_unlock();
        return ( l_nResult );
}
I have 2 problems why i can use this sequence and 1 thing in my advantage
[1] I need to create a string mp object from an input argument of the handler. Creating objects might raise an exception and can't be done in gc_lock state, so i have to change the order. The can code does not have this problem because the use a small int (which is only ok if representation is such that small int fits in a number and not an object, but okay).
[2] I am running under freertos so no scheduler.

The good thing is that my handler is not an interrupt handler, but just a notification coming from another task that is probably not too bothered by having to wait a bit.
So i think about going to this form:

Code: Select all

int handler( char* somestring ) 
{
	int l_nResult = e_RETURNVALUE_Success;
        pyb_obj_t * self = xxx;
	MP_THREAD_GIL_ENTER(); 
	
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_obj_t callbackstring =  mp_obj_new_str( somestring, strlen(somestring), 0 )		
	    gc_lock();
            mp_call_function_x( l_pSelf->callback , l_pSelf,  callbackstring, );
            gc_unlock();        

            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            pyb_setbareventcallback(l_pSelf, mp_const_none);
            printf("uncaught exception in handler\n");
            mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
            l_nResult = e_RETURNVALUE_Failure;
        }

        MP_THREAD_GIL_EXIT(); 
        return ( l_nResult );
}
But i do not know the scenarios that would need an mp_sched_lock and especially gc_lock in the original code, so it is a bit hard to see if I break something this way.
The mp_sched_lock might be useful to prevent the scheduler from running another 'task' if the can interrupt itself is interrupted by a timer tick i suppose (not sure if that is possible, i have not looked at the scheduler to see how it is works). But then why the extra gc_lock ? To make sure that anyone attempting to do a malloc in the callback gets trapped in the uncaught exception branch ?
In that case setting up nlr before doing a gc_lock should not be a problem.

Did i miss something ?

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Structure of an (interrupt) handler that does a callback

Post by dhylands » Mon Apr 16, 2018 4:37 pm

Changing the order will cause problems.

You are NOT allowed to allocate anything during a hard interrupt handler. PERIOD.

Unlocking the heap or any other thing that allows the memory allocation to go ahead WILL corrupt the heap at some point in time. This heap corruption will happen if the interrupt happens during just the wrong time while performing a garbage collection.

If you need the string object, then you need to preallocate (probably when registering the interrupt) and have the interrupt pass a pointer to this preallocated object.

BramPeeters
Posts: 54
Joined: Wed Jan 31, 2018 3:10 pm

Re: Structure of an (interrupt) handler that does a callback

Post by BramPeeters » Mon Apr 16, 2018 4:47 pm

I would expect waiting for the GIL will make sure the heap is protected ?
(as i said, i am not calling for a "real" interrupt, so i can wait for it)

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Structure of an (interrupt) handler that does a callback

Post by dhylands » Mon Apr 16, 2018 7:51 pm

Possibly - I haven't looked at the code. If you're just behaving like another thread then using the thread locking mechanism should be fine.

Hard interrupt handlers can't wait on the GIL.

Post Reply