Tasks v/s Threads

Hi Community members,
I’m new to this community and to FreeRTOS as well. So, apologies if the question/topic is already discussed. I want to know the difference between Tasks and Threads (POSIX). I’ve seen many examples where Threads are being used. Some of them I could replace with Tasks and got the same functionality achieved. However, some Threads threw errors when I replaced them with Tasks.
Can someone please help?

Regards,
H C Trivedi

There is no difference - In a FreeRTOS application, every independent thread of execution is a task. See this page for fundamentals of FreeRTOS.

Which examples are you talking about here?

What are those errors?

In a “Big” system like POSIX, the system is divided into Processes and each process can have multiple Threads. Processes are well isolated from each other and Threads are easy to interact with each other.

FreeRTOS doesn’t implement both of these, but instead implements a “Task” that is in many ways close to a POSIX thread in capability, but also somewhat like a Process in usage (in y experience, they tend to be long lived like a processes).

They ARE different models of processing, so you shouldn’t just think of them as the same. Note, there is a “POSIX Emulation” setup, but don’t think of it as a way to ignore the difference. I would say one of the biggest challages of someone coming from a POSIX environment is “unlearning” how you do things in POSIX and replacing it with an embedded design methodology.

I’m using CC3235 device from Texas Instruments. This is basically a WiFi Network Processor. In one of the example codes I tried replacing treads with tasks. Some of it worked and some of it did not. The same are as follows:

  1. in main_freertos.c code below, I replace mainThread with mainTask and the code ran without any error. However, in platform.c file, when I tried replacing spawn thread with spawn task, the code did not run at all. It ends up stating that “the processor received a fault interrupt”.
/*
 *  ======== main_freertos.c ========
 */
#include <stdint.h>

/* POSIX Header files */
#include <pthread.h>

/* RTOS header files */
#include "FreeRTOS.h"
#include "task.h"

/* TI-DRIVERS Header files */
#include "ti_drivers_config.h"

/* TI-RTOS Header files */
#include <ti/drivers/GPIO.h>

extern void * mainThread(void *arg0);

/* Stack size in bytes */
#define THREADSTACKSIZE   4096

/*
 *  ======== main ========
 */
int main(void)
{
    pthread_t thread;
    pthread_attr_t pAttrs;
    struct sched_param priParam;
    int retc;
    int detachState;

    /* Call board init functions */
    Board_init();

    /* Set priority and stack size attributes */
    pthread_attr_init(&pAttrs);
    priParam.sched_priority = 1;

    detachState = PTHREAD_CREATE_DETACHED;
    retc = pthread_attr_setdetachstate(&pAttrs, detachState);
    if(retc != 0)
    {
        /* pthread_attr_setdetachstate() failed */
        while(1)
        {
            ;
        }
    }

    pthread_attr_setschedparam(&pAttrs, &priParam);

    retc |= pthread_attr_setstacksize(&pAttrs, THREADSTACKSIZE);
    if(retc != 0)
    {
        /* pthread_attr_setstacksize() failed */
        while(1)
        {
            ;
        }
    }

    retc = pthread_create(&thread, &pAttrs, mainThread, NULL);
    if(retc != 0)
    {
        /* pthread_create() failed */
        while(1)
        {
            ;
        }
    }

    /* Start the FreeRTOS scheduler */
    vTaskStartScheduler();

    return (0);
}

//*****************************************************************************
//
//! \brief Application defined malloc failed hook
//!
//! \param  none
//!
//! \return none
//!
//*****************************************************************************
void vApplicationMallocFailedHook()
{
    /* Handle Memory Allocation Errors */
    while(1)
    {
    }
}

//*****************************************************************************
//
//! \brief Application defined stack overflow hook
//!
//! \param  none
//!
//! \return none
//!
//*****************************************************************************
void vApplicationStackOverflowHook(TaskHandle_t pxTask,
                                   char *pcTaskName)
{
    //Handle FreeRTOS Stack Overflow
    while(1)
    {
    }
}

void vApplicationTickHook(void)
{
    /*
     * This function will be called by each tick interrupt if
     * configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h.  User code can be
     * added here, but the tick hook is called from an interrupt context, so
     * code must not attempt to block, and only the interrupt safe FreeRTOS API
     * functions can be used (those that end in FromISR()).
     */
}

void vPreSleepProcessing(uint32_t ulExpectedIdleTime)
{
}

//*****************************************************************************
//
//! \brief Application defined idle task hook
//!
//! \param  none
//!
//! \return none
//!
//*****************************************************************************
void
vApplicationIdleHook(void)
{
    /* Handle Idle Hook for Profiling, Power Management etc */
}

