[PATCH] radeon: proper DRI2 pending events handling when client gone. (v3)

Oldřich Jedlička oldium.pro at seznam.cz
Tue Sep 14 13:55:57 PDT 2010


Properly handle asynchronous DRI2 events for disconnected clients.
Track client's pending requests and mark them as invalid when the
client disconnects.

This is based on the version from Alban Browaeys in bug #29065.

v1 (Alban Browaeys): Based upon a detailed explanation from Oldřich
Jedlička and comments from Christopher James Halse Rogers.
on http://lists.x.org/archives/xorg-driver-ati/2010-August/016780.html .

v2: Updated version to apply on master. Removed unnecessary
client_index field from _DRI2FrameEvent. Added freeing/removing from
list to failed paths of radeon_dri2_schedule_wait_msc and
radeon_dri2_schedule_swap.

v3: Adopt to older xorg-server that doesn't have dixRegisterPrivateKey.
---
 src/radeon_dri2.c |  149 +++++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 128 insertions(+), 21 deletions(-)

diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c
index ed7fdd6..054b405 100644
--- a/src/radeon_dri2.c
+++ b/src/radeon_dri2.c
@@ -37,6 +37,7 @@
 #include "radeon.h"
 #include "radeon_dri2.h"
 #include "radeon_version.h"
+#include "list.h"
 
 #ifdef RADEON_DRI2
 
@@ -376,7 +377,6 @@ enum DRI2FrameEventType {
 typedef struct _DRI2FrameEvent {
     XID drawable_id;
     ClientPtr client;
-    int client_index;
     enum DRI2FrameEventType type;
     int frame;
 
@@ -385,8 +385,86 @@ typedef struct _DRI2FrameEvent {
     void *event_data;
     DRI2BufferPtr front;
     DRI2BufferPtr back;
+
+    Bool valid;
+
+    struct list link;
 } DRI2FrameEventRec, *DRI2FrameEventPtr;
 
+typedef struct _DRI2ClientEvents {
+    struct list   reference_list;
+} DRI2ClientEventsRec, *DRI2ClientEventsPtr;
+
+#if HAS_DEVPRIVATEKEYREC
+
+static DevPrivateKeyRec DRI2ClientEventsPrivateKeyRec;
+#define DRI2ClientEventsPrivateKey (&DRI2ClientEventsPrivateKeyRec)
+
+#else
+
+static int DRI2ClientEventsPrivateKeyIndex;
+DevPrivateKey DRI2ClientEventsPrivateKey = &DRI2ClientEventsPrivateKeyIndex;
+
+#endif /* HAS_DEVPRIVATEKEYREC */
+
+#define GetDRI2ClientEvents(pClient)	((DRI2ClientEventsPtr) \
+    dixLookupPrivate(&(pClient)->devPrivates, DRI2ClientEventsPrivateKey))
+
+static int
+ListAddDRI2ClientEvents(ClientPtr client, struct list *entry)
+{
+    DRI2ClientEventsPtr pClientPriv;
+    pClientPriv = GetDRI2ClientEvents(client);
+
+    if (!pClientPriv) {
+        return BadAlloc;
+    }
+
+    list_add(entry, &pClientPriv->reference_list);
+    return 0;
+}
+
+static void
+ListDelDRI2ClientEvents(ClientPtr client, struct list *entry)
+{
+    DRI2ClientEventsPtr pClientPriv;
+    pClientPriv = GetDRI2ClientEvents(client);
+
+    if (!pClientPriv) {
+        return;
+    }
+    list_del(entry);
+}
+
+static void
+radeon_dri2_client_state_changed(CallbackListPtr *ClientStateCallback, pointer data, pointer calldata)
+{
+    DRI2ClientEventsPtr pClientEventsPriv;
+    DRI2FrameEventPtr ref;
+    NewClientInfoRec *clientinfo = calldata;
+    ClientPtr pClient = clientinfo->client;
+    pClientEventsPriv = GetDRI2ClientEvents(pClient);
+
+    switch (pClient->clientState) {
+    case ClientStateInitial:
+        list_init(&pClientEventsPriv->reference_list);
+        break;
+    case ClientStateRunning:
+        break;
+
+    case ClientStateRetained:
+    case ClientStateGone:
+        if (pClientEventsPriv) {
+            list_for_each_entry(ref, &pClientEventsPriv->reference_list, link) {
+                ref->valid = FALSE;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+}
+
 static void
 radeon_dri2_ref_buffer(BufferPtr buffer)
 {
@@ -416,28 +494,20 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
     BoxRec box;
     RegionRec region;
 
+    if (!event->valid) {
+        goto cleanup;
+    }
+
     status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
                                M_ANY, DixWriteAccess);
     if (status != Success) {
-        radeon_dri2_unref_buffer(event->front);
-        radeon_dri2_unref_buffer(event->back);
-        free(event);
+        goto cleanup;
         return;
     }
 
     screen = drawable->pScreen;
     scrn = xf86Screens[screen->myNum];
-
-    /* event->client may have quit between submitting a request
-     * and this callback being triggered.
-     *
-     * Check our saved client pointer against the client in the saved client
-     * slot.  This will catch almost all cases where the client that requested
-     * SwapBuffers has gone away, and will guarantee that there is at least a 
-     * valid client to write the BufferSwapComplete event to.
-     */
-    client = event->client == clients[event->client_index] ? 
-            event->client : NULL;
+    client = event->client;
 
     switch (event->type) {
     case DRI2_FLIP:
@@ -453,8 +523,6 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
         DRI2SwapComplete(client, drawable, frame, tv_sec, tv_usec,
                 swap_type, event->event_complete, event->event_data);
 
-        radeon_dri2_unref_buffer(event->front);
-        radeon_dri2_unref_buffer(event->back);
         break;
     case DRI2_WAITMSC:
         DRI2WaitMSCComplete(client, drawable, frame, tv_sec, tv_usec);
@@ -466,6 +534,11 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec,
         break;
     }
 
+cleanup:
+    radeon_dri2_unref_buffer(event->front);
+    radeon_dri2_unref_buffer(event->back);
+    if (event->valid)
+        ListDelDRI2ClientEvents(event->client, &event->link);
     free(event);
 }
 
@@ -539,7 +612,7 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
     ScreenPtr screen = draw->pScreen;
     ScrnInfoPtr scrn = xf86Screens[screen->myNum];
     RADEONInfoPtr info = RADEONPTR(scrn);
-    DRI2FrameEventPtr wait_info;
+    DRI2FrameEventPtr wait_info = NULL;
     drmVBlank vbl;
     int ret, crtc = radeon_dri2_drawable_crtc(draw);
     CARD64 current_msc;
@@ -560,8 +633,14 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
 
     wait_info->drawable_id = draw->id;
     wait_info->client = client;
-    wait_info->client_index = client->index;
     wait_info->type = DRI2_WAITMSC;
+    wait_info->valid = TRUE;
+
+    if (ListAddDRI2ClientEvents(client, &wait_info->link)) {
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                "add events to client private failed.\n");
+        goto out_complete;
+    }
 
     /* Get current count */
     vbl.request.type = DRM_VBLANK_RELATIVE;
@@ -642,6 +721,10 @@ static int radeon_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw,
     return TRUE;
 
 out_complete:
+    if (wait_info) {
+        ListDelDRI2ClientEvents(wait_info->client, &wait_info->link);
+        free(wait_info);
+    }
     DRI2WaitMSCComplete(client, draw, target_msc, 0, 0);
     return TRUE;
 }
@@ -704,11 +787,16 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
 
     swap_info->drawable_id = draw->id;
     swap_info->client = client;
-    swap_info->client_index = client->index;
     swap_info->event_complete = func;
     swap_info->event_data = data;
     swap_info->front = front;
     swap_info->back = back;
+    swap_info->valid = TRUE;
+    if (ListAddDRI2ClientEvents(client, &swap_info->link)) {
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                "add events to client private failed.\n");
+        goto blit_fallback;
+    }
 
     /* Get current count */
     vbl.request.type = DRM_VBLANK_RELATIVE;
