[Spice-devel] [spice v10 10/27] server: Make the RedDrawable refcount thread-safe
Francois Gouget
fgouget at codeweavers.com
Mon Mar 21 18:43:20 UTC 2016
On Thu, 3 Mar 2016, Christophe Fergeau wrote:
[...]
> Making the refcounting thread safe is one thing, but then the code
> freeing the drawable needs to be thread-safe too. red_put_drawable might
> be fine (only looked briefly), but release_resource which is a vfunc
> provided by QEMU seems much less obvious. I think the drawable refcount
> is changed in the display channel thread, so I would try to queue an
> idle on its main_context, and do the unref from there. Hopefully it will
> not be too messy... (and I'll mention again
> g_main_context_push_thread_default which could be useful there ;) or
> some thread-local storage variable).
>
> By "queueing an idle", I mean something like
> GSource *source = g_idle_source_new();
> g_source_set_callback(..);
> g_source_attach(source, worker->core.main_context);
I have tried this approach but I get a feeling it cannot work and is
fundamentally flawed :-( There are two sticking points, the dependency
on the glib main loop and getting access to main_context.
I have not found a clean way to get the main_context information from
the RedDrawable:
- From the RedDrawable I can get a QXLState pointer with
red_drawable->qxl->st.
- And given a RedsState pointer I can get the main_context with
reds_get_core_interface(reds)->main_context
- But while QXLState does have a reds field, it's defined in red-qxl.c
so it's not accessible from red-worker.c and I have not found a
function that would return this information.
There is a loophole though: reds.h defines a reds global variable so I
can just use reds_get_core_interface(reds)->main_context! That really
does not feel right though. Anyway, doing that I got:
if (g_atomic_int_dec_and_test(&red_drawable->refs)) {
GSource *source = g_idle_source_new();
g_source_set_callback(source, red_drawable_unref_cb, red_drawable, NULL);
g_source_attach(source, reds_get_core_interface(reds)->main_context);
}
But this resulted in red_drawable_unref_cb never being called. The same
is true if using g_timeout_source_new() instead wit various timeout
values. I'll get back to this.
(there is also the issue of freeing the source which is uglyly fixable)
I'm also concerned about this use of reds_get_core_interface(). As far
as I can tell it's only used to call red_cannel_create() /
red_channel_create_parser(). And access to main_context seems to be
restricted to initializing RedWorker objects and implementing the timer
functions in event-loop.c.
That got me looking at the event-loop.c code and it essentially does all
the source manipulations we want, except as timers. That makes me think
that bypassing the reds_core_timer_*() functions is wrong.
So I implemented the unref through the reds_core_timer_*() functions
with a 1ms delay (0ms did not work). The callback got called and was
freeing RedDrawables... for a short time. Then I got a crash in
TimerSet()!
TimerSet() is an Xorg function on top of which the reds_core_timer_*()
functions are implemented in Xspice.
- It turns out TimerSet() & co are not thread-safe, hence the crash. So
reds_core_timer_*() cannot be used to solve thread-safety issues.
- I feel that this confirms the glib approach is wrong as it bypasses
the Xorg implementation.
- I now wonder if there is really a glib main loop in the Xspice case.
If not that would explain why the callback was not called with
g_idle_source_new().
A completely different approach to solving this would be to handle it
all in the GStreamer encoder code:
- Only ref the RedDrawable once but wrap it in a struct with its own
refcount. It's that struct which will track the actual refcounting by
the GstMemory objects.
- When that struct's refcount drops to zero, put the RedDrawable into a
GAsyncQueue.
- encode_frame() is always called from a safe thread, so unref all the
RedDrawables in the async queue whenever we enter / leave
encode_frame() or destroy the GStreamer encoder.
The drawback is that we could get up to a 1 frame delay before a
RedDrawable is freed.
--
Francois Gouget <fgouget at codeweavers.com>
More information about the Spice-devel
mailing list