I have a question about using operator “new” inside/outside a FreeRTOS task.
Board: SmartFusion2 Evaluation Board
Language: C++
IDE: SoftConsole v2021
Use newlib-nano
It looks using “new” outside FreeRTOS tasks is working fine. If I use “new” to allocate memory inside a FreeRTOS task, it will crash the debugger. But if I use “pvPortMalloc()” instead, then it is fine.
Could you please help me understand what new() does inside/outside a FreeRTOS, and why it causes crashes inside a FreeRTOS task?
“new” will almost certainly be calling malloc internally, and malloc inside a task would need to have been made “thread-safe”. Your “crash” may be due to two different tasks being inside malloc at the same time and corrupting the heap.
You will need to see if the version of newlib that SoftConsole is using was complied with the thread-safety capability in place, if so there will be functions in the linker map for malloc_lock and malloc_unlock. If there is, then you need to write your own version of these functions where malloc_lock calls vTaskSuspendAll() and malloc_unlock calls vTaskResumeAll() to protect the heap. If not, you will need to replace the malloc and free in the library with a version that has been compiled to use those.
In addition as another method you could use the (GCC) linker wrap feature to wrap malloc, calloc, free along with their reentrant versions with the trailing _r (as in commonly used newlib) or replace them e.g. with the thread-safe FreeRTOS heap calls in the corresponding wrapper functions in the linked application (elf) if you don’t want to patch the provided C-library assuming you’re using GCC toolchain. Maybe other toolchains provide a similar feature.
It is also very straightforward to globally override the new operator. That alone, however, will not be too useful as (as other pointed out) the pitfalls lie in the underlying malloc() implementation.
To prevent yourself from now having two sets of memory (one used by freeRTOS’s heap and one used by ‘new’), you could also override new to instead have it call the freeRTOS heap, consolidating all memory usage into a single location. This is what we do in our programs:
One problem with over-riding new and delete that way, is you still leave malloc and free (which the library may use) pointing to the standard library implementation. That is why I first look to see what is needed to make the standard libary malloc/free/etc thread safe, but does require a bit more work.
It looks the problem goes away if over-riding new/delete operators with pvPortMallc() and vPortFree(). Also, in the linker map file, there are __malloc_lock(), __malloc_unlock(), _malloc_r(). I will keep watching if any issues are coming up.
As Richard mentioned a few C-library functions use malloc/malloc_r internally.
Hence you should implement the malloc_lock/unlock hooks (e.g. by just calling vTaskSuspend/ResumeAll) if it’s ok for you to use the C-library heap in addition to the FreeRTOS heap (interface).
One more thing. In case you’re using heap_3 which just wraps C-library malloc/free to make them thread-safe analog to the malloc_lock/unlock hooks you could ‘implement’ pvPortMalloc/Free as simple macros defined in your FreeRTOSConfig.h like
Note, pvPortMalloc isn’t exactly just a call to malloc, but it also will test the return value a call the failure hook on error, so I make an alternative heap_malloc.c that calls malloc, and tests the results.
That’s right Richard. Thanks for the pointer ! Then the macro approach might be an idea for the optimized Release build and/or if the malloc fail hook isn’t used. The success of dynamic allocations is and should be checked anyway.