Best practice for updating LVGL widgets under thread

Description

I am unclear on how best to update LVGL widgets under a thread.
I have a project where a thread periodically polls the serial port for data and updates the UI.
After some amount of time, my application crashes mostly with what appears to be memory issues.

I created a really simple project that simply shows the current time on a label widget every 500ms under a thread. This simple project also crashes after a period of time.

Here are some of the crash messages:

[Error] (199.920, +199920)       lv_tlsf_realloc: Asserted at expression: !block_is_free(block) && "block already marked as free"       (in lv_tlsf.c line #1208)

[Error] (199.920, +199920)       block_locate_free: Asserted at expression: block_size(block) > size       (in lv_tlsf.c line #775)

[Error] (998.580, +998580)       search_suitable_block: Asserted at expression: sl_map && "internal error - second level bitmap is null"        (in lv_tlsf.c line #577)

What MCU/Processor/Board and compiler are you using?

Raspberry Pi 4

What LVGL version are you using?

Latest

What do you want to achieve?

I would like to know best practice for updating widgets from a thread.

What have you tried so far?

Nothing

Code to reproduce

This is the thread function.

static void *AppThread( void *parameter )
{
    int halfseccntr = 50;
    char str[ 20 ];

    _controlThreadRunning = true;
    while ( _terminateControlThread == false )
    {
        //
        // The thread will sleep for 10ms then will see what needs to
        // be done before returning to sleep.
        //
        usleep( 10000 );        
        lv_tick_inc(10); 

        //
        // Countdown 50 x 10ms (0.5s)
        //
        if ( halfseccntr-- == 0 )
        {
            std::time_t t = std::time(0);   // get time now
            std::tm* now = std::localtime(&t);
            sprintf( str, "%02u %02u %02u", now->tm_hour, now->tm_min, now->tm_sec );
            lv_label_set_text( ui_lblTime, str );

            //
            // We enter here every second.
            // Do whatever we want to do once per second.
            //
            halfseccntr = 50;
        }
    }
}

The thread is created thus:

    volatile int rc = 0;

    _controlThreadRunning = false;
    _terminateControlThread = false;
    rc = pthread_create( &_controlThread, NULL, AppThread, NULL );
    if ( rc )
    {
        printf( "Failed to start the main firmware thread !\r\nTerminating this application\r\n" );
        pthread_exit( NULL );
    }

You have to use mutex when updating from an other thread. So lv_task_handler() is not called when you update lvgl.
Here c++ example

gui_mutex.lock();
lv_task_handler();

in the thread

unique_lock gui_lock(gui_mutex);|
lv_label_set_text …

2 Likes

Paul,

Many thanks for that :+1:
Gabor Kiss-Vamosi had suggested using a Mutex but I had missed the point of wrapping the mutex around the call to lv_task_handler as well as around my code in the thread.

Thanks again.

Andy

1 Like

Hi AndyFraser, there is still the problem with “tlsf_assert(block_size(block) >= size);” after I used Mutex for lv_task_handler(),where can I find the problem to debug it?

Sorry to bump an old thread, but I thought my experience might be relevant/helpful to some.

I experienced these crashes on ESP32-S3 + arduino as well, but I wasn’t starting threads explicitly, everything was traditional, one thread programming.
But turns out that the underlying rtos was somehow still distributing tasks to different threads:
My problem was one lvgl call (calling for an animation to play) which lived in a wifi callback function.
I replaced the lvgl call with a bool flag instead, which the main program loop dealt with, and the crashes were gone.
So you might think you’re being thread-safe, the integrated libraries might still be creating threads behind the scenes.

1 Like