[Spice-devel] [spice v10 10/27] server: Make the RedDrawable refcount thread-safe
Frediano Ziglio
fziglio at redhat.com
Wed Mar 23 09:32:22 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
No, this is the problem. Every RedWorker has a different glib context, if
you pass through the core main_context should be NULL pointing to default
one which is not used.
> - 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.
You could use red_qxl_get_server but as said before you would get
the wrong context.
I think here what's missing is a way to get a RedWorker from a QXLInstance.
This could be added to red-qxl.[ch].
>
> 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.
>
There is similar code to deal with Glz (see display_channel_free_glz_drawables_to_free
call in red-worker.c).
Frediano
More information about the Spice-devel
mailing list