e_hwc_windows: Fix deadlock when e_hwc_window is freed 75/259875/4 accepted/tizen/unified/20210617.124858 submit/tizen/20210616.110744
authorChangyeon Lee <cyeon.lee@samsung.com>
Wed, 16 Jun 2021 06:03:23 +0000 (15:03 +0900)
committerChangyeon Lee <cyeon.lee@samsung.com>
Wed, 16 Jun 2021 10:10:16 +0000 (19:10 +0900)
after tbm_surface of E_Hwc_Window_Update is referenced,
it is possible that unreferenced api of tbm_surface
(tbm_surface_internal_unref) is called when e_hwc_window
is freed.

e_hwc_windows add rendererd_windows list to user_data of
tbm_surface of target_window
rendered_windows list has reference of e_hwc_window and
unreference of e_hwc_window can be called when rendered_windows
list is freed in user_data delete callback of tbm_surface.

but it cause deadlock problem when tbm_surface_internal_unref
is called in callback of tbm_surface
because libtbm doesn't allow that tbm_surface api is called
in callback of tbm_surface.

so e_hwc_windows doesn't use user_data of tbm_surface for
rendered_buffer and rendered_windows list.
they are added to member of E_Hwc_Window_Comp_Info.
E_Hwc_Window_Comp_Info isn't freed in user_data deleted
callback.

Change-Id: I40deba667bbfb7bbfba2807830e823faa90c07b4

src/bin/e_hwc_windows.c

index 119ac2d..380557a 100644 (file)
@@ -64,15 +64,15 @@ struct _E_Hwc_Windows_Comp_Info
    Eina_Bool skip;
    E_Egl_Sync *release_fence;
    Eina_List *pending_update_list;
+
+   Eina_List *rendered_windows;
+   Eina_List *rendered_buffers;
 };
 
 static Eina_Bool ehws_trace = EINA_FALSE;
 static Eina_Bool ehws_dump_enable = EINA_FALSE;
-static uint64_t ehws_rendered_windows_key;
-#define EHWS_RENDERED_WINDOWS_KEY  (unsigned long)(&ehws_rendered_windows_key)
 
-static uint64_t ehws_rendered_buffers_key;
-#define EHWS_RENDERED_BUFFERS_KEY  (unsigned long)(&ehws_rendered_buffers_key)
+static int E_EVENT_HWC_WINDOWS_COMP_INFO_FREE = -1;
 
 static uint64_t ehws_comp_buffer_info_key;
 #define EHWS_BUFFER_COMP_INFO_KEY  (unsigned long)(&ehws_comp_buffer_info_key)
@@ -194,18 +194,86 @@ _e_hwc_windows_aligned_width_get(tbm_surface_h tsurface)
 }
 
 static void