//*****************************************************************************
//
//! \brief  Overwrite the GCC _sbrk function which check the heap limit related
//!         to the stack pointer.
//!         In case of freertos this checking will fail.
//! \param  none
//!
//! \return none
//!
//*****************************************************************************
#if defined (__GNUC__)
void * _sbrk(uint32_t delta)
{
    extern char _end;     /* Defined by the linker */
    extern char __HeapLimit;
    static char *heap_end;
    static char *heap_limit;
    char *prev_heap_end;

    if(heap_end == 0)
    {
        heap_end = &_end;
        heap_limit = &__HeapLimit;
    }

    prev_heap_end = heap_end;
    if(prev_heap_end + delta > heap_limit)
    {
        return((void *) -1L);
    }
    heap_end += delta;
    return((void *) prev_heap_end);
}

#endif
#include <ti/drivers/net/wifi/simplelink.h>
#include <ti/drivers/net/wifi/slnetifwifi.h>

#include <ti/display/Display.h>

#include <ti/drivers/SPI.h>

// TI-Driver includes
#include "ti_drivers_config.h"
#include "pthread.h"

#define APPLICATION_NAME                      ("UDP Echo")
#define APPLICATION_VERSION                   ("1.0.0.0")
#define DEVICE_ERROR                          ("Device error, please refer \"DEVICE ERRORS CODES\" section in errors.h")
#define WLAN_ERROR                            ("WLAN error, please refer \"WLAN ERRORS CODES\" section in errors.h")
#define SL_STOP_TIMEOUT                       (200)

#define UDPPORT 							  (1000)
#define SPAWN_TASK_PRIORITY                   (9)
#define TASK_STACK_SIZE                       (2048)
#define SLNET_IF_WIFI_PRIO                    (5)
#define SLNET_IF_WIFI_NAME                    "CC32xx"

#define SSID_NAME                             "DemoAP"                  /* AP SSID */
#define SECURITY_TYPE                         SL_WLAN_SEC_TYPE_WPA_WPA2 /* Security type could be SL_WLAN_SEC_TYPE_OPEN */
#define SECURITY_KEY                          "12345678"                /* Password of the secured AP */

pthread_t udpThread = (pthread_t)NULL;
pthread_t spawn_thread = (pthread_t)NULL;
int32_t             mode;
Display_Handle display;

extern void echoFxn(uint32_t arg0, uint32_t arg1);
extern int32_t ti_net_SlNet_initConfig();

/*
 *  ======== printError ========
 */
void printError(char *errString, int code)
{
    Display_printf(display, 0, 0, "Error! code = %d, Description = %s\n", code,
            errString);
    while(1);
}

/*!
    \brief          SimpleLinkNetAppEventHandler

    This handler gets called whenever a Netapp event is reported
    by the host driver / NWP. Here user can implement he's own logic
    for any of these events. This handler is used by 'network_terminal'
    application to show case the following scenarios:

    1. Handling IPv4 / IPv6 IP address acquisition.
    2. Handling IPv4 / IPv6 IP address Dropping.

    \param          pNetAppEvent     -   pointer to Netapp event data.

    \return         void

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) section 5.7

*/
void SimpleLinkNetAppEventHandler(SlNetAppEvent_t *pNetAppEvent)
{
    int32_t             status = 0;
    pthread_attr_t      pAttrs;
    struct sched_param  priParam;

    if(pNetAppEvent == NULL)
    {
        return;
    }

    switch(pNetAppEvent->Id)
    {
        case SL_NETAPP_EVENT_IPV4_ACQUIRED:
        case SL_NETAPP_EVENT_IPV6_ACQUIRED:

            /* Initialize SlNetSock layer with CC3x20 interface                      */
            status = ti_net_SlNet_initConfig();
            if(0 != status)
            {
                Display_printf(display, 0, 0, "Failed to initialize SlNetSock\n\r");
            }

            if(mode != ROLE_AP)
           {
                Display_printf(display, 0, 0,"[NETAPP EVENT] IP Acquired: IP=%d.%d.%d.%d , "
                                       "Gateway=%d.%d.%d.%d\n\r",
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,3),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,2),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,1),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Ip,0),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,3),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,2),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,1),
                                       SL_IPV4_BYTE(pNetAppEvent->Data.IpAcquiredV4.Gateway,0));

                pthread_attr_init(&pAttrs);
                priParam.sched_priority = 1;
                status = pthread_attr_setschedparam(&pAttrs, &priParam);
                status |= pthread_attr_setstacksize(&pAttrs, TASK_STACK_SIZE);
                status = pthread_create(&udpThread, &pAttrs, (void *(*)(void *))echoFxn, (void*)UDPPORT);
                if(status)
                {
                    printError("Task create failed", status);
                }
            }
            break;
        default:
            break;
   }
}
/*!
    \brief          SimpleLinkFatalErrorEventHandler

    This handler gets called whenever a socket event is reported
    by the NWP / Host driver. After this routine is called, the user's
    application must restart the device in order to recover.

    \param          slFatalErrorEvent    -   pointer to fatal error event.

    \return         void

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) section 17.9.

*/
void SimpleLinkFatalErrorEventHandler(SlDeviceFatal_t *slFatalErrorEvent)
{
    /* Unused in this application */
}
/*!
    \brief          SimpleLinkNetAppRequestMemFreeEventHandler

    This handler gets called whenever the NWP is done handling with
    the buffer used in a NetApp request. This allows the use of
    dynamic memory with these requests.

    \param          pNetAppRequest     -   Pointer to NetApp request structure.

    \param          pNetAppResponse    -   Pointer to NetApp request Response.

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) section 17.9.

    \return         void

*/
void SimpleLinkNetAppRequestMemFreeEventHandler(uint8_t *buffer)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkNetAppRequestEventHandler

    This handler gets called whenever a NetApp event is reported
    by the NWP / Host driver. User can write he's logic to handle
    the event here.

    \param          pNetAppRequest     -   Pointer to NetApp request structure.

    \param          pNetAppResponse    -   Pointer to NetApp request Response.

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) section 17.9.

    \return         void

