FreeRTOS task blocks indefinitely without context switch

I’m writing a simple FreeRTOS app in which I create a new task and block until the new task has been initialized. However, the task never proceeds to run after hitting the semaphore block. Check this out:

main.cpp

#include "thread2.hpp"
os2::thread2 th{};

extern "C" auto app_main() -> void {
    vTaskDelay(portMAX_DELAY);
};

thread2.hpp

#include <cstdio>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

namespace os2
{

    struct comm_obj
    {
        SemaphoreHandle_t sem;
    };

    inline auto worker(void* const param) -> void
    {
        printf("Worker task before giving mux");
        fflush(stdout);

        auto& comm = *static_cast<comm_obj*>(param);

        xSemaphoreGive( comm.sem );

        printf("Worker after giving mux!\n");
        fflush(stdout);

        vTaskDelete(NULL);
    }

    struct thread2
    {

        thread2() {

            printf("init!\n");
            StaticSemaphore_t stack;

            comm_obj comm{
                .sem = xSemaphoreCreateBinaryStatic(&stack),
            };

            printf("Hello ");
            fflush(stdout);
            [[maybe_unused]] TaskHandle_t handle = xTaskCreateStaticPinnedToCore(
                &worker,
                "default",
                5*1024,
                &comm,
                10,
                stack_.data(),
                &tcb_,
                0);

            printf("World!");
            fflush(stdout);
            xSemaphoreTake( comm.sem, portMAX_DELAY );
        }

        StaticTask_t tcb_;
        std::array<StackType_t, 5*1024> stack_;
    };

}

This will just output:

init!
Hello World!

(and then stall which causes the task watchdog to be triggered)

But the worker task is never called. It seems that the program is blocking on xSemaphoreTake( comm.sem, portMAX_DELAY ); but in this case the task should yield and the newly created task should start to run (if my logic is correct). Why is that not happening?

Silly question, but do you start the scheduler / FreeRTOS somewhere ?

… As it turns out no lol. I thought this would happen automatically, but since I do static initialization with my C++ object, apparently the constructor code is run and will hit the semaphore block before the scheduler has an opportunity to start up which of course blocks everything after. Thanks for pointing that out to me!

Which indicates you don’t have the configASSERT macro defined, as that should trap on that condition.

1 Like