-_e_hwc_windows_comp_info_free(void *data)
+_e_hwc_windows_comp_info_rendered_buffers_set(E_Hwc_Windows_Comp_Info *comp_info,
+                                              Eina_List *rendered_buffers)
+{
+   E_Comp_Wl_Buffer_Ref *buffer_ref;
+
+   EINA_SAFETY_ON_NULL_RETURN(comp_info);
+
+   if (comp_info->rendered_buffers == rendered_buffers)
+     return;
+
+   if (comp_info->rendered_buffers)
+     {
+        if (eina_list_count(comp_info->rendered_buffers))
+          {
+             EINA_LIST_FREE(comp_info->rendered_buffers, buffer_ref)
+               {
+                  e_comp_wl_buffer_reference(buffer_ref, NULL);
+                  E_FREE(buffer_ref);
+               }
+          }
+     }
+
+   comp_info->rendered_buffers = rendered_buffers;
+}
+
+static void
+_e_hwc_windows_comp_info_rendered_windows_set(E_Hwc_Windows_Comp_Info *comp_info,
+                                              Eina_List *rendered_windows)
+{
+   E_Hwc_Window *hwc_window = NULL;
+
+   EINA_SAFETY_ON_NULL_RETURN(comp_info);
+
+   if (comp_info->rendered_windows == rendered_windows)
+     return;
+
+   if (comp_info->rendered_windows)
+     {
+        if (eina_list_count(comp_info->rendered_windows))
+          {
+             EINA_LIST_FREE(comp_info->rendered_windows, hwc_window)
+               e_hwc_window_unref(hwc_window);
+          }
+     }
+
+   comp_info->rendered_windows = rendered_windows;
+}
+
+static void
+_e_hwc_windows_comp_info_free(void *data EINA_UNUSED, void *event)
 {
    E_Hwc_Windows_Comp_Info *comp_info;
+   E_Hwc_Window_Update_Data *update;
 
-   comp_info = (E_Hwc_Windows_Comp_Info *)data;
+   comp_info = (E_Hwc_Windows_Comp_Info *)event;
    if (!comp_info) return;
 
+   EINA_LIST_FREE(comp_info->pending_update_list, update)
+     e_hwc_window_pending_update_data_dequeue_cancel(update->hwc_window, update);
+
+   _e_hwc_windows_comp_info_rendered_windows_set(comp_info, NULL);
+   _e_hwc_windows_comp_info_rendered_buffers_set(comp_info, NULL);
+
    e_presentation_time_container_finish(&comp_info->presentation_container);
 
    E_FREE(comp_info);
 }
 
+static void
+_e_hwc_windows_comp_info_cb_tbm_surface_destroy(void *data)
+{
+   E_Hwc_Windows_Comp_Info *comp_info;
+
+   comp_info = (E_Hwc_Windows_Comp_Info *)data;
+   if (!comp_info) return;
+
+   ecore_event_add(E_EVENT_HWC_WINDOWS_COMP_INFO_FREE, comp_info,
+                   _e_hwc_windows_comp_info_free, NULL);
+}
+
 static E_Hwc_Windows_Comp_Info *
 _e_hwc_windows_comp_info_get(tbm_surface_h tbm_surface)
 {
@@ -222,7 +290,7 @@ _e_hwc_windows_comp_info_get(tbm_surface_h tbm_surface)
    e_presentation_time_container_init(&comp_info->presentation_container);
 
    tbm_surface_internal_add_user_data(tbm_surface, EHWS_BUFFER_COMP_INFO_KEY,
-                                     _e_hwc_windows_comp_info_free);
+                                      _e_hwc_windows_comp_info_cb_tbm_surface_destroy);
 
    tbm_surface_internal_set_user_data(tbm_surface, EHWS_BUFFER_COMP_INFO_KEY,
                                       comp_info);
@@ -469,14 +537,14 @@ _e_hwc_windows_offscreen_commit(E_Hwc *hwc)
 static Eina_List *
 _e_hwc_windows_target_window_rendered_windows_get(tbm_surface_h target_tsurface)
 {
-   Eina_List *rendered_windows = NULL;
+   E_Hwc_Windows_Comp_Info *comp_info;
 
    if (!target_tsurface) return NULL;
 
-   tbm_surface_internal_get_user_data(target_tsurface, EHWS_RENDERED_WINDOWS_KEY,
-                            (void**)&rendered_windows);
+   comp_info = _e_hwc_windows_comp_info_get(target_tsurface);
+   if (!comp_info) return NULL;
 
-   return rendered_windows;
+   return comp_info->rendered_windows;
 }
 
 static void
@@ -1028,37 +1096,6 @@ _e_hwc_windows_visible_windows_list_get(E_Hwc *hwc)
    return windows_list;
 }
 
