From 65c4e27ca4106078e4cd9010a126ef6d0ffbeb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Mon, 6 Sep 2010 21:14:52 +0200 Subject: [PATCH] radeon: proper DRI2 pending events handling when client gone. (v4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. v4: Conditional include of list.h, unreachable return removed. --- src/radeon_dri2.c | 155 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 131 insertions(+), 24 deletions(-) diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c index ed7fdd6..742bd42 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -38,6 +38,10 @@ #include "radeon_dri2.h" #include "radeon_version.h" +#if XORG_VERSION_CURRENT >= XORG_VERSION_NUMERIC(1,7,99,902, 0) +#include "list.h" +#endif + #ifdef RADEON_DRI2 #include "radeon_bo_gem.h" @@ -376,7 +380,6 @@ enum DRI2FrameEventType { typedef struct _DRI2FrameEvent { XID drawable_id; ClientPtr client; - int client_index; enum DRI2FrameEventType type; int frame; @@ -385,8 +388,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 +497,17 @@ 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); - return; - } + if (status != Success) + goto cleanup; 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, ®ion, 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.3