From f28c6d76e1cdc5ba649ed722dac3d44accbc206b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 25 Jun 2014 22:19:23 +0100 Subject: [PATCH] sna/dri2: Hook into ClientGone callback to clear dangling references As the Window may exist for multiple Clients, we cannot rely on the destruction of the Window decoupling all outstanding events and preventing chasing a stale Client pointer. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=80157 Signed-off-by: Chris Wilson --- src/sna/sna.h | 2 + src/sna/sna_dri2.c | 140 +++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 101 insertions(+), 41 deletions(-) diff --git a/src/sna/sna.h b/src/sna/sna.h index 8640f2f..7b89f8c 100644 --- a/src/sna/sna.h +++ b/src/sna/sna.h @@ -114,6 +114,7 @@ struct sna_cursor; struct sna_crtc; struct sna_client { + struct list events; int is_compositor; /* only 4 bits used */ }; @@ -334,6 +335,7 @@ struct sna { #if HAVE_DRI2 void *flip_pending; + unsigned client_count; #endif } dri2; diff --git a/src/sna/sna_dri2.c b/src/sna/sna_dri2.c index 9f8b8ee..3591036 100644 --- a/src/sna/sna_dri2.c +++ b/src/sna/sna_dri2.c @@ -123,6 +123,7 @@ struct sna_dri2_event { struct sna_dri2_event *chain; struct list cache; + struct list link; int mode; }; @@ -1218,8 +1219,93 @@ sna_dri2_remove_event(WindowPtr win, struct sna_dri2_event *info) chain->chain = info->chain; } +static void +sna_dri2_event_free(struct sna *sna, + DrawablePtr draw, + struct sna_dri2_event *info) +{ + DBG(("%s\n", __FUNCTION__)); + + if (draw && draw->type == DRAWABLE_WINDOW) + sna_dri2_remove_event((WindowPtr)draw, info); + _sna_dri2_destroy_buffer(sna, info->front); + _sna_dri2_destroy_buffer(sna, info->back); + + while (!list_is_empty(&info->cache)) { + struct dri_bo *c; + + c = list_first_entry(&info->cache, struct dri_bo, link); + list_del(&c->link); + + DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); + if (c->bo) + kgem_bo_destroy(&sna->kgem, c->bo); + + free(c); + } + + if (info->bo) { + DBG(("%s: releasing batch handle=%d\n", __FUNCTION__, info->bo->handle)); + kgem_bo_destroy(&sna->kgem, info->bo); + } + + _list_del(&info->link); + free(info); +} + +static void +sna_dri2_client_gone(CallbackListPtr *list, void *closure, void *data) +{ + NewClientInfoRec *clientinfo = data; + ClientPtr client = clientinfo->client; + struct sna_client *priv = sna_client(client); + struct sna *sna = closure; + + if (priv->events.next == NULL) + return; + + if (client->clientState != ClientStateGone) + return; + + DBG(("%s()\n", __FUNCTION__)); + + while (!list_is_empty(&priv->events)) { + struct sna_dri2_event *event; + + event = list_first_entry(&priv->events, struct sna_dri2_event, link); + assert(event->client == client); + + if (event->queued) { + event->client = NULL; + event->draw = NULL; + list_del(&event->link); + } else + sna_dri2_event_free(sna, event->draw, event); + } + + if (--sna->dri2.client_count == 0) + DeleteCallback(&ClientStateCallback, sna_dri2_client_gone, sna); +} + +static bool add_event_to_client(struct sna_dri2_event *info, struct sna *sna, ClientPtr client) +{ + struct sna_client *priv = sna_client(client); + + if (priv->events.next == NULL) { + if (sna->dri2.client_count++ == 0 && + !AddCallback(&ClientStateCallback, sna_dri2_client_gone, sna)) + return false; + + list_init(&priv->events); + } + + list_add(&info->link, &priv->events); + info->client = client; + return true; +} + static struct sna_dri2_event * -sna_dri2_add_event(DrawablePtr draw, ClientPtr client) +sna_dri2_add_event(struct sna *sna, DrawablePtr draw, ClientPtr client) { struct dri2_window *priv; struct sna_dri2_event *info, *chain; @@ -1238,10 +1324,14 @@ sna_dri2_add_event(DrawablePtr draw, ClientPtr client) list_init(&info->cache); info->draw = draw; - info->client = client; info->crtc = priv->crtc; info->pipe = sna_crtc_to_pipe(priv->crtc); + if (!add_event_to_client(info, sna, client)) { + free(info); + return NULL; + } + assert(priv->chain != info); if (priv->chain == NULL) { @@ -1258,39 +1348,6 @@ sna_dri2_add_event(DrawablePtr draw, ClientPtr client) return info; } -static void -sna_dri2_event_free(struct sna *sna, - DrawablePtr draw, - struct sna_dri2_event *info) -{ - DBG(("%s\n", __FUNCTION__)); - - if (draw && draw->type == DRAWABLE_WINDOW) - sna_dri2_remove_event((WindowPtr)draw, info); - _sna_dri2_destroy_buffer(sna, info->front); - _sna_dri2_destroy_buffer(sna, info->back); - - while (!list_is_empty(&info->cache)) { - struct dri_bo *c; - - c = list_first_entry(&info->cache, struct dri_bo, link); - list_del(&c->link); - - DBG(("%s: releasing cached handle=%d\n", __FUNCTION__, c->bo ? c->bo->handle : 0)); - if (c->bo) - kgem_bo_destroy(&sna->kgem, c->bo); - - free(c); - } - - if (info->bo) { - DBG(("%s: releasing batch handle=%d\n", __FUNCTION__, info->bo->handle)); - kgem_bo_destroy(&sna->kgem, info->bo); - } - - free(info); -} - void sna_dri2_destroy_window(WindowPtr win) { struct sna *sna; @@ -2022,6 +2079,7 @@ void sna_dri2_vblank_handler(struct sna *sna, struct drm_event_vblank *event) break; case WAITMSC: + assert(info->client); DRI2WaitMSCComplete(info->client, draw, msc, event->tv_sec, event->tv_usec); break; @@ -2377,7 +2435,7 @@ sna_dri2_schedule_flip(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, } } - info = sna_dri2_add_event(draw, client); + info = sna_dri2_add_event(sna, draw, client); if (info == NULL) return false; @@ -2423,7 +2481,7 @@ out: return true; } - info = sna_dri2_add_event(draw, client); + info = sna_dri2_add_event(sna, draw, client); if (info == NULL) return false; @@ -2507,7 +2565,7 @@ sna_dri2_schedule_xchg(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc, if (sync) { struct sna_dri2_event *info; - info = sna_dri2_add_event(draw, client); + info = sna_dri2_add_event(sna, draw, client); if (!info) goto complete; @@ -2568,7 +2626,7 @@ sna_dri2_schedule_xchg_crtc(ClientPtr client, DrawablePtr draw, xf86CrtcPtr crtc if (sync) { struct sna_dri2_event *info; - info = sna_dri2_add_event(draw, client); + info = sna_dri2_add_event(sna, draw, client); if (!info) goto complete; @@ -2725,7 +2783,7 @@ sna_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, VG_CLEAR(vbl); - info = sna_dri2_add_event(draw, client); + info = sna_dri2_add_event(sna, draw, client); if (!info) goto blit; @@ -2893,7 +2951,7 @@ sna_dri2_schedule_wait_msc(ClientPtr client, DrawablePtr draw, CARD64 target_msc if (divisor == 0 && current_msc >= target_msc) goto out_complete; - info = sna_dri2_add_event(draw, client); + info = sna_dri2_add_event(sna, draw, client); if (!info) goto out_complete; -- 1.9.1