I am using FreeRTOS on the LPC4350 using the IAR compiler. I am trying to decide the best way to handle thread local variables. I am going to be processing the same communications protocol over several serial ports, USB & Ethernet. Each one of the communication tasks will require the same set of control variables to remember it’s “state”. Each task should have a separate instance of each thread local variable. What is the best way to handle this requirement? I see some of the thread local support is provided, or at least stubbed out in IAR. Is it recommended to integrate with IAR or roll your own? I don’t see where FreeRTOS has any built in support for thread local storage. Are there any examples available which integrate FreeRTOS with IARs TLS framework?
I don’t know what the IAR system does, but FreeRTOS does not attempt to provide any special mechanism for doing this.
If the protocol is controlled in a task, can you have the state information as a stack variable in the task? You would have to make the stack big enough of course, but that would give each task its own copy very easily. If there is too much data, then have the task declare a pointer to the data on its stack, and then set the pointer to a block of RAM malloced by the task when the task starts to run. The result will be the same, but the RAM will be from the heap, not on the stack.
As Richard said, FreeRTOS doesn’t provide Thread Local Storage which looks like globals, but have a unique value for each thread. Typically this is implemented with some global “variable” which is changed to point to a different block of memory for each task (effectively a struct). You could add a field to the TCB storing this pointer, allocate a new block of memory of the size indicated by the compiler/linker for TLS in task create, and have the schedule change the appropriate variable on a task switch. Note that EVERY task will get the same set of TLS variables, so this doesn’t really work well for things lie communications protocol state, as it means that EVERY task will get allocated these variables.
If you don’t need the “global” nature of TLS, then each task naturally has its own stack variables as Richard pointed out that can be either used directly or to store a pointer to an allocation if you need bigger buffers. Another option that can be used in part is to pass the task a pointer to a control block, and create several tasks using the exact same task function. I commonly do this if I have a unit with multiple serial ports that, at least at the task level, are treated similarly. This same pointer can be passed to any functions that need similar information about the serial port.
Yes, I have thought about doing just what you said. The only problem is getting access to the pointer on the data stack from all of the subroutine functions that are called from the main task function. You would have to pass the pointer to each subroutine, taking up additional stack area. With complicated tasks this can be many levels deep. The code becomes much more wordy when you have to include the pointer in each reference also. The IAR compiler has support for TLS (thread local storage) which the user has to implement the interface for. You basically place all of the thread local variables in a specific section (__DLIB_PERTHREAD) and then allocate a separate storage area for each thread. The context switch updates a pointer to the beginning of the thread local storage area. I believe that the IAR compiler will then call special handling routines when these variables are accessed. These handler routines are what has to be implemented by the user for the particular RTOS. If you search for Multithreaded in the IAR C/C++ Development Guide you will see a how section in the manual that describes the support. Anyway, this may be something worth looking into in the future. It seems like this would be a very valuable feature to support in an RTOS. It helps to make the task functionality much easier to write when you have multiple instances of the same task like with this communications example.
Typically this is implemented with some global “variable” which is changed to point to a different block of memory for each task (effectively a struct).
There is floating about somewhere a copy of FreeRTOS that was adapted in just this way for integration with NewLib, which has a pointer to structures, the name of which that escapes me, to hold thread local variables.
The only problem is getting access to the pointer on the data stack from all of the subroutine functions that are called from the main task function.
Set configUSE_APPLICATION_TASK_TAG to 1, then use the pxTaskTag member of the task’s TCB. Look at the vTaskSetApplicationTaskTag() and xTaskGetApplicationTaskTag() functions. xTaskGetApplicationTaskTag() lets you obtain a value from the TCB, from any function, so long as that task is running.
I feel there are times where it’s appropriate to add thread-local storage variables, but in general I would _only_ do this for lib-function variables, such as errno.
Like the others have said, for general “state” values then stack variables are your best bet. After all, that’s what the stack is for!
Thank you all very much for your suggestions. This will give me enough to proceed. I think I will try the USE_APPLICATION_TASK_TAG suggestion. This will at least minimize my stack usage. Thanks again!
It sounds like with the TASK TAG implantation, it would be possible to implement TLS by also defining a few of the trace hooks to set the TLS pointer as the TASK TAG when a task is created, and to switch the current TLS pointer to the current TASK TAG on a task switch.
I will say that I find adding sending the pointer on function calls to not be that intrusive. Using TLS for these variables mean that EVER task will get them, so that not only do your serial tasks have the serial variables, but also every other task. This can add up to a lot more of a loss than the few bits to pass the pointer to structure down the call chain.
If you are writing C++ code, then it even makes sense to make these member functions of some protocol class that your task class is derived from and the passing is automatic.
If you are writing C code, then passing a pointer to a device descriptor structure seems natural for an implementation that is expecting to have multiple similar devices to specify which device we are working on here. Think of it like the FILE* parameter on all the file I/O functions. It would be a bit unusual to think of a fwrite function that used globals for per file information. Sometimes even seeing up this structure allows you to make some code more common, and use information in the structure to customize special cases out of the main code. To just use a global for the state hides the multiple instances and makes it to easy in my mind to get sloppy. Remember, you need to worry about access contention for “real” global variables, the these “pseudo” globals via TLS don’t need to worry (normally), when you start to get used to accessing “globle” variables (that are really TLS) you might forget that something ISN’T TLS, and forget about access synchronization.
That is a very good point! I think you have talked me into not trying to use TLS. I am converting an application that I have been working on for the past 20+ years on multiple previous hardware platforms. It is entirely written in FORTH (if you have ever heard of that). We used the concept of User variables in FORTH which are basically the same thing as TLS. It made the code convenient to write but I can see the the benefits of what you are saying - especially with the per task allocation requirements for TLS.
I realize this thread is part of an FAQ now - but; has anyone pursued getting the TLS with IAR and FreeRTOS running?
The goal of course is to have a thread-safe re-entrant CLIB to run in an embedded systems environment. I suppose an alternate approach is to compile a custom library and make sure the FreeRTOS memory manager controls all heap access; at least that’s a step in the right direction. I really don’t see the need to add more mutexes to an an environment where FreeRTOS already provides that.
Interesting thread. I remember FORTH too - but I always thought C was better. For scientific number crunching, Fortran '77 is still hard to beat.