Timer callback signature

Hi,

I am using FreeRTOS in a c++ project, where I have a c++ wrapper on top of the functions i use in FreeRTOS so that I can mock FreeRTOS in unit tests.

E.g. this is how I create a thread

    void thread::start(
        size_t stack_size,
        size_t priority,
        const char * name,
        thread_delegate run,
        void * parameter)
    {
        project_assert(is_idle() , error_code_thread_already_running);

        _state = thread_state::running;
        _parameter = parameter;
        _delegate = run;

        xTaskCreate(
            task_callback,
            name,
            stack_size,
            this,
            priority,
            &_handle);
    }

Where the mocked method is as simple as

MOCK_METHOD((void), start, (size_t, size_t, const char *, thread_delegate, void *), (override));

The beauty here is, that the FreeRTOS callback for a task, provides the argument I passed as void *. I can smash my c++ object in there, and cast it back when FreeRTOS calls the callback.

The Timer looks similar, but instead of an argument I can pass a void * timerID. The problem I have, is that the timer id is not passed with the callback. I need to retrieve it through a pvTimerGetTimerID. And that is exactly what i need to mock out.

Maybe a code example makes it somewhat more clear

        _timer = _free_rtos.timer_create("VMC Sim", pdMS_TO_TICKS(1000), true, this, [](TimerHandle_t timer)
        {
            // Since C does not support capturing lambdas, I can't capture the _free_rtos interface here
            // I also can't call the pvTimerGetTimerID function, because FreeRTOS is not compiled in unit tests
            auto simulator = static_cast<vending_machine_simulator*>(gameover);
            simulator->run_state_machine();
        });

What would be brilliant for my case, is if the callback would also pass along the timer id (like the callback of a Task does with the parameter argument). Then I would be able to do this

        _timer = _free_rtos.timer_create("VMC Sim", pdMS_TO_TICKS(1000), true, this, [](TimerHandle_t timer, void * pvTimerID)
        {
            auto simulator = static_cast<vending_machine_simulator*>(pvTimerID);
            simulator->run_state_machine();
        });

So I added this myself in FreeRTOS

from:

typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer );

to:

typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer, void * pvTimerID );

, but that is kind of annoying, because now the next time I upgrade FreeRTOS I need to manually merge this.

Can you follow this story?

Am I making a mistake in the train of thoughts?

Can you think of an easier work around?

Would it be a problem to extend FreeRTOS with another timer callback which does fit better in C++ projejcts with the need to mock out FreeRTOS?

sounds good, what is preventing you from submitting a PR to Github?

Oh wauw, that is fast.

Well, I guess my current solution will be rejected, as it will be a breaking which won’t be appreciated ;-).

Which direction would be the right one? Making a config flag so that the old or new signature can be selected? Or make an overload on the xTimerCreate which accepts the new signature for callback?

oh i see, sorry… if the TimerHandle structure is truly opaque, maybe it can be modified to point to a structure in which some kind of signature (in the beginning) indicates old or new behavior. I do not have access to the code right now so can not say for sure if that will work.

When I did something like that, my rule was that the timer_id became reserved for use in the wrapper, so if the task needed something like that, it was put into the wrapper.

FreeRTOS provides a lot of the void* pointers which allow for this wrapping, but if you want to use them for wrapping, you can’t “mix” that usage.

If you are going to try to add something like this for a testing framework, you will need to make “low level” changes to the structure to add the pointer for mocking. If the code code is going to know about the wrapping, it needs to commit to the wrapping and not use the pointer used by the wrapping layer for other purposes, and only use the wrappers which can then store the “user” pointer for the timer in the wrapping.

Hmm, looking a bit closer to the implementation in FreeRTOS it would result in a lot of overloads to make this happen. I think I will just accept the fact that this is a “my problem” :). I will change it on my end, and pay the price when upgrading FreeRTOS in the future