When and where to create a task, what's preferred?

I’ve got the usual large program, configuration controlled by include files abusing #ifdef structures.

This is running on an STMicro STML562 with 512K flash and 256K of internal RAM. The QSPI memory interface runs a 32 MBit memory mapped into the processor address space as 2 MB of 32 bit words (32 bit ARM processor).

I have a number of queues in the system, which is packet oriented, has numerous communications, mesh networking, and supports multiple displays, so not quite simple.

My previous architecture was to use the skeleton generated by CubeMXIDE, which uses FreeRTOS 10.3.1 (takes them forever to upgrade RTOS since they prefer Azure), add a call to a setup program (CPP_LINK) after the default task has been created. CPP_LINK creates the main application task.

CPP_LINK was called from the default task before the loop, and then called the main application setup, creating the application task. This returned to the default task which was a 10 second loop since I never used it. (and CubeMXIDE always creates this task).

The IOC configurator has the ability to set up tasks and the like outside of the main task, but has no awareness of conditionals, so the way I configure the system won’t work.

What it looks like to me (and first question) is that the code needed to create a task takes a lot of FreeRTOS heap (I use 4.c). The FreeRTOS stack was set to user supplied, and is maxed out at 256K in the QSPI memory.

I kept having to increase the size of the default task as I added more and more program features, to the point where I was running out of that heap memory. The program kept crashing, or performing unreliably (overwriting things and returning bad results from the scans of queues, semaphores, and tasks).

I moved the creation routine (CPP_LINK) out of the task, and put it into the main program before the oskernelstart, so no task creation inside a task. I have just finished putting the rest of the main program loop into a similar situation, with the main task created, but not actively running. All the setups are not in the main program loop, so that seems to be working properly.

So, design wise, is FreeRTOS really intended to create tasks gracefully from within another task? Is that task creation a temporary memory hog which requires one-time use of the default task’s heap?

Question 2: I noticed that the debugger was erratic until I increased some task heap sizes, but I’m not sure if FreeRTOS has the capability to figure this out. I do have a modified “malloc failed” hook that tells me how much memory I needed and what the shortfall was, but that just came back with zero. So: no task overrun catching? I do have checkforstackoverflow set to option 2, but I may not be using it properly since it doesn’t seem to trip.

I got a lot of hard fault memory accesses, some of which were my fault in the graphics object tree, some of which were caused by memory pointers being corrupted.

Somewhat long, but the devil is in these details. Have I guessed correctly about the design intent of task creation? Things seem to be working better now.

in summary, FreeRTOS heap is in a dedicated memory area per program configuration. Graphics tree, graphics programs, and things not in FreeRTOS are generally in XHEAP (separate memory manager) memory, and should be well behaved. Actual graphics display memories are in yet another area of memory, so everything should be nicely partitioned

You have some terminolgy mixup, as tasks don’t have an individual HEAP, they get a dedicated individual STACK.

Creating a task in a task shouldn’t cost must task stack for doing that, but some functions like printf can use a lot, so if you use that for debugging there could be extra usage for that.

