Best way for event handler

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

Best way for event handler

Post by BramPeeters » Wed Mar 28, 2018 9:17 pm

Hi,

I am using micropython on an stm32 on top of freertos with thread support in micropython and GIL.
I am trying to figure out the best way to process 'events' arriving from other freertos tasks in micropython.
If the event originated from an interrupt I will decouple it to normal freertos task level already in c, but since i am using preemtive scheduling in freertos , from the point of view of the micropython scheduler there is little difference between real interrupt level and an freertos task preemting out of nowhere ...

I have already read the following resources:
[1] https://github.com/bbcmicrobit/micropython/issues/36
=> It seems there is no progress on this or am i wrong ?

[2] https://micropython.org/resources/docs/ ... rules.html
=> I am looking for a way to 'notify'/wakeup a normal micropython thread waiting for events. This document does not handle that question ( and I find the amount of work being done at interrupt level in these examples rather scary, eg i would never call led.toggle() from interrupt level , the led might be connected to an i2c io expander, the function might try to print if something goes wrong because the person implementing it does not expect it to be called on irq level, ... )

It did mention the micropython.schedule which might be interesting to get from the scheduler in an inconsistent state to a normal state. It does not really elaborate though, but luckily viewtopic.php?t=4027 gives an example and some pitfalls.


Questions:
[A] Is it possible to do the 'micropython.schedule' trick already from C instead of using mp_call_function_X so you never end up executing in python with the kernel in an inconsistent state ?
Note: I assume a function scheduled this way will run as soon as possible preempting another executing python thread at a random point ?

Currently I use the following code to schedule the callback (cfr can ISR)

Code: Select all

    if ( l_pSelf->keyEventCallback != mp_const_none )
    {
        mp_sched_lock();
        gc_lock();
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_call_function_2( l_pSelf->keyEventCallback,  MP_OBJ_NEW_SMALL_INT(( ((uint32_t)nKeyID) << 2) & nType), MP_OBJ_NEW_SMALL_INT(nRepeats) );
            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            pyb_keyboard_setkeyeventcallback(l_pSelf, mp_const_none);
            printf("uncaught exception in keyevent interrupt 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();
    }
I see that mp_call_function_2 is the highest number of arguments, is there an inherent reason why 2 is the maximum or would it be easy to add a mp_call_function_3 so i do not have to merge my 3 arguments into 2.

I also don't really understand the cases for when mp_sched_lock and gc_lock are required, so I don't know what the effect of these will be if I am not running this code from a real ISR (that will never be preempted by the normal python scheduler) versus my case where i am running this from a freertos task that might in turn be preempted by a task running normal micropython code. If this is bad I could create a freertos critical section around this to fix that, but i hope the mp_sched_lock will already prevent funky stuff from happening but that is messy so i like to avoid that.

[C] I think a way to achieve my purpose of waking up an event handling task in a thread safe way could be to add support for semaphores to micropython (similar to locks that are already present in modthread, but these are based on mutexes so I can't use them for signalling between tasks). So that would allow to imlement an event queue mechanism (grab the semaphore in the event handling task and signal the semaphore in the event callback after filling in some queue element). And if i need to set some non primitive variables to pass event information I better use the micropython.schedule trick (preferably directly from C so that the callback immediately runs in a consistent state).

That seems rather complex though so I really hope I am overlooking an obvious solution here ?

Bram

Post Reply