Lv_scr_load_anim() with delay causes an assert

Description

LV_ASSERT_OBJ fires when performing a screen transition with delay.

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

Occurs on two platforms: STM32 and Qt on a Mac

What do you experience?

When a delay is used for the transition, we take the assert.

Changing the delay from 100 to 0 prevents the assert. I have not tried further variants of time or transition style.

It appears to be timing related, sometimes the example code gets through 20 cycles and sometimes just one or two.

What do you expect?

No assert.

Code to reproduce

// If 'delay' in the lv_scr_load_anim() call is 100, after some
// number of cycles (1-20 typically) this happens:
//
//    Error: lv_obj_get_local_style 	(lv_obj.c #2927 lv_obj_get_local_style())
//    Error: Invalid object (0x00000001001ECE50) 	(lv_debug.c #127 lv_debug_log_error())
//
// The check that fails is:
//      lv_debug_check_obj_valid()
//
// And the object that is invalid is the screen being transitioned to.
//
// Setting the delay to 0 appears to fix it. (can run for thousands of cycles)
//
// Occurs on 7.7.1, although seen on earlier releases as well.


#include <stdio.h>

#include "lvgl/lvgl.h"


static void next_screen( lv_task_t *task );
lv_obj_t *make_screen( const char *name );


void ui_init(void)
{
    // Make sure any printf() output gets flushed.
    setvbuf( stdout, NULL, _IOLBF, 0 );

    lv_task_create( next_screen, 500, LV_TASK_PRIO_MID, NULL );
}



static void next_screen( lv_task_t *task )
{
    static int count;
    static bool f = true;

    const char *name;

    if( f )
    {
        name = "Screen A";
        f = false;
    }
    else
    {
        name = "Screen B";
        f = true;
    }

    lv_obj_t *next = make_screen( name );

    printf( "Begin %s load (%d)   %p  ->  %p\n", name, ++count, lv_scr_act(), next );

    // If 'delay' is 0 the problem goes away.
    lv_scr_load_anim( next, LV_SCR_LOAD_ANIM_MOVE_TOP, 200, 100, true );
}


lv_obj_t *make_screen( const char *name )
{
    lv_obj_t *screen = lv_obj_create( NULL, NULL );

    lv_obj_t * btn = lv_btn_create( screen, NULL );
    lv_obj_set_pos( btn, 10, 10 );
    lv_obj_set_size( btn, 120, 50 );

    lv_obj_t * label = lv_label_create( btn, NULL );
    lv_label_set_text( label, name );

    return screen;
}

Screenshot and/or video

If possible, add screenshots and/or videos about the current issue.

It works well for me in the simulator. :frowning:

In which file and line does the assert fire? Please copy the log.

Sorry, I meant to add that in my original post!

LVGL 7.6.0

Begin Screen A load (21) 0x2400766c -> 0x240077d4
Error: lv_obj_get_local_style (lv_obj.c #2904 lv_obj_get_local_style())
Error: Invalid object (0x240077D4) (lv_debug.c #127 lv_debug_log_error())

It is quite variable. It typically crashes after 10 or 20 iterations, but I have seen it be successful for hundreds before the assert occurs.

Screen Shot 2020-11-18 at 9.48.59 AM

Stack trace

That suggests memory corruption. You could try enabling LV_ASSERT_MEM_INTEGRITY if it’s not already enabled.

I do have the memory integrity check enabled.

I should re-iterate that changing the delay to zero lets it run for thousands of cycles. (I wind up stopping the test, it doesn’t fail.)

That and the variability as to when it occurs makes me think timing, some race-condition?

I can reproduce it on both my target STM32 hardware and a simulator I implemented using Qt and running on my Mac, so very different environments and the same assert.

Are you deleting created screens somehow? Also did you test pointers values?

The screens are deleted automatically by LVGL after they transition.

Screen pointers aren’t tested but they are printed for diagnostics purposes.

Please try these:

  • In lv_disp.c modify scr_anim_ready like this
static void scr_anim_ready(lv_anim_t * a)
{
    LV_ASSERT_MEM_INTEGRITY();
    lv_disp_t * d = lv_obj_get_disp(a->var);
    LV_ASSERT_MEM_INTEGRITY();
    if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr);
    LV_ASSERT_MEM_INTEGRITY();
    d->prev_scr = NULL;
    lv_style_remove_prop(lv_obj_get_local_style(a->var, LV_OBJ_PART_MAIN), LV_STYLE_OPA_SCALE);
    LV_ASSERT_MEM_INTEGRITY();
}
  • Enable LV_USE_DEBUG and LV_USE_ASSERT_MEM_INTEGRITY in lv_conf.h
  • Enable LV_MEM_ADD_JUNK in lv_mem.c

Made those changes, and get the same results.

Begin Screen A load (13) 0x10a634b78 -> 0x10a634e60
Error: lv_obj_get_local_style (lv_obj.c #2927 lv_obj_get_local_style())
Error: Invalid object (0x000000010A634E60) (lv_debug.c #127 lv_debug_log_error())

(LVGL version 7.7.2)

Finally I could reproduce it and now I see the issue.

This simple code can reproduce it.

  scr1 = lv_obj_create(NULL, NULL);
  scr2 = lv_obj_create(NULL, NULL);
  lv_scr_load_anim(scr1, LV_SCR_LOAD_ANIM_OVER_LEFT, 1000, 500, 1);
  lv_scr_load_anim(scr2, LV_SCR_LOAD_ANIM_OVER_LEFT, 1000, 0, 1);

So:

  1. scr1 will start to load with 500 ms delay.
  2. scr2 will start to load immediately.

The problem is the screen “on-delay” is no managed properly. However I don’t know what would be the expected behaviour in this case.

  1. Start to load scr2 and in half-time start to load scr1 too? This way scr1 will be visible in the end.
  2. As the command to load scr2 was sent later, scr2 should be visible in the end. So delete all pending load anim when a new starts?

Please open a new issue for it on GitHub. I’m checking it more frequently.

Meanwhile, I’ve applied a fix that immediately finishes the pending screen animations when starting a new one.