I’m using C ++ with FreeRTOS 10.0.1, so I use the operators new, delete, new[], delete[], new MyClass(), vectors, new string(), string concatenation (which internally can cause a reallocation of the memory used by the string).
But I suspect that all these operations are not thread safe, can anyone confirm me?
If my suspicions are true, can I make these operations thread safe? for example by overriding “malloc (…)” and “free (…)”.
I read a lot of discussions about it, but I still preferred to open this discussion.
I also discovered “configUSE_NEWLIB_REENTRANT”, what is it? it can be useful?
By default, new/delete are often not thread safe in a FreeRTOS app because the basic malloc and free are not thread safe.
Some embedded libraries have hooks that can be defined to make these functions thread safe. For example, newlib, if it was compiled properly, will use the functions __malloc_lock() and __malloc_unlock() to allow you to protect the library from re-entrancy. I often define them with a call to vTaskSuspendAll() / vTaskResumeAll() to provide that protection.
You also may be able to replace them with alternate functions that do what you want. The one issue here is you need to dig into the library, as sometimes malloc/free are just thin wrappers around something deeper internally, and parts of the library may just call those internals, so THOSE are what willl need to be replaced.
If you are only worried about fixing new/delete, then you can over ride the implementation of those to use a thread safe allocator, (be sure to get new[] and delete{} too), but this will not fix usage of malloc and free.
configUSE_NEWLIB_REENTRANT is a flag that creates a structure for every thread that contains a bunch of internal state used by some functions in newlib, depending on some library options, this can be fairly small or quite big (as it can contain the File IO Buffers). Programs designed for small systems often don’t use the parts that need that structure on a per-task basis, as they don’t use that functionality. If you do use it, you need to define that to 1 to get the needed thread safety, or manually protect those parts of the library from reentrancy.
So I understand that “configUSE_NEWLIB_REENTRANT” would not solve my problem.
I’m thinking about overriding the operator new, delete, new[] and delete[].
But I’m not sure this is the best solution.
For example, the “string” class can allocate memory dynamically when concatenating a string.
string my_string = string ("Hello");
/* a memory allocation can be performed internally, which operator uses the "string" class? "new" or "malloc(...)"? */
my_string + = "world!"
OVerriding operatior new/delete that way will replace them, but only for things that use new/delete. string probably does, but I am not sure it is promised by the standard.
The better option in my mind is defining the functions
__malloc_lock() and __malloc_unlock()
if your implementation uses them (a quick check is to see if they appear in the linker map without you defining them), if it does then the proper definition will make the whole memory allocation system thread safe with no need to worry about if there is something getting arround the protections.
1>Linking ../MyProject/...
1>c:/sysgcc/arm-eabi/bin/../lib/gcc/arm-eabi/7.2.0/../../../../arm-eabi/lib/thumb/cortex_m4\libc_nano.a(lib_a-mlock.o): In function `__malloc_lock':
1>q:\gnu\newlib-nano\gcc-arm-none-eabi-6-2017-q2-update\src\newlib\newlib\libc\stdlib\mlock.c(53): error : multiple definition of `__malloc_lock'
1>VisualGDB/Debug/main.o:C:\visual_gdb_workspace\MyProject\MyProject/main.cpp:130: first defined here
1>collect2.exe : error : ld returned 1 exit status
That sounds like your newlib was somehow build wrong or you system isn’t compatible with a right definition as the definitions in mlock are supposed to be weak, so are ok to be overriden by your copy. That probably needs to be taken up with the supplyer of your tools for the processor.
While it should not be necessary, with gcc you can override the non-weak
implementations using the linker “wrap” option.
See example in code here (though different functions are wrapped):
What processor and toolchain are you using?
On 8/19/2019 6:55 AM, Richard Damon wrote:
That sounds like your newlib was somehow build wrong or you system isn’t compatible with a right definition as the definitions in mlock are supposed to be weak, so are ok to be overriden by your copy. That probably needs to be taken up with the supplyer of your tools for the processor.
Looking closer at my projects, yes __malloc_lock/__malloc_unlock is not weak, but is generally in a library file, and linkers will generally allow the overriding of entries in a library with a entry in an object file, at long as no other symbol in that module in the library is needed. For some reason you are getting a fatal link error for doing this.
Also, I would not use vPortEnter/ExitCritical for this purpose, as that will disable interrrupts for a time that is too long for me. I use VTaskSuspend/ResumeAll for this to just stop the scheduler. You could also use a mutex to only block other tasks that need to use malloc (but watch out for the sequencing of creating the mutex with using malloc/free.
I would not use vPortEnter/ExitCritical either, except in the STM case where the buffoons used malloc inside an ISR… That’s why vPortEnter/ExitCritical vs. VTaskSuspend/ResumeAll is conditionally compiled in my code grrrrr… ;-(
But you can’t call vPortEnterCritical inside an ISR, so it doesn’t solve the problem. (Now, I don’t use the STM code that uses malloc inside and ISR, I find a lot of the STM application support code desearving of replacement)
thank you all for your help.
I solved the problem, in my code (main.cpp) I had only overridden the function ‘malloc_lock(…)’, I had forgotten to override the function ‘malloc_unlock(…)’, and then for some reason the linker gave me that error.
When I overridden both the function the problem was solved, the functions are called correctly every time I do a “new”, “delete”, “new[]”, “delete[]”, “malloc(…)”, "free (…) ", concatenate a long string, push back into a vector.
Also now I’m using “vTaskSuspendAll()” and “xTaskResumeAll()” instead of “vPortEnterCritical()”, “vPortExitCritical()”. main.cpp
If you define just one of the functions then the linker does still need the module from the library and then does have the duplicate symbol for the one you did define, and get the error.
You could in a similar manner redefine nalloc and free, but you will run into a couple of problems:
newlib itself doesn’t call malloc and free, but instead something like __malloc_r() and __free_r()
FreeRTOS doesn’t provide an equivalent to remalloc() because it doesn’t need it, but the standard library does, so it isn’t a complete memory solution.
Because of this, I tend to just use a modified version of heap3, that just translate calls to pvProtMalloc to calls to malloc, and adds the heap exhausted test
I just made my own, starting with heap3.c, add the malloc_lock() and malloc_unlock() functions, and remove the calls to vTaskSuspendAll() and vTaskResumeAll() (since malloc/free will call them in lock/unlock).
Dear all,
For _malloc_lock and malloc_unlock instead of using vTaskSuspendAll and xTaskResumeAll could I use a mutex with priority inheritance (xSemaphoreTake and xSemaphoreGive)?
Yes, but you need to handle the issue of creating the mutex before you use it. I think there is a function called to give you a hook to do this. You will need to make the mutex statically or bypass the mutex while creating it.
yes I agree… during startup I could call a function inside heap.c file of freertos which create a mutex Calling xSemaphoreTake I don’t block all taskes. when I need to allocare memory from heap What do you think about it ?