Search code examples
rtoszephyr-rtosnrf52nordic-semi

Zephyr RTOS (nRF Connect SDK): are k_msleep() and k_sleep() both non-blocking?


I'm quite new to Zephyr RTOS and the nRF Connect SDK. What I want to know is if the kernel sleep routines k_sleep and k_msleep are both non-blocking. This post says:

k_msleep (which uses k_sleep) is not a blocking call.

Which is a little confusing: k_msleep is non-blocking it says but is the underlying k_sleep blocking? The reason I want to know this is because in my nRF Connect application (that I'm writing for a custom nRF52832 based board), I want to introduce LED blinking on BLE connection and disconnection: blink twice on connection and blink thrice on disconnection. The way I'm approaching this is by adding a blink work item to the system work queue. Below is roughly what I'm doing:

...
static uint8_t nblinks = 0;
static struct k_work blink_work;

static void blink_work_fn(struct k_work *work)
{
    int val = 0;
    for (int i = 0; i < nblinks; ++i) {
        gpio_pin_set_dt(&led, (val = !val));
        k_sleep(K_MSEC(500)); // or k_msleep(500) ?
    }
}

void connected(...)
{
    ...
    nblinks = 2;
    k_work_submit(&blink_work);
}

void disconnected(...)
{
    ...
    nblinks = 3;
    k_work_submit(&blink_work);
}

BT_CONN_CB_DEFINE(conn_callbacks) = {
    .connected = connected,
    .disconnected = disconnected
};

...
main()
{
    k_work_init(&blink_work, blink_work_fn);
    ...   
}

From what I've read, work items submitted to the system work queue should not be blocking or the kernel might panic. If you have some experience with Zephyr for applications like this, would you say that this is even a good strategy for achieving what I have in mind? Thanks for your time.


Solution

  • From Zephyr Scheduling docs:

    The scheduler determines which thread is allowed to execute at any point in time; this thread is known as the current thread.

    There are various points in time when the scheduler is given an opportunity to change the identity of the current thread. These points are called reschedule points. Some potential reschedule points are:

    • transition of a thread from running state to a suspended or waiting state, for example by k_sem_take() or k_sleep().

    • transition of a thread to the ready state, for example by k_sem_give() or k_thread_start()

    • return to thread context after processing an interrupt

    • when a running thread invokes k_yield()

    A thread sleeps when it voluntarily initiates an operation that transitions itself to a suspended or waiting state.

    k_msleep() and k_sleep() will behave the same, k_msleep() is just a wrapper for k_sleep(). k_sleep() is not only non-blocking, but it will hand over code to the scheduler. Meaning that if you do a loop in your code, you should usually use k_sleep() or k_yield(). For an example: This code will never yield to the scheduler, so other threads will not get the chance to run:

    while(1){
         printk("In loop\n");
    }
    

    So instead you should do something like this, so the scheduler can do its thing:

    while(1){
             printk("In loop\n");
             k_sleep(K_MSEC(1));
    }