-static void
-_e_hwc_windows_rendered_buffers_free(void *data)
-{
-   Eina_List *rendered_buffers = (Eina_List *)data;
-   E_Comp_Wl_Buffer_Ref *buffer_ref;
-
-   if (!rendered_buffers) return;
-
-   if (eina_list_count(rendered_buffers))
-     {
-        EINA_LIST_FREE(rendered_buffers, buffer_ref)
-          {
-             e_comp_wl_buffer_reference(buffer_ref, NULL);
-             E_FREE(buffer_ref);
-          }
-     }
-}
-
-static void
-_e_hwc_windows_rendered_windows_free(void *data)
-{
-   Eina_List *rendered_windows = (Eina_List *)data;
-   E_Hwc_Window *hwc_window = NULL;
-
-  if (eina_list_count(rendered_windows))
-    {
-        EINA_LIST_FREE(rendered_windows, hwc_window)
-          e_hwc_window_unref(hwc_window);
-    }
-}
-
 static Eina_Bool
 _e_hwc_windows_pending_update_data_enqueue(E_Hwc_Windows_Comp_Info *comp_info)
 {
@@ -1152,13 +1189,6 @@ _e_hwc_windows_target_window_surface_queue_trace_cb(tbm_surface_queue_h surface_
 
         EHWSTRACE("{%s} dequeue ts:%p", NULL, target_hwc_window->hwc, "@TARGET WINDOW@", tsurface);
 
-        tbm_surface_internal_add_user_data(tsurface,
-                                           EHWS_RENDERED_BUFFERS_KEY,
-                                           _e_hwc_windows_rendered_buffers_free);
-
-        tbm_surface_internal_add_user_data(tsurface,
-                                           EHWS_RENDERED_WINDOWS_KEY,
-                                           _e_hwc_windows_rendered_windows_free);
         target_hwc_window->dequeued_tsurface = tsurface;
 
         if (!eina_list_data_find(target_hwc_window->rendering_tsurfaces, tsurface))
@@ -1177,14 +1207,15 @@ _e_hwc_windows_target_window_surface_queue_trace_cb(tbm_surface_queue_h surface_
 
    if (trace == TBM_SURFACE_QUEUE_TRACE_ACQUIRE)
      {
-        tbm_surface_internal_set_user_data(tsurface, EHWS_RENDERED_BUFFERS_KEY, NULL);
-
         target_hwc_window->rendering_tsurfaces =
           eina_list_remove(target_hwc_window->rendering_tsurfaces, tsurface);
 
         comp_info = _e_hwc_windows_comp_info_get(tsurface);
         if (comp_info)
-          _e_hwc_windows_pending_update_data_enqueue(comp_info);
+          {
+             _e_hwc_windows_pending_update_data_enqueue(comp_info);
+             _e_hwc_windows_comp_info_rendered_buffers_set(comp_info, NULL);
+          }
      }
 
    /* tsurface has been released at the queue */
@@ -1192,16 +1223,16 @@ _e_hwc_windows_target_window_surface_queue_trace_cb(tbm_surface_queue_h surface_
      {
         EHWSTRACE("{%s} release ts:%p", NULL, target_hwc_window->hwc, "@TARGET WINDOW@", tsurface);
 
-        tbm_surface_internal_delete_user_data(tsurface, EHWS_RENDERED_BUFFERS_KEY);
-
-        tbm_surface_internal_delete_user_data(tsurface, EHWS_RENDERED_WINDOWS_KEY);
-
         target_hwc_window->rendering_tsurfaces =
           eina_list_remove(target_hwc_window->rendering_tsurfaces, tsurface);
 
         comp_info = _e_hwc_windows_comp_info_get(tsurface);
         if (comp_info)
-          comp_info->skip = EINA_FALSE;
+          {
+             comp_info->skip = EINA_FALSE;
+             _e_hwc_windows_comp_info_rendered_buffers_set(comp_info, NULL);
+             _e_hwc_windows_comp_info_rendered_windows_set(comp_info, NULL);
+          }
      }
 }
 
@@ -1229,6 +1260,7 @@ _e_hwc_windows_target_window_render_finished_cb(void *data, Ecore_Fd_Handler *fd
    E_Hwc_Window *hwc_window = NULL;
    Eina_List *acquirable_buffers = NULL;
    E_Hwc_Window_Queue_Buffer *queue_buffer = NULL;
+   E_Hwc_Windows_Comp_Info *comp_info;
 
    fd = ecore_main_fd_handler_fd_get(fd_handler);
    if (fd < 0) return ECORE_CALLBACK_RENEW;
@@ -1248,9 +1280,10 @@ _e_hwc_windows_target_window_render_finished_cb(void *data, Ecore_Fd_Handler *fd
      {
         if (!queue_buffer->tsurface) continue;
 
-        tbm_surface_internal_set_user_data(queue_buffer->tsurface,
-                                           EHWS_RENDERED_BUFFERS_KEY,
-                                           NULL);
+        comp_info = _e_hwc_windows_comp_info_get(queue_buffer->tsurface);
+        if (!comp_info) continue;
+
+        _e_hwc_windows_comp_info_rendered_buffers_set(comp_info, NULL);
      }
 
    return ECORE_CALLBACK_RENEW;
@@ -1431,6 +1464,15 @@ _e_hwc_windows_target_window_render_flush_post_cb(void *data, Evas *e EINA_UNUSE
         return;
      }
 
+   comp_info = _e_hwc_windows_comp_info_get(target_hwc_window->dequeued_tsurface);
+   if (!comp_info)
+     {
+        EHWSERR("fail to get comp_info tsurface:%p", target_hwc_window->hwc, target_hwc_window->dequeued_tsurface);
+        return;
+     }
+
+   _e_hwc_windows_pending_update_data_dequeue(target_hwc_window, comp_info);
+
    visible_windows = _e_hwc_windows_visible_windows_list_get(target_hwc_window->hwc);
    EINA_LIST_FREE(visible_windows, hwc_window)
      {
@@ -1440,12 +1482,6 @@ _e_hwc_windows_target_window_render_flush_post_cb(void *data, Evas *e EINA_UNUSE
           e_hwc_windows_rendered_window_add(hwc_window);
      }
 
-   comp_info = _e_hwc_windows_comp_info_get(target_hwc_window->dequeued_tsurface);
-   if (comp_info)
-     _e_hwc_windows_pending_update_data_dequeue(target_hwc_window, comp_info);
-   else
-     EHWSERR("fail to get comp_info tsurface:%p", target_hwc_window->hwc, target_hwc_window->dequeued_tsurface);
-
    fence_enabled = e_hwc_windows_fence_enabled_get(target_hwc_window->hwc);
 
    /* all ecs have been composited so we can attach a list of composited e_hwc_windows to the surface
@@ -1463,7 +1499,7 @@ _e_hwc_windows_target_window_render_flush_post_cb(void *data, Evas *e EINA_UNUSE
         if (!ec) continue;
 
         cdata = e_client_cdata_get(ec);
-        if (comp_info && cdata)
+        if (cdata)
           e_presentation_time_container_feedback_merge(&comp_info->presentation_container,
                                                        &cdata->presentation_container);
 
@@ -1502,13 +1538,8 @@ _e_hwc_windows_target_window_render_flush_post_cb(void *data, Evas *e EINA_UNUSE
         rendered_buffers = eina_list_append(rendered_buffers, buffer_ref);
      }
 
-   tbm_surface_internal_set_user_data(target_hwc_window->dequeued_tsurface,
-                                      EHWS_RENDERED_BUFFERS_KEY,
-                                      rendered_buffers);
-
-   tbm_surface_internal_set_user_data(target_hwc_window->dequeued_tsurface,
-                                      EHWS_RENDERED_WINDOWS_KEY,
-                                      rendered_windows);
+   _e_hwc_windows_comp_info_rendered_buffers_set(comp_info, rendered_buffers);
+   _e_hwc_windows_comp_info_rendered_windows_set(comp_info, rendered_windows);
 
    eina_list_free(target_hwc_window->rendered_windows);
    target_hwc_window->rendered_windows = NULL;