My personal preference is to try to create all my FreeRTOS objects (Tasks, Queue, Semaphores, etc) before starting up the system (this avoids most worries of using something before creating it, and where possible using static allocation for them so it is clear where my memory is being use. IF dynamically creating them, then they will use as much heap to create as the stack allocated to it, plus the TCB overhead.

I also dislike using the vendor frameworks, as they tend to force me into ways I don’t want, but it does mean that I need to handle everything. I will use the IOC configurator to figure out the settings, and then copy them into my own configuration settings system that runs at program startup.

1 Like

well, terminology aside, it’s doing what it wants to do.

What I notice is that a program segment in a task that creates a task can have a very large stack requirement, then once the task is created and the system is running, will have a much smaller stack high water mark (as shown by CubeMXIDE). Hence the question

I can probably create the tasks and stacks before hand, and in fact, that’s what I’m doing since there are no tasks running when I create most of the tasks, stacks, and queues.
None of the tasks I’m creating create more stacks, etc.

I don’t use printf except in certain circumstances, and these ain’t them.

I could likely go to static allocation, but for now, I think I’ll wait.

The vendor framework has been bypassed for the most part, since I only use it to set up peripherals.

So no idea so far why the high water mark changes, then goes down? or isn’t that what the CubeMXIDE displays?

I just browsed the code for xTaskCreate() real quick ( FreeRTOS-Kernel/tasks.c at main · FreeRTOS/FreeRTOS-Kernel · GitHub) but could not find any indication that the function itself uses a lot of automatic variables. Could it be that the implementation of traceENTER_xTaskCreate() in your eco system is itself stack hungry (poster case of the Heisenberg effect)?

well, I have no idea. I’m not sure what to look at, although I did skim through the code. I couldn’t find traceENTER_xTaskCreate() at all, but then again, this is version 10.3.1 and ST-Micro is relatively indifferent in upgrading FreeRTOS to the latest. That’s them, though.

I’m guessing I just need to live with it for now.

Thanks, though.

From my understanding, the implementation is NOT part of FreeRTOS code but is to be supplied by either the developer or the tool vendor. It should be easy to find, though. An easy test to determine if that is the culprit is to comment out the macro (and of course the matching traceEXIT as well) and see if you can replicate your observation that the task guzzles a lost of stack during xTaskCreate but not afterwards.

As @RAc already pointed, there is no specific stack requirement at the time of task creation. Would you please share how you concluded that?

FreeRTOS will not have such large stack requirements. Note, “High Water Mark” (from FreeRTOS) is the amount of stack LEFT, not used, and can’t move in a direction to indicate more available stack.

If the tool is looking at the current stack pointer, that could go down.

If stack usage is high, you may want to look at the variable created at each level and see if anything has large arrays.

Trying to track memory use and possible overflows where tasks act very oddly,
I stepped through the program watching tasks. One task starts off with, say, 88/41000, seems to hit 40500/41000, then when reviewed next is back at 88/41000.

Likely doing something wrong.

Presumably, the CubeMXIDE task monitoring gives you high water mark?

WHich means it can’t be a “High Water Mark”, as those don’t go back down (unless you delete and then recreate the task).

My guess is that this is a CURRENT stack usage, so you should see where it jumps up and see what function is using all that memory, likely a large array of some sort being used.

May be some IDE issue. You can probably use uxTaskGetStackHighWaterMark to find stack high watermark.

What problem are you facing? Can you share code?

Unfortunately, the problem (and the code) is complex enough that I don’t quite know how to reproduce it. The basic problem is that I’m seeing crashes in the code due to pointers mutating or data mutating. Thinking that this is due to insufficient stack, which is a possibility, I increased the stack available to the task.
The IDE shows 98 to 99.5 (or so) percent use, so obviously increase available memory. The problem is that I seem to be using a lot of memory, so it’s a bit troubling.

Now those are tasks shown by the IDE. I’ve moved most (if not all) task creation, queue creation, and semaphore creation out of the perview of any running task. All this is run before the OSKernelInit routine.

A further problem is that this is mostly C++ code and tasks are being created within C++ classes. Although, the whole thing is quite stable when it works, and it mostly works.

I may have to automatically build statistics into each task.

The problem with making changes without confirming the root cause is that you might just be masking the real problem. I’d suggest to use data breakpoint (or something else) to first confirm that the cause is a stack overflow.

One thought that comes to mind with making tasks in classes, is that if the class includes the stack for the task, the object for that class will be large, and must stay in existence for the duration of the task.

The classes I use to create tasks are designed to be used as global variables, so the stack comes out of global memory, not task stack.

The task owns the taskpointer. The task is defined outside the class, although created within the class. Classes are created dynamically, though.

You’d suggest that the class be created statically with deliberate assignments from global memory?

These are objects (and tasks) that are created, and because it’s hardware support for existing hardware, the task is never deleted nor is the object.

I personally try to minimize use of dynamic memory, and stack to a reason. Tasks are created with their stack statically allocated at fixed locations in ram allocated by the linker in arrays statically allocated.

Your problem is to locate what is causing short term explosion in your stack usage, One risk of classes is it can become easier to just tuck a big array into one and forget about it, as it is a layer or two below what you are looking at.

1 Like

OK, I’m looking for a suggestion here.
I have two areas of memory, one the processor’s RAM, the other is the 2M (*32) add on memory.
Other than display images, I have a FreeRTOS memory of 256K (IDE insists on that limit), and a managed memory area of another 256K (acts a lot like Heap4).

All of the FreeRTOS allocations can be put into that managed memory by using static allocations (I have been using dynamic). Most of the things I use malloc for are in managed memory. NEW (C++) is used only when managed memory is not available (#ifdef). Instances of classes are also in managed memory.

Does this seem to be a better solution?

Just to be sure, your custom heap implementation is also thread safe like e.g. heap_4 ?

Good question, and thankfully I already thought of that. Malloc, calloc and Free for that are protected by semaphores. It returns a void pointer that must by appropriately typed.