[PATCH] dri2: Reference count DRI2 buffers
Oldřich Jedlička
oldium.pro at seznam.cz
Thu Aug 19 12:23:04 PDT 2010
On Thursday 19 August 2010 10:57:21 Christopher James Halse Rogers wrote:
> When a client calls ScheduleSwap we set up a kernel callback when the
> relevent vblank event occurs. However, it's possible for the client
> to go away between calling ScheduleSwap and the vblank event,
> resulting in the buffers being destroyed before they're passed to
> radeon_dri2_frame_event_handler.
I was also thinking about the solution and did some xorg-server investigation.
Personally I don't like comparing pointer values (ClientPtr), because it could
be the same - sequence malloc/free/malloc could return the same pointer value.
Here are two other possible solutions:
1. Add a "uniqueId" (increasing number with each client) to ClientRec. Then
you can compare something really unique. On the other hand this needs change
in xorg-server.
2. Use AddCallback(&ClientStateCallback, our_client_state_changed_method, 0)
during driver initialization to detect that any client went away and
invalidate its events (add field "valid" in event). That would be even better
than solution 1 - no change of xorg-server is needed. Each client could have
private data too - double-linked list of pending events - registered with
dixRegisterPrivateKey(). The event would be removed from the list only when it
is valid (otherwise the prev/next list pointers would be invalid too). Invalid
events would be ignored in the handler, they would be only freed.
Personally I like solution 2, because it fully uses xorg-server facilities.
But I don't know if this isn't too much or if there exists a simpler solution.
Oldřich.
> Add reference-counting to the buffers and take a reference in
> radeon_dri2_schedule_swap to ensure the buffers won't be destroyed
> before the vblank event is dealt with.
>
> Fixes: http://bugs.freedesktop.org/show_bug.cgi?id=29065
>
> v2: Don't write completion events to the client if it has quit.
> Signed-off-by: Christopher James Halse Rogers
> <christopher.halse.rogers at canonical.com> ---
> src/radeon_dri2.c | 73
> +++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 66
> insertions(+), 7 deletions(-)
>
> diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c
> index 4cafbc6..c6636c6 100644
> --- a/src/radeon_dri2.c
> +++ b/src/radeon_dri2.c
> @@ -55,6 +55,7 @@ typedef DRI2Buffer2Ptr BufferPtr;
> struct dri2_buffer_priv {
> PixmapPtr pixmap;
> unsigned int attachment;
> + unsigned int refcnt;
> };
>
>
> @@ -244,6 +245,7 @@ radeon_dri2_create_buffer(DrawablePtr drawable,
> buffers->flags = 0; /* not tiled */
> privates->pixmap = pixmap;
> privates->attachment = attachment;
> + privates->refcnt = 1;
>
> return buffers;
> }
> @@ -275,13 +277,26 @@ radeon_dri2_destroy_buffer(DrawablePtr drawable,
> BufferPtr buffers) if(buffers)
> {
> ScreenPtr pScreen = drawable->pScreen;
> - struct dri2_buffer_priv *private;
> + struct dri2_buffer_priv *private = buffers->driverPrivate;
>
> - private = buffers->driverPrivate;
> - (*pScreen->DestroyPixmap)(private->pixmap);
> + /* Trying to free an already freed buffer is unlikely to end well
> */ + if (private->refcnt == 0) {
> + ScrnInfoPtr scrn = xf86Screens[pScreen->myNum];
>
> - free(buffers->driverPrivate);
> - free(buffers);
> + xf86DrvMsg(scrn->scrnIndex, X_WARNING,
> + "Attempted to destroy previously destroyed buffer.\
> + This is a programming error\n");
> + return;
> + }
> +
> + private->refcnt--;
> + if (private->refcnt == 0)
> + {
> + (*pScreen->DestroyPixmap)(private->pixmap);
> +
> + free(buffers->driverPrivate);
> + free(buffers);
> + }
> }
> }
> #endif
> @@ -362,6 +377,7 @@ enum DRI2FrameEventType {
> typedef struct _DRI2FrameEvent {
> XID drawable_id;
> ClientPtr client;
> + int client_index;
> enum DRI2FrameEventType type;
> int frame;
>
> @@ -372,11 +388,26 @@ typedef struct _DRI2FrameEvent {
> DRI2BufferPtr back;
> } DRI2FrameEventRec, *DRI2FrameEventPtr;
>
> +static void
> +radeon_dri2_ref_buffer(BufferPtr buffer)
> +{
> + struct dri2_buffer_priv *private = buffer->driverPrivate;
> + private->refcnt++;
> +}
> +
> +static void
> +radeon_dri2_unref_buffer(BufferPtr buffer)
> +{
> + struct dri2_buffer_priv *private = buffer->driverPrivate;
> + radeon_dri2_destroy_buffer(&(private->pixmap->drawable), buffer);
> +}
> +
> void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int
> tv_sec, unsigned int tv_usec, void *event_data) {
> DRI2FrameEventPtr event = event_data;
> DrawablePtr drawable;
> + ClientPtr client;
> ScreenPtr screen;
> ScrnInfoPtr scrn;
> int status;
> @@ -387,6 +418,8 @@ void radeon_dri2_frame_event_handler(unsigned int
> frame, unsigned int tv_sec, 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;
> }
> @@ -394,6 +427,17 @@ void radeon_dri2_frame_event_handler(unsigned int
> frame, unsigned int tv_sec, screen = drawable->pScreen;
> scrn = xf86Screens[screen->myNum];
>
> + /* event->client may have quit between submitting a SwapBuffers
> 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;
> +
> switch (event->type) {
> case DRI2_FLIP:
> case DRI2_SWAP:
> @@ -405,11 +449,11 @@ void radeon_dri2_frame_event_handler(unsigned int
> frame, unsigned int tv_sec, radeon_dri2_copy_region(drawable, ®ion,
> event->front, event->back); swap_type = DRI2_BLIT_COMPLETE;
>
> - DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec,
> + DRI2SwapComplete(client, drawable, frame, tv_sec, tv_usec,
> swap_type, event->event_complete, event->event_data);
> break;
> case DRI2_WAITMSC:
> - DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec,
> tv_usec); + DRI2WaitMSCComplete(client, drawable, frame, tv_sec,
> tv_usec); break;
> default:
> /* Unknown type */
> @@ -418,6 +462,8 @@ void radeon_dri2_frame_event_handler(unsigned int
> frame, unsigned int tv_sec, break;
> }
>
> + radeon_dri2_unref_buffer(event->front);
> + radeon_dri2_unref_buffer(event->back);
> free(event);
> }
>
> @@ -512,6 +558,7 @@ 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;
>
> /* Get current count */
> @@ -648,11 +695,19 @@ 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;
>
> + /* radeon_dri2_frame_event_handler will get called some unknown time
> in the + * future with these buffers. Take a reference to ensure that
> they won't + * get destroyed before then.
> + */
> + radeon_dri2_ref_buffer(front);
> + radeon_dri2_ref_buffer(back);
> +
> /* Get current count */
> vbl.request.type = DRM_VBLANK_RELATIVE;
> if (crtc > 0)
> @@ -776,6 +831,10 @@ blit_fallback:
> DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func,
> data); if (swap_info)
> free(swap_info);
> +
> + radeon_dri2_unref_buffer(front);
> + radeon_dri2_unref_buffer(back);
> +
> *target_msc = 0; /* offscreen, so zero out target vblank count */
> return TRUE;
> }
More information about the xorg-driver-ati
mailing list