LVGL v9.4 lv_draw_dispatch_wait_for_request() hangs after migrating from FreeRTOS V1 to V2

Description / Background

  • LVGL version: v9.4 (downloaded from lvgl/lvgl on GitHub)
  • Environment: STM32 + display driver, using CMSIS-RTOS2 (FreeRTOS V2 wrapper)
  • Previous setup: FreeRTOS V1 — everything worked fine, screens rendered correctly.
  • Change: Migrated to FreeRTOS V2 — lv_tick appears to increment normally, but rendering never completes and the system hangs.

Observed Behavior & Debug Clues

  1. Stuck inside lv_draw_dispatch_wait_for_request() (inside lv_thread_sync_wait()), indicating the draw task never signals completion.
  2. Stack trace in FreeRTOS V1 (working path) shows draw_buf_flush() → refr_invalid_areas() → etc. — there, the buffer is updated correctly.
  3. In FreeRTOS V2, the same call stack executes, but the buffer remains unchanged, and xSyncSignal remains 0 — causing ulTaskNotifyTake(pdTRUE, portMAX_DELAY) to block indefinitely.

Comparison: FreeRTOS V1 (Working) vs. V2 (Hangs)

Step / Function FreeRTOS V1 (Working) FreeRTOS V2 (Hangs)
lvglRun()lv_timer_handler() Executes regularly Executes, but stall downstream
refr_invalid_areas()draw_buf_flush() Buffer (buf_t) filled with new pixel data Buffer remains zero / stale
Render completion signal (lv_disp_flush_ready) Signal fires → tasks resume Signal not fired → xSyncSignal = 0
lv_thread_sync_wait() (inside dispatcher) xSyncSignal becomes 1 — proceeds xSyncSignal stays 0 — hang in ulTaskNotifyTake()
Display update Visible on screen Frozen/blank screen

Relevant Code Snippet — `lv_thread_sync_wait()
lv_result_t lv_thread_sync_wait(lv_thread_sync_t * pxCond)
{
lv_result_t lvRes = LV_RESULT_OK;
prvCheckCondInit(pxCond);

LV_LOG_WARN("WAIT ENTER: cond=%p, before_crit signal=%d, xTaskToNotify=%p",
            pxCond, (int)pxCond->xSyncSignal, (void*)pxCond->xTaskToNotify);

#if LV_USE_FREERTOS_TASK_NOTIFY
TaskHandle_t xCurrentTaskHandle = xTaskGetCurrentTaskHandle();

_enter_critical();
BaseType_t xSyncSygnal = pxCond->xSyncSignal;
pxCond->xSyncSignal = pdFALSE;
if(xSyncSygnal == pdFALSE) {
    pxCond->xTaskToNotify = xCurrentTaskHandle;
}
_exit_critical();

LV_LOG_WARN("WAIT AFTER CRIT: cond=%p, xSyncSignal=%d, xTaskToNotify=%p",
            pxCond, (int)xSyncSygnal, (void*)pxCond->xTaskToNotify);

if(xSyncSygnal == pdFALSE) {
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
    LV_LOG_WARN("WAIT RESUMED: cond=%p, task=%p (notify returned)",
                pxCond, (void*)xCurrentTaskHandle);
}

#endif

return lvRes;

}

  • In V1 xSyncSignal becomes 1 after flush finish, and the task proceeds.
  • In V2 xSyncSignal always remains 0, so the wait never returns.

What I’ve Tried & Observed

  1. Confirmed that lv_tick works in both V1 and V2.
  2. Attempted .thread_attr.stack_size = 4096, but allocation failed.
  3. No rendering occurs — buffer remains unchanged.

Request to LVGL / RTOS Experts

  1. Is this a known issue when using CMSIS-RTOS2/FreeRTOS V2 integration?
  2. Does LVGL v9.4 have documented minimum stack or heap requirements for the draw thread?
  3. Could there be improvements in LVGL’s error handling when render thread creation fails?
  4. Would anyone share a CMSIS-RTOS2-compatible lv_thread_sync implementation that gracefully handles thread creation failures or uses static allocation?
1 Like

Is this tpic still open?
I have similer problems.

Subject: Re: LVGL v9.4 lv_draw_dispatch_wait_for_request hangs

I am experiencing exactly the same issue on a custom board using STM32U599 with NeoChrom GPU (GPU2D) and FreeRTOS. The system consistently hangs at lv_draw_dispatch_wait_for_request (specifically inside ulTaskNotifyTake) during high-load rendering, such as opacity layers in the benchmark.

Through extensive debugging with RTT logs and register monitoring, I have found the following:

  1. Hardware is fine: The GPU2D_IRQHandler triggers correctly, and the GPU finishes its task.
  2. Notification is lost: The deadlock happens because the swdraw task misses the notification. Even manually enabling USE_HAL_GPU2D_REGISTER_CALLBACKS 1U did not resolve the issue in my custom environment, suggesting a fragile synchronization link between the HAL interrupt and LVGL’s OS abstraction layer.
  3. The “Lost Notification” Race Condition: I observed cases where the interrupt fires just before the task officially enters the “Wait” state, or the callback chain fails to bridge the signal.

Temporary Workaround: I modified lv_os_freertos.c to replace portMAX_DELAY with a 100ms timeout:

`C/* lv_os_freertos.c */
if(ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100)) == 0) {
    // Safety wake-up to prevent permanent deadlock
    LV_LOG_WARN("Thread sync timeout - forced wake up");
}`

With this change, the benchmark completes successfully without any issues. This proves that the core problem is a permanent deadlock caused by a single missed notification.

I believe lv_os_freertos.c (or the underlying sync logic) needs a more robust way to handle missed notifications or a mandatory safety timeout to prevent the entire UI thread from hanging.