*/
void SimpleLinkNetAppRequestEventHandler(SlNetAppRequest_t *pNetAppRequest, SlNetAppResponse_t *pNetAppResponse)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkHttpServerEventHandler

    This handler gets called whenever a HTTP event is reported
    by the NWP internal HTTP server.

    \param          pHttpEvent       -   pointer to http event data.

    \param          pHttpEvent       -   pointer to http response.

    \return         void

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) chapter 9.

*/
void SimpleLinkHttpServerEventHandler(SlNetAppHttpServerEvent_t *pHttpEvent,
                                      SlNetAppHttpServerResponse_t *pHttpResponse)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkWlanEventHandler

    This handler gets called whenever a WLAN event is reported
    by the host driver / NWP. Here user can implement he's own logic
    for any of these events. This handler is used by 'network_terminal'
    application to show case the following scenarios:

    1. Handling connection / Disconnection.
    2. Handling Addition of station / removal.
    3. RX filter match handler.
    4. P2P connection establishment.

    \param          pWlanEvent       -   pointer to Wlan event data.

    \return         void

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) sections 4.3.4, 4.4.5 and 4.5.5.

    \sa             cmdWlanConnectCallback, cmdEnableFilterCallback, cmdWlanDisconnectCallback,
                    cmdP2PModecallback.

*/
void SimpleLinkWlanEventHandler(SlWlanEvent_t *pWlanEvent)
{
    /* Unused in this application */
}
/*!
    \brief          SimpleLinkGeneralEventHandler

    This handler gets called whenever a general error is reported
    by the NWP / Host driver. Since these errors are not fatal,
    application can handle them.

    \param          pDevEvent    -   pointer to device error event.

    \return         void

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) section 17.9.

*/
void SimpleLinkGeneralEventHandler(SlDeviceEvent_t *pDevEvent)
{
    /* Unused in this application */
}

/*!
    \brief          SimpleLinkSockEventHandler

    This handler gets called whenever a socket event is reported
    by the NWP / Host driver.

    \param          SlSockEvent_t    -   pointer to socket event data.

    \return         void

    \note           For more information, please refer to: user.h in the porting
                    folder of the host driver and the  CC31xx/CC32xx NWP programmer's
                    guide (SWRU455) section 7.6.

*/
void SimpleLinkSockEventHandler(SlSockEvent_t *pSock)
{
    /* Unused in this application */
}

void Connect(void)
{
    SlWlanSecParams_t   secParams = {0};
    int16_t ret = 0;
    secParams.Key = (signed char*)SECURITY_KEY;
    secParams.KeyLen = strlen(SECURITY_KEY);
    secParams.Type = SECURITY_TYPE;
    Display_printf(display, 0, 0, "Connecting to : %s.\r\n",SSID_NAME);
    ret = sl_WlanConnect((signed char*)SSID_NAME, strlen(SSID_NAME), 0, &secParams, 0);
    if (ret)
    {
        printError("Connection failed", ret);
    }
}

