This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SDK13 app_sched_execute() infinite recursion

Hi,

In SDK11, it was possible to call app_sched_execute() from a function that was itself called from the scheduler:

void app_sched_execute(void)
{
    void                    * p_event_data;
    uint16_t                  event_data_size;
    app_sched_event_handler_t event_handler;

    // Get next event (if any), and execute handler
    while ((app_sched_event_get(&p_event_data, &event_data_size, &event_handler) == NRF_SUCCESS))
    {
        event_handler(p_event_data, event_data_size);
    }
}

static uint32_t app_sched_event_get(void                     ** pp_event_data,
                                    uint16_t *                  p_event_data_size,
                                    app_sched_event_handler_t * p_event_handler)
{
    uint32_t err_code = NRF_ERROR_NOT_FOUND;

    if (!APP_SCHED_QUEUE_EMPTY())
    {
        uint16_t event_index;

        // NOTE: There is no need for a critical region here, as this function will only be called
        //       from app_sched_execute() from inside the main loop, so it will never interrupt
        //       app_sched_event_put(). Also, updating of (i.e. writing to) the start index will be
        //       an atomic operation.
        event_index         = m_queue_start_index;
        m_queue_start_index = next_index(m_queue_start_index);

        *pp_event_data     = &m_queue_event_data[event_index * m_queue_event_size];
        *p_event_data_size = m_queue_event_headers[event_index].event_data_size;
        *p_event_handler   = m_queue_event_headers[event_index].handler;

        err_code = NRF_SUCCESS;
    }

    return err_code;
}

This is possible because the queue is updated in app_sched_event_get() before the event_handler is called; thus, if the event_handler calls app_sched_execute() again, it will either return if there are no more events on the queue, or execute the next queued event.

However, now in SDK13, the event queue is updated after calling the handler:

void app_sched_execute(void)
{
    while (!is_app_sched_paused() && !APP_SCHED_QUEUE_EMPTY())
    {
        // Since this function is only called from the main loop, there is no
        // need for a critical region here, however a special care must be taken
        // regarding update of the queue start index (see the end of the loop).
        uint16_t event_index = m_queue_start_index;

        void * p_event_data;
        uint16_t event_data_size;
        app_sched_event_handler_t event_handler;

        p_event_data = &m_queue_event_data[event_index * m_queue_event_size];
        event_data_size = m_queue_event_headers[event_index].event_data_size;
        event_handler   = m_queue_event_headers[event_index].handler;

        event_handler(p_event_data, event_data_size);

        // Event processed, now it is safe to move the queue start index,
        // so the queue entry occupied by this event can be used to store
        // a next one.
        m_queue_start_index = next_index(m_queue_start_index);
    }
}

Thus, no scheduled function can ever call app_sched_execute(), because it'll cause infinite recursion (since that function itself is still at the front of the queue!)

Being able to call app_sched_execute() from places other than the main loop was useful, because for example you could block from anywhere, waiting for timer or BLE events. There may be better ways to do this in SDK13, but my app built on SDK11 relied on being able to call app_sched_execute() from anywhere (including scheduled functions).

Is there a good reason why this was changed in SDK13 to update the queue after calling the event_handler, rather than before? I don't see any downside to updating it prior to the call, and only see upside (i.e., not worrying about infinite recursion if app_sched_execute() is called from places other than the main loop).

Parents
  • Hi Mike,

    The app_sched_execute() was made to be called from inside main loop, not for calling recursively from within the event itself. You can find this in the description:

    /**@brief Function for executing all scheduled events.
     *
     * @details This function must be called from within the main loop. It will execute all events
     *          scheduled since the last time it was called.
     */
    void app_sched_execute(void);
    

    The information I got from our developer was that we updated the code from SDK v12 to fix a potential bug that if the event is freed before executing, the data comes with that event can be corrupted before it is executed. That's why we swapped the order.

    Calling app_sched_execute() from inside an event handler is not recommended.

  • Thanks for the reply Hung! But, I don't quite understand what you mean by "if the event is freed before executing". The line of code: m_queue_start_index = next_index(m_queue_start_index); is not freeing anything; it's just updating the m_queue_start_index, right? So how could updating m_queue_start_index before calling the handler corrupt the event data?

Reply Children
No Data
Related