wayland_shm: Refcount the dmabuf buffer manager

Because we async render into buffers before the compositor has told us
we can use them, we can end up kicking over to fallback while still
rendering into a buffer.

Refcount the manager to let us clean up properly without crashing when
this happens.
This commit is contained in:
Derek Foreman 2016-12-06 16:16:10 -06:00
parent 9b048df6ff
commit 7fa02c8994
1 changed files with 63 additions and 12 deletions

View File

@ -42,6 +42,8 @@ struct _Buffer_Manager
void (*manager_destroy)(void);
void *priv;
void *dl_handle;
int refcount;
Eina_Bool destroyed;
};
Buffer_Manager *buffer_manager = NULL;
@ -316,6 +318,7 @@ _buffer_manager_get(void)
if (!success) goto err_bm;
drm_fd = fd;
buffer_manager->refcount = 1;
return buffer_manager;
err_bm:
@ -328,9 +331,16 @@ err_alloc:
}
static void
_buffer_manager_destroy(void)
_buffer_manager_ref(void)
{
if (!buffer_manager) return;
buffer_manager->refcount++;
}
static void
_buffer_manager_deref(void)
{
buffer_manager->refcount--;
if (buffer_manager->refcount || !buffer_manager->destroyed) return;
if (buffer_manager->manager_destroy) buffer_manager->manager_destroy();
free(buffer_manager);
@ -338,6 +348,50 @@ _buffer_manager_destroy(void)
close(drm_fd);
}
static void
_buffer_manager_destroy(void)
{
if (buffer_manager->destroyed) return;
buffer_manager->destroyed = EINA_TRUE;
_buffer_manager_deref();
}
static Buffer_Handle *
_buffer_manager_alloc(const char *name, int w, int h, unsigned long *stride, int32_t *fd)
{
Buffer_Handle *out;
_buffer_manager_ref();
out = buffer_manager->alloc(buffer_manager, name, w, h, stride, fd);
if (!out) _buffer_manager_deref();
return out;
}
static void *
_buffer_manager_map(Dmabuf_Buffer *buf)
{
void *out;
_buffer_manager_ref();
out = buffer_manager->map(buf);
if (!out) _buffer_manager_deref();
return out;
}
static void
_buffer_manager_unmap(Dmabuf_Buffer *buf)
{
buffer_manager->unmap(buf);
_buffer_manager_deref();
}
static void
_buffer_manager_discard(Dmabuf_Buffer *buf)
{
buffer_manager->discard(buf);
_buffer_manager_deref();
}
static void
buffer_release(void *data, struct wl_buffer *buffer EINA_UNUSED)
{
@ -379,7 +433,7 @@ _fallback(Dmabuf_Surface *s, int w, int h)
if (!b) b = s->current;
if (!b) goto out;
if (!b->mapping) b->mapping = buffer_manager->map(b);
if (!b->mapping) b->mapping = _buffer_manager_map(b);
b->busy = EINA_FALSE;
@ -394,7 +448,7 @@ _fallback(Dmabuf_Surface *s, int w, int h)
for (y = 0; y < h; y++)
memcpy(new_data + y * w * 4, old_data + y * b->stride, w * 4);
surf->funcs.post(surf, NULL, 0, EINA_FALSE);
buffer_manager->unmap(b);
_buffer_manager_unmap(b);
out:
_internal_evas_dmabuf_surface_destroy(s);
@ -471,7 +525,7 @@ static const struct zwp_linux_buffer_params_v1_listener params_listener =
static void
_evas_dmabuf_buffer_unlock(Dmabuf_Buffer *b)
{
buffer_manager->unmap(b);
_buffer_manager_unmap(b);
b->mapping = NULL;
b->locked = EINA_FALSE;
}
@ -488,9 +542,7 @@ _evas_dmabuf_buffer_destroy(Dmabuf_Buffer *b)
return;
}
if (b->fd != -1) close(b->fd);
/* The buffer manager may have been destroyed already if we're
* doing fallback */
if (buffer_manager) buffer_manager->discard(b);
_buffer_manager_discard(b);
if (b->wl_buffer) wl_buffer_destroy(b->wl_buffer);
b->wl_buffer = NULL;
free(b);
@ -545,7 +597,7 @@ _evas_dmabuf_surface_data_get(Surface *s, int *w, int *h)
if (h) *h = b->h;
if (b->locked) return b->mapping;
ptr = buffer_manager->map(b);
ptr = _buffer_manager_map(b);
if (!ptr)
return NULL;
@ -648,17 +700,16 @@ _evas_dmabuf_buffer_init(Dmabuf_Surface *s, int w, int h)
{
Dmabuf_Buffer *out;
struct zwp_linux_buffer_params_v1 *dp;
Buffer_Manager *bm = _buffer_manager_get();
uint32_t flags = 0;
if (!bm) return NULL;
if (!_buffer_manager_get()) return NULL;
out = calloc(1, sizeof(Dmabuf_Buffer));
if (!out) return NULL;
out->fd = -1;
out->surface = s;
out->bh = bm->alloc(bm, "name", w, h, &out->stride, &out->fd);
out->bh = _buffer_manager_alloc("name", w, h, &out->stride, &out->fd);
if (!out->bh)
{
free(out);