glx: Fix memory leak in context garbage collection (v2)

I broke this, back in:

    commit a48dadc98a
    Author: Adam Jackson <ajax@redhat.com>
    Date:   Mon Mar 21 11:59:29 2011 -0400

	glx: Reimplement context tags

In that, I changed the glx client state to not explicitly track the list
of current contexts for the client (since that was what we were deriving
tags from).  The bug was that I removed the code for same from
glxClientCallback without noticing that it had the side effect of
effectively de-currenting those contexts, so that ContextGone could free
them.  So, if you had a client exit with a context still current, the
context's memory would leak.  Not a huge deal for direct clients, but
viciously bad for indirect, since the swrast context state at the bottom
of Mesa is like 15M.

Fix this by promoting Bool isCurrent to ClientPtr currentClient, so that
we have a back-pointer to chase when walking the list of contexts when
ClientStateGone happens.

v2: Explicitly call __glXFreeContext on the ClientStateGone path.  Our
current context might be one we got from EXT_import_context and whose
creating client has since died.  Without the explicit call, the creating
client's FreeClientResources would not free the context because it's
still current, and the using client's FreeClientResources would not free
the context because it's not an XID it created.  This matches the logic
from a48dadc.

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Signed-off-by: Adam Jackson <ajax@redhat.com>
This commit is contained in:
Adam Jackson 2013-08-03 09:47:55 -04:00
parent 75b362763c
commit 276d8057aa
4 changed files with 35 additions and 24 deletions

View File

@ -320,7 +320,7 @@ __glXDisp_CreateContextAttribsARB(__GLXclientState * cl, GLbyte * pc)
ctx->id = req->context; ctx->id = req->context;
ctx->share_id = req->shareList; ctx->share_id = req->shareList;
ctx->idExists = True; ctx->idExists = True;
ctx->isCurrent = False; ctx->currentClient = False;
ctx->isDirect = req->isDirect; ctx->isDirect = req->isDirect;
ctx->hasUnflushedCommands = False; ctx->hasUnflushedCommands = False;
ctx->renderMode = GL_RENDER; ctx->renderMode = GL_RENDER;

View File

@ -299,7 +299,7 @@ DoCreateContext(__GLXclientState * cl, GLXContextID gcId,
glxc->id = gcId; glxc->id = gcId;
glxc->share_id = shareList; glxc->share_id = shareList;
glxc->idExists = GL_TRUE; glxc->idExists = GL_TRUE;
glxc->isCurrent = GL_FALSE; glxc->currentClient = NULL;
glxc->isDirect = isDirect; glxc->isDirect = isDirect;
glxc->hasUnflushedCommands = GL_FALSE; glxc->hasUnflushedCommands = GL_FALSE;
glxc->renderMode = GL_RENDER; glxc->renderMode = GL_RENDER;
@ -407,7 +407,7 @@ __glXDisp_DestroyContext(__GLXclientState * cl, GLbyte * pc)
return err; return err;
glxc->idExists = GL_FALSE; glxc->idExists = GL_FALSE;
if (!glxc->isCurrent) if (!glxc->currentClient)
FreeResourceByType(req->context, __glXContextRes, FALSE); FreeResourceByType(req->context, __glXContextRes, FALSE);
return Success; return Success;
@ -444,7 +444,7 @@ StopUsingContext(__GLXcontext * glxc)
/* Tell server GL library */ /* Tell server GL library */
__glXLastContext = 0; __glXLastContext = 0;
} }
glxc->isCurrent = GL_FALSE; glxc->currentClient = NULL;
if (!glxc->idExists) { if (!glxc->idExists) {
FreeResourceByType(glxc->id, __glXContextRes, FALSE); FreeResourceByType(glxc->id, __glXContextRes, FALSE);
} }
@ -454,8 +454,8 @@ StopUsingContext(__GLXcontext * glxc)
static void static void
StartUsingContext(__GLXclientState * cl, __GLXcontext * glxc) StartUsingContext(__GLXclientState * cl, __GLXcontext * glxc)
{ {
glxc->isCurrent = GL_TRUE;
__glXLastContext = glxc; __glXLastContext = glxc;
glxc->currentClient = cl->client;
} }
/** /**
@ -589,7 +589,7 @@ DoMakeCurrent(__GLXclientState * cl,
if (!validGlxContext(client, contextId, DixUseAccess, &glxc, &error)) if (!validGlxContext(client, contextId, DixUseAccess, &glxc, &error))
return error; return error;
if ((glxc != prevglxc) && glxc->isCurrent) { if ((glxc != prevglxc) && glxc->currentClient) {
/* Context is current to somebody else */ /* Context is current to somebody else */
return BadAccess; return BadAccess;
} }
@ -652,7 +652,7 @@ DoMakeCurrent(__GLXclientState * cl,
return __glXError(GLXBadContext); return __glXError(GLXBadContext);
} }
glxc->isCurrent = GL_TRUE; glxc->currentClient = client;
} }
StopUsingContext(prevglxc); StopUsingContext(prevglxc);
@ -873,7 +873,7 @@ __glXDisp_CopyContext(__GLXclientState * cl, GLbyte * pc)
/* /*
** The destination context must not be current for any client. ** The destination context must not be current for any client.
*/ */
if (dst->isCurrent) { if (dst->currentClient) {
client->errorValue = dest; client->errorValue = dest;
return BadAccess; return BadAccess;
} }

