Return from task

qyw wrote on Sunday, August 30, 2015:

I use some code which implements tasks that may return. This code should not be changed, should stay portable.
Will FreeRTOSv8 delete task automatically after it returns?

If not
Here is old topic from FreeRTOS Support Archive.

you can place the return address that is on the inital stack (setup when the task is created) to jump to the delete function. Then it would happen automatically which is ok as long as this is what you expect to happen.

How can I implement this idea?

Another idea
I can create wrapper function:

struct delegate { 
    void(*task)(void*);
    void *args;
};
task_wrapper( struct delegate *task )
{
    task->task( task->args );
    vTaskDelete( NULL );
}
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread , void *arg, int stacksize, int prio)
{
    sys_thread_t createdTask;
    struct delegate de = { thread, arg };  // created on stack. It may fail one day. Use malloc, or static...
    int result;
    result = xTaskCreate( task_wrapper, name, stacksize, &de, prio, &createdTask );
    if( result == pdPASS ){
        return createdTask;
    }
    return NULL;
}

but I think i is not good idea.

rtel wrote on Sunday, August 30, 2015:

Which port are you using?

richard_damon wrote on Sunday, August 30, 2015:

FreeRTOS currently (as far as I know) does not spend the resources to make it so that returning from a task causes it to automatically be deleted. Just jumping to the vTaskDelete is NOT enough, as you still need to give vTaskDelete a parmater of 0 to make it delete the current task, thus it would require adding several words onto the stack.

Also, most tasks do not just run and return, and adding a vTaskDelete(0) at the end, or wrapping the task function with a function that calls the task function and then vTaskDelete isn’t that bad of an option for the cases you need this behavior. I would say that less than 1% of the tasks I have written would want to terminate themselves, and thus benefit from this, by far the vast majority still have something to do, and after finishing their task, go back and wait to be tasked again. This means that the couple of words that would need to be added to EVERY tasks stack is a lot more expensive then the overhead of the wrapper (and adding vTaskDelete(0) at the end of the task is virtualy free, just a few locations of program space).

I suppose this comes from the basic idea of needing strong guarentees of operation, and the only time I can see wanting to use vTaskDelete is if you also create tasks after startup, which becomes a requirement to dynamically allocate memory at run time for something critical (unless the task isn’t really important). I find it much better to instead of creating and destroying tasks, to convert this into a set of tasks to handle all the needs that might come up.

I don’t quite understand how not adding vTaskDelete(0) to the end of the function makes it more portable? Do you really have a function that naturally takes a single void* parameter that also need to do NO other syncronization or communication with other tasks?

One big result of doing something like that is the task will most likely need to set some global flag that it has finished which some other task needs to poll to determine that it is done. If the task was blocked by being lower priority, it might as well have just called the function instead of starting a task.

qyw wrote on Monday, August 31, 2015:

Which port are you using?
CM4-F

rtel wrote on Monday, August 31, 2015:

Try the following:

  1. Define a function as follows:
void vDeleteCallingTask( void )
{
     vTaskDelete( NULL );
}
  1. Add the following to FreeRTOSConfig.h
extern void vDeleteCallingTask( void );
#define configTASK_RETURN_ADDRESS vDeleteCallingTask

Regards.

qyw wrote on Monday, August 31, 2015:

It would be cool.
But I cannot find any reference to configTASK_RETURN_ADDRESS in FreeRTOSv8.2.2 sources. I am afraid of function will not be called.

qyw wrote on Monday, August 31, 2015:

Thank you for this short lecture. I agree with you in most cases.

…that less than 1% of the tasks I have written would want to terminate themselves…

I give an example:

sockfd = socket(AF_INET, SOCK_STREAM, 0);
if( sockfd < 0 ){
    // Failed. Now task is worthless.
	debugf("create socket failed\n");
	return;  // from task
}

I don’t quite understand how not adding vTaskDelete(0) to the end of the function makes it more portable?

vTaskDelete is not understandable by other OSes. That part of code works under RT-Thread.
I have added vTaskDelete(), but I would like to retain compability in future.

rtel wrote on Monday, August 31, 2015:

It is used in the port.c file for the Cortex-M port. Did you try it?

richard_damon wrote on Tuesday, September 01, 2015:

Is that task FOREVER worthless? That would see to imply that if the task can’t ever get a socket again, than neither will the whole device you are part of (otherwise it could be better to have the task block until there is another need to try to get a socket and then retry).

Also, for the return to be the actual return from task implies that THIS FUNCTION is the task function, so it has been decided that void task(void* parm) is a “portable” as is assuming that a return from a task is portable for killing it (and as you have found it isn’t).

One solution is to make the actual task function something that calls this function and can do the vTaskDelete(0) after it returns. A second is to add a “OS Portability Layer” into the function and call a function something like taskExit() just before the return. taskExit() can be empty for RT-Thread, and call vTaskDelete(0) in FreeRTOS and do whatever else is needed in the next OS that you convert to.

A second major difference looks to be the types of things we do. This application looks to be a “web” based device that is responding on a “best effort” to answer requests. I presume best effort as I suspect that you are allocating a new task for every connection, and if you run out of memory to start the task you just fail. Almost everything I do has firmer requirements that MUST be meet or the device can be considered failed. For something like this, I would have a requirement that I must be able to handle say 4 simulatinous connections, so I would create a pool of 4 tasks sitting on a request queue. (If I have the memory, I might setup a 5th task to have some slack on the requirements). I rarely allocate ANY dynamic memory after the unit finishes startup.

qyw wrote on Tuesday, September 01, 2015:

There is no configTASK_RETURN_ADDRESS in FreeRTOSv8.2.2/Source/portable/RVDS/ARM_CM4F/port.c
Function vDeleteCallingTask compilies as unused.

rtel wrote on Tuesday, September 01, 2015:

I don’t think you mentioned you were using RVDS before, I was looking at line 145 in https://sourceforge.net/p/freertos/code/HEAD/tree/tags/V8.2.2/FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c

In the RVDS version the return address is hard coded to prvTaskExitError(). You could make that a week symbol so you can override it, or update the file so it can be overridden using the pre-processor as per the GCC version.

qyw wrote on Tuesday, September 01, 2015:

Thank you.

qyw wrote on Sunday, September 06, 2015:

Thank you Richard. I ask you to write a short article-post about How to write and manage proper tasks for socket-style network app (like ftp server), advisable with code snippets. Here I’ve found LwIP apps. Some of them I’d like to port to FreeRTOS+FATFS with backward compability.