[cairo] cairo_xcb_surface_create() segfaults on second call with different xcb info

Uli Schlachter psychon at znc.in
Sat Nov 10 09:11:36 UTC 2018


On 10.11.18 06:36, Ryan Flannery wrote:
[...]
>> Also: Finishing the device automatically finishes all cairo_surface_t
>> instances for this XCB devices.
> 
> Ok - this raises a question for me about the device state between
> surface creation.
> Do you mean one of the following:
> 
> 1. Calling cairo_device_finish(cairo_surface_get_device(cs)) when
> destroying a xcb/cairo setup should, effectively, remove the state
> tracked in cairo_xcb_surface_create() (and thus allow a subsequent
> call with the same pointer value, but different contents, succeed)?
> 
> -or-
> 
> 2. That there's still state there and I should track that
> cairo_device_t* reference on my side, even if I call the
> cairo_device_finish() bits?

I think I mean (1). Finishing the cairo_device_t instance tries to get
rid of everything that uses the underlying xcb_connection_t*. It does
not do this completely, but the difference should not matter to you.

The reason is reference counting: Finishing the cairo_device_t finishes
everything using this cairo_device_t / xcb_connection_t. "Finishes" here
means "makes unusable". However, it does not actually free everything,
since other pieces of code could still have a reference that keeps the
cairo_device_t alive (e.g. through cairo_device_reference,
cairo_surface_reference, or just cairo_xcb_surface_create). All this
"state" is cannot be freed yet, because otherwise the code having that
reference would end up with a dangling pointer. But, any attempt of
using one of these objects generates a CAIRO_STATUS_SURFACE_FINISHED or
CAIRO_STATUS_DEVICE_FINISHED error status.

The important bit for you is that finishing the connection removes the
cairo_device_t from cairo's internal cache, so any following attempts at
using the same xcb_connection_t will create a new cairo_device_t instance.

> I could probably answer that on my own if I had my dev laptop with me,

If you want to dig into some code:
src/cairo-xcb-connection.c has a _cairo_xcb_device_backend struct that
defines the operations on a cairo_xcb_connection_t (which is basically a
non-public subtype of cairo_device_t).

_cairo_xcb_connection_get() is used to "turn" a xcb_connection_t into a
cairo_xcb_connection_t. This first checks a global list called
"connections" for a matching entry and otherwise creates a new instance
of a cairo_xcb_connection_t.

_device_finish() does the work in cairo_device_finish(). It gets rid of
open fonts and a cairo-specific representation of an X11 screen (which
contains a list of all cairo-xcb surfaces using this one screen; thus
this also cairo_surface_finish()es all surfaces using this device).

Later, _device_destroy() is run when the reference count of the
cairo_device_t actually drops to zero. This does not do anything with
the xcb_connection_t, but instead does all the freeing that is
necessary. E.g., some internal mutexes are destroyed. This also free()s
the cairo_xcb_connection_t itself.

Hope this helps,
Uli
-- 
"Why make things difficult, when it is possible to make them cryptic
and totally illogical, with just a little bit more effort?" -- A. P. J.


More information about the cairo mailing list