@@ -831,8 +919,10 @@ blit_fallback:
     radeon_dri2_copy_region(draw, &region, front, back);
 
     DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, data);
-    if (swap_info)
+    if (swap_info) {
+        ListDelDRI2ClientEvents(swap_info->client, &swap_info->link);
         free(swap_info);
+    }
 
     radeon_dri2_unref_buffer(front);
     radeon_dri2_unref_buffer(back);
@@ -895,6 +985,20 @@ radeon_dri2_screen_init(ScreenPtr pScreen)
     } else {
         xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "You need a newer kernel for sync extension\n");
     }
+
+#if HAS_DIXREGISTERPRIVATEKEY
+    if (!dixRegisterPrivateKey(DRI2ClientEventsPrivateKey, PRIVATE_CLIENT, sizeof(DRI2ClientEventsRec))) {
+        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DRI2 registering private key to client failed\n");
+        return FALSE;
+    }
+#else
+    if (!dixRequestPrivate(DRI2ClientEventsPrivateKey, sizeof(DRI2ClientEventsRec))) {
+        xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "DRI2 requesting private key to client failed\n");
+        return FALSE;
+    }
+#endif
+
+    AddCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
 #endif
 
     info->dri2.enabled = DRI2ScreenInit(pScreen, &dri2_info);
@@ -906,6 +1010,9 @@ void radeon_dri2_close_screen(ScreenPtr pScreen)
     ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
     RADEONInfoPtr info = RADEONPTR(pScrn);
 
+#if DRI2INFOREC_VERSION >= 4
+    DeleteCallback(&ClientStateCallback, radeon_dri2_client_state_changed, 0);
+#endif
     DRI2CloseScreen(pScreen);
     drmFree(info->dri2.device_name);
 }
-- 
1.7.2.2



More information about the xorg-driver-ati mailing list