void mainThread(void *pvParameters)
{
    int32_t             status = 0;
    pthread_attr_t      pAttrs_spawn;
    struct sched_param  priParam;

    SPI_init();
    Display_init();
    display = Display_open(Display_Type_UART, NULL);
    if (display == NULL) {
        /* Failed to open display driver */
        while(1);
    }

    /* Start the SimpleLink Host */
    pthread_attr_init(&pAttrs_spawn);
    priParam.sched_priority = SPAWN_TASK_PRIORITY;
    status = pthread_attr_setschedparam(&pAttrs_spawn, &priParam);
    status |= pthread_attr_setstacksize(&pAttrs_spawn, TASK_STACK_SIZE);

    status = pthread_create(&spawn_thread, &pAttrs_spawn, sl_Task, NULL);
    if(status)
    {
        printError("Task create failed", status);
    }

    /* Turn NWP on - initialize the device*/
    mode = sl_Start(0, 0, 0);
    if (mode < 0)
    {
        Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, DEVICE_ERROR);
    }

    if(mode != ROLE_STA)
    {
        /* Set NWP role as STA */
        mode = sl_WlanSetMode(ROLE_STA);
        if (mode < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, WLAN_ERROR);
        }

        /* For changes to take affect, we restart the NWP */
        status = sl_Stop(SL_STOP_TIMEOUT);
        if (status < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, status, DEVICE_ERROR);
        }

        mode = sl_Start(0, 0, 0);
        if (mode < 0)
        {
            Display_printf(display, 0, 0,"\n\r[line:%d, error code:%d] %s\n\r", __LINE__, mode, DEVICE_ERROR);
        }
    }

    if(mode != ROLE_STA)
    {
        printError("Failed to configure device to it's default state", mode);
    }

    Connect();
}

In main_freertos.c file, I did modifications as follows:

retc = xTaskCreate (mainThread, "mainThread1", THREADSTACKSIZE, NULL, 3, NULL);

In platform.c file, I did modifications as follows:

status = xTaskCreate(spawn_thread, "Task 2", 1000, sl_Task, 1, NULL);

Please let me know if you need any further input on this from my side.

Regards,
H C Trivedi

Hello Richard,
just to acknowledge the fact that you suggested, I’m actually not from POSIX background and novice to Embedded Design. Hence a little confusion between threads and tasks.
After referring to mastering FreeRTOS guide, I found tasks much simpler to implement and hence intending to replace threads in the example code with tasks. Bust facing many challenges in doing so.

Regards,
H C Trivedi

TI provide a POSIX threading wrapper on top of FreeRTOS. The implementation of the POSIX thread create API just creates a FreeRTOS task. FreeRTOS applications can be thought of as consisting of threads that all run in the same processes as there is not native concept of processes in FreeRTOS - hence to avoid confusion we avoid the term “thread” and instead just say “task”.

I would suggest NOT using a “POSIX Wrapper” to program for FreeRTOS unless you are already ingrained into thinking of POSIX threads. The problem is that basically by definition, such a wrapper is a compromise and needs to use concepts that aren’t actually fully defined. The objects it creates are NOT full-fledged POSIX threads, because FreeRTOS doesn’t actually support all the needed details, and it needs to hide some of the abilities for FreeRTOS Tasks, as they aren’t available in the POSIX view.

This means that the only good “support” for using that package, is the package itself. You can’t go to generic POSIX documentation and help, as you don’t know enough to know if that is applicable here.

Hello @richard-damon ,

I’m not at all interested into using Threads. However, as I’ve stated above, Example file being provided by TI includes threads in its code. While I try to replace thread with tasks, it says, "the processor received a fault interrupt”. Code modification for the same is also provided above. That’s where I’m struggling. Can you please help?

Regards,
H C Trivedi

Hello @rtel ,

I understood the concept. Thanks for that. However, replacing the Thread thing in example with Tasks doesn’t work for me.

Regards,
H C Trivedi

Since there IS a wrapper, you can’t just “replace” a “Thread” with a “Task”, but you need to change the code that refers to it from the pthread wrapper to the FreeRTOS calls. That includes any auxiliary usage of the handle. If you are using some of their libraries, this may be impossible.

This isn’t always trivial.

Since you are copying an example, and don’t seem to really understand it, you are likely missing some step in the conversion process.

While in my mind thinking of your problem in terms of Tasks should be better than thinking of it in terms of sort-of pthreads, since your example is already using pthreads, trying to mix the two is likely the worst of both worlds.

Hey Richard,

I had just referred to the example for the sake of understanding the imlpementation. Now my application is also in line with the example. In TI documentations, it suggests that spawn thread is a must thing before starting the network processor. But that’s the only thing I currently know. No any other literature available on this. Also, I’ve written entire application on my own using Tasks only. This is the only portion of the code that I’m struggling with currently.

Regards,
H C Trivedi

Information about the TI system will need to come from TI. We have no idea what additional things they do when creating a “Thread” above the creation of the FreeRTOS task.

They may well use some of the hooks that FreeRTOS provides to setup additional information to support their threads.