View File

@ -68,6 +68,11 @@ struct __GLXcontext {
*/ */
__GLXscreen *pGlxScreen; __GLXscreen *pGlxScreen;
/*
** If this context is current for a client, this will be that client
*/
ClientPtr currentClient;
/* /*
** The XID of this context. ** The XID of this context.
*/ */
@ -83,11 +88,6 @@ struct __GLXcontext {
*/ */
GLboolean idExists; GLboolean idExists;
/*
** Whether this context is current for some client.
*/
GLboolean isCurrent;
/* /*
** Whether this context is a direct rendering context. ** Whether this context is a direct rendering context.
*/ */

View File

@ -95,16 +95,15 @@ __glXResetLargeCommandStatus(__GLXclientState * cl)
} }
/* /*
** This procedure is called when the client who created the context goes * This procedure is called when the client who created the context goes away
** away OR when glXDestroyContext is called. In either case, all we do is * OR when glXDestroyContext is called. In either case, all we do is flag that
** flag that the ID is no longer valid, and (maybe) free the context. * the ID is no longer valid, and (maybe) free the context.
** use.
*/ */
static int static int
ContextGone(__GLXcontext * cx, XID id) ContextGone(__GLXcontext * cx, XID id)
{ {
cx->idExists = GL_FALSE; cx->idExists = GL_FALSE;
if (!cx->isCurrent) { if (!cx->currentClient) {
__glXFreeContext(cx); __glXFreeContext(cx);
} }
@ -138,9 +137,10 @@ DrawableGone(__GLXdrawable * glxPriv, XID xid)
for (c = glxAllContexts; c; c = next) { for (c = glxAllContexts; c; c = next) {
next = c->next; next = c->next;
if (c->isCurrent && (c->drawPriv == glxPriv || c->readPriv == glxPriv)) { if (c->currentClient &&
(c->drawPriv == glxPriv || c->readPriv == glxPriv)) {
(*c->loseCurrent) (c); (*c->loseCurrent) (c);
c->isCurrent = GL_FALSE; c->currentClient = NULL;
if (c == __glXLastContext) if (c == __glXLastContext)
__glXFlushContextCache(); __glXFlushContextCache();
} }
@ -196,17 +196,17 @@ __glXRemoveFromContextList(__GLXcontext * cx)
GLboolean GLboolean
__glXFreeContext(__GLXcontext * cx) __glXFreeContext(__GLXcontext * cx)
{ {
if (cx->idExists || cx->isCurrent) if (cx->idExists || cx->currentClient)
return GL_FALSE; return GL_FALSE;
__glXRemoveFromContextList(cx);
free(cx->feedbackBuf); free(cx->feedbackBuf);
free(cx->selectBuf); free(cx->selectBuf);
if (cx == __glXLastContext) { if (cx == __glXLastContext) {
__glXFlushContextCache(); __glXFlushContextCache();
} }
__glXRemoveFromContextList(cx);
/* We can get here through both regular dispatching from /* We can get here through both regular dispatching from
* __glXDispatch() or as a callback from the resource manager. In * __glXDispatch() or as a callback from the resource manager. In
* the latter case we need to lift the DRI lock manually. */ * the latter case we need to lift the DRI lock manually. */
@ -283,6 +283,7 @@ glxClientCallback(CallbackListPtr *list, pointer closure, pointer data)
NewClientInfoRec *clientinfo = (NewClientInfoRec *) data; NewClientInfoRec *clientinfo = (NewClientInfoRec *) data;
ClientPtr pClient = clientinfo->client; ClientPtr pClient = clientinfo->client;
__GLXclientState *cl = glxGetClient(pClient); __GLXclientState *cl = glxGetClient(pClient);
__GLXcontext *c, *next;
switch (pClient->clientState) { switch (pClient->clientState) {
case ClientStateRunning: case ClientStateRunning:
@ -290,6 +291,16 @@ glxClientCallback(CallbackListPtr *list, pointer closure, pointer data)
break; break;
case ClientStateGone: case ClientStateGone:
/* detach from all current contexts */
for (c = glxAllContexts; c; c = next) {
next = c->next;
if (c->currentClient == pClient) {
c->loseCurrent(c);
c->currentClient = NULL;
__glXFreeContext(c);
}
}
free(cl->returnBuf); free(cl->returnBuf);
free(cl->largeCmdBuf); free(cl->largeCmdBuf);
free(cl->GLClientextensions); free(cl->GLClientextensions);