[Spice-devel] [spice-gtk v1 04/11] channel-display: use GHashTable to keep stream's structure
Victor Toso
victortoso at redhat.com
Tue Apr 10 11:58:17 UTC 2018
Hi,
On Mon, Apr 09, 2018 at 02:14:15PM +0300, Uri Lublin wrote:
> On 03/13/2018 01:25 PM, Victor Toso wrote:
> > From: Victor Toso <me at victortoso.com>
> >
> > The major issue with the current approach is that it relies on
> > the ID from SpiceMsgDisplayStreamCreate to create the smallest 2^n
> > sized array that could fit the stream's id as index.
> >
> > This is potentially dangerous as the ID value is not documented and
> > could have any uint32_t value. We depend on Spice server's
> > implementation which currently defines max of 50 streams. >
> > > /** Maximum number of streams created by spice-server */
> > > #define NUM_STREAMS 50
> >
> > The first ID has the value of 49* while the c->streams array would be
> > allocated to 64. We could only benefit from allocating on high
> > creating/removal of streams, which is not the case.
> >
> > We can improve the above situations by using a hash table.
>
> Some thoughts:
> 1. It make sense to make it 64 on the server side.
Why? Just so we can alloc a power of two?
> 2. Perhaps we should limit the total number of stream allowed
> (and/or add a message telling the client how many streams
> the server allocates)
I don't know why we should limit it at this poing.
> 3. O(1) is not exactly 1 and also hash consumes (a little bit)
> more memory.
Indeed. Hash table takes more time then a direct access in an
array and uses more memory too but I'm thinking that we are not
dealing with enough data to consider hash collisions to be a
problem.
> Other than those and some comments below, it looks good.
Thanks,
>
> > Signed-off-by: Victor Toso <victortoso at redhat.com>
> > ---
> > src/channel-display.c | 73 ++++++++++++++++++++++-----------------------------
> > 1 file changed, 32 insertions(+), 41 deletions(-)
> >
> > diff --git a/src/channel-display.c b/src/channel-display.c
> > index 2ea0922..ec94cf8 100644
> > --- a/src/channel-display.c
> > +++ b/src/channel-display.c
> > @@ -63,8 +63,7 @@ struct _SpiceDisplayChannelPrivate {
> > SpicePaletteCache palette_cache;
> > SpiceImageSurfaces image_surfaces;
> > SpiceGlzDecoderWindow *glz_window;
> > - display_stream **streams;
> > - int nstreams;
> > + GHashTable *streams;
> > gboolean mark;
> > guint mark_false_event_id;
> > GArray *monitors;
> > @@ -168,7 +167,7 @@ static void spice_display_channel_finalize(GObject *object)
> > g_clear_pointer(&c->monitors, g_array_unref);
> > clear_surfaces(SPICE_CHANNEL(object), FALSE);
> > g_hash_table_unref(c->surfaces);
> > - clear_streams(SPICE_CHANNEL(object));
> > + g_clear_pointer(&c->streams, g_hash_table_unref);
>
> Is it safer to keep clear_streams similar to clear_surfaces ?
> There is a difference, clear_surfaces also emits a signal.
The main use for clear_streams() is to clear the streams, which
does that by calling g_hash_table_remove_all(). In the finalize
method we are destroying the object so I think g_clear_pointer()
with unref for the GHashTable works fine.
Maybe I did not understand what you mean with clear_surfaces. I
did not touch that part but as you said clear_surfaces() is more
elaborated then clear_streams(). Still, I'd mind to change the
g_hash_table_unref(c->surfaces) to be a g_clear_pointer(..) too.
> > g_clear_pointer(&c->palettes, cache_free);
> > if (G_OBJECT_CLASS(spice_display_channel_parent_class)->finalize)
> > @@ -863,6 +862,7 @@ static void spice_display_channel_init(SpiceDisplayChannel *channel)
> > c = channel->priv = SPICE_DISPLAY_CHANNEL_GET_PRIVATE(channel);
> > c->surfaces = g_hash_table_new_full(NULL, NULL, NULL, destroy_surface);
> > + c->streams = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, display_stream_destroy);
>
> - Stream id is an integer, why not keep integer keys
> (g_int_hash, g_int_equal)?
> Probably it does not matter much.
It is a unsigned integer. Yes, I was using it in v1 but it wasn't
working properly while with g_direct_hash() I never had a
problem. Probably my fault.
> - Why make it different than the line above (using NULL instead of
> specifying the default functions)
Because the there is not allocation to the hash table's key
(pointer to uint);
> Uri.
Thanks for the review, let me know if there anything you want me
to change ;)
Cheers,
toso
>
> > c->image_cache.ops = &image_cache_ops;
> > c->palette_cache.ops = &palette_cache_ops;
> > c->image_surfaces.ops = &image_surfaces_ops;
> > @@ -1207,15 +1207,18 @@ static void report_invalid_stream(SpiceChannel *channel, uint32_t id)
> > static display_stream *get_stream_by_id(SpiceChannel *channel, uint32_t id)
> > {
> > + display_stream *st;
> > SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
> > - if (c != NULL && c->streams != NULL && id < c->nstreams &&
> > - c->streams[id] != NULL) {
> > - return c->streams[id];
> > + g_return_val_if_fail(c != NULL && c->streams != NULL, NULL);
> > +
> > + st = g_hash_table_lookup(c->streams, GUINT_TO_POINTER(id));
> > + if (st == NULL) {
> > + spice_printerr("couldn't find stream %u", id);
> > + report_invalid_stream(channel, id);
> > }
> > - report_invalid_stream(channel, id);
> > - return NULL;
> > + return st;
> > }
> > /* coroutine context */
> > @@ -1257,45 +1260,36 @@ static display_stream *display_stream_create(SpiceChannel *channel,
> > return st;
> > }
> > -static void destroy_stream(SpiceChannel *channel, int id)
> > +static void destroy_stream(SpiceChannel *channel, uint32_t id)
> > {
> > SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
> > g_return_if_fail(c != NULL);
> > g_return_if_fail(c->streams != NULL);
> > - g_return_if_fail(c->nstreams > id);
> > - g_clear_pointer(&c->streams[id], display_stream_destroy);
> > + g_hash_table_remove(c->streams, GUINT_TO_POINTER(id));
> > }
> > static void display_handle_stream_create(SpiceChannel *channel, SpiceMsgIn *in)
> > {
> > SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
> > SpiceMsgDisplayStreamCreate *op = spice_msg_in_parsed(in);
> > + display_stream *st;
> > CHANNEL_DEBUG(channel, "%s: id %u", __FUNCTION__, op->id);
> > + g_return_if_fail(c->streams != NULL);
> > - if (op->id >= c->nstreams) {
> > - int n = c->nstreams;
> > - if (!c->nstreams) {
> > - c->nstreams = 1;
> > - }
> > - while (op->id >= c->nstreams) {
> > - c->nstreams *= 2;
> > - }
> > - c->streams = realloc(c->streams, c->nstreams * sizeof(c->streams[0]));
> > - memset(c->streams + n, 0, (c->nstreams - n) * sizeof(c->streams[0]));
> > - }
> > - g_return_if_fail(c->streams[op->id] == NULL);
> > -
> > - c->streams[op->id] = display_stream_create(channel, op->id, op->surface_id,
> > - op->flags, op->codec_type,
> > - &op->dest, &op->clip);
> > - if (c->streams[op->id] == NULL) {
> > + st = display_stream_create(channel, op->id, op->surface_id,
> > + op->flags, op->codec_type,
> > + &op->dest, &op->clip);
> > + if (st == NULL) {
> > spice_printerr("could not create the %u video stream", op->id);
> > destroy_stream(channel, op->id);
> > report_invalid_stream(channel, op->id);
> > + return;
> > }
> > +
> > + g_hash_table_insert(c->streams, GUINT_TO_POINTER(op->id), st);
> > }
> > static const SpiceRect *stream_get_dest(display_stream *st, SpiceMsgIn *frame_msg)
> > @@ -1470,18 +1464,21 @@ static void display_session_mm_time_reset_cb(SpiceSession *session, gpointer dat
> > {
> > SpiceChannel *channel = data;
> > SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
> > - guint i;
> > + GHashTableIter iter;
> > + gpointer key, value;
> > CHANNEL_DEBUG(channel, "%s", __FUNCTION__);
> > - for (i = 0; i < c->nstreams; i++) {
> > - display_stream *st;
> > + g_hash_table_iter_init (&iter, c->streams);
> > + while (g_hash_table_iter_next (&iter, &key, &value)) {
> > + display_stream *st = value;
> > - if (c->streams[i] == NULL) {
> > + if (st == NULL) {
> > + g_warn_if_reached();
> > continue;
> > }
> > - SPICE_DEBUG("%s: stream-id %u", __FUNCTION__, i);
> > - st = c->streams[i];
> > +
> > + SPICE_DEBUG("%s: stream-id %u", __FUNCTION__, GPOINTER_TO_UINT(key));
> > st->video_decoder->reschedule(st->video_decoder);
> > }
> > }
> > @@ -1626,13 +1623,7 @@ static void display_stream_destroy(gpointer st_pointer)
> > static void clear_streams(SpiceChannel *channel)
> > {
> > SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv;
> > - int i;
> > -
> > - for (i = 0; i < c->nstreams; i++) {
> > - destroy_stream(channel, i);
> > - }
> > - g_clear_pointer(&c->streams, g_free);
> > - c->nstreams = 0;
> > + g_hash_table_remove_all(c->streams);
> > }
> > /* coroutine context */
> >
>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20180410/e1d33d3e/attachment.sig>
More information about the Spice-devel
mailing list