[Spice-devel] [PATCH spice-server 1/2] Refactory Glz RedDrawable retention code

Frediano Ziglio fziglio at redhat.com
Thu Dec 8 14:45:33 UTC 2016


ping

this patch is about 6 months old

Frediano

----- Original Message -----
> From: "Frediano Ziglio" <fziglio at redhat.com>
> To: spice-devel at lists.freedesktop.org
> Cc: "Frediano Ziglio" <fziglio at redhat.com>
> Sent: Tuesday, October 18, 2016 10:28:25 AM
> Subject: [PATCH spice-server 1/2] Refactory Glz RedDrawable retention code
> 
> The code was quite complicated.
> - a GlzEncDictImageContext was bound to a GlzDrawableInstanceItem
>   (1-1 relation);
> - multiple GlzDrawableInstanceItem were attached to RedGlzDrawable;
> - multiple RedGlzDrawable (one for RedClient) were attached to Drawable
>   using GlzImageRetention;
> - OOM code require all Glz dictionary used by DisplayChannel to be locked
>   at the same time;
> - drawable count computation during OOM was not corrent in case of
>   multiple clients;
> - not clear why to call glz_retention_free_drawables or
>   glz_retention_detach_drawables.
> 
> Now:
> - a GlzEncDictImageContext is bound to a GlzDictItem (still 1-1 relation);
> - multiple GlzDictItem are attached to a Drawable using GlzImageRetention;
> - there is only a glz_retention_free to be called when the structure
>   must be destroyed.
> 
> Implementation detail:
> - red_drawable_unref returns a gboolean to indicate if the object
>   was really freed;
> - glz_drawable_count was removed as there are no more RedGlzDrawable;
> - image_encoders_glz_encode_lock and image_encoders_glz_encode_unlock
>   were removed and now the locking is handled entirely by ImageEncoders;
> - instead of marking GlzDictItem/GlzDrawableInstanceItem changing
>   has_drawable flag contexts are moved to a glz_orphans ring;
> - code is smaller (100 lines less in image-encoders.c) and I hope easier
>   to read.
> 
> Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> ---
>  server/display-channel.c |  57 +++++----
>  server/image-encoders.c  | 300
>  +++++++++++++++--------------------------------
>  server/image-encoders.h  |  27 +++--
>  server/red-worker.c      |   5 +-
>  server/red-worker.h      |   2 +-
>  5 files changed, 145 insertions(+), 246 deletions(-)
> 
> diff --git a/server/display-channel.c b/server/display-channel.c
> index 69edd35..9a56080 100644
> --- a/server/display-channel.c
> +++ b/server/display-channel.c
> @@ -1185,7 +1185,7 @@ void display_channel_free_glz_drawables(DisplayChannel
> *display)
>      }
>  }
>  
> -static bool free_one_drawable(DisplayChannel *display, int force_glz_free)
> +static bool free_one_drawable(DisplayChannel *display)
>  {
>      RingItem *ring_item = ring_get_tail(&display->priv->current_list);
>      Drawable *drawable;
> @@ -1196,9 +1196,6 @@ static bool free_one_drawable(DisplayChannel *display,
> int force_glz_free)
>      }
>  
>      drawable = SPICE_CONTAINEROF(ring_item, Drawable, list_link);
> -    if (force_glz_free) {
> -        glz_retention_free_drawables(&drawable->glz_retention);
> -    }
>      drawable_draw(display, drawable);
>      container = drawable->tree_item.base.container;
>  
> @@ -1210,37 +1207,47 @@ static bool free_one_drawable(DisplayChannel
> *display, int force_glz_free)
>  void display_channel_current_flush(DisplayChannel *display, int surface_id)
>  {
>      while
>      (!ring_is_empty(&display->priv->surfaces[surface_id].current_list)) {
> -        free_one_drawable(display, FALSE);
> +        free_one_drawable(display);
>      }
>      current_remove_all(display, surface_id);
>  }
>  
>  void display_channel_free_some(DisplayChannel *display)
>  {
> -    int n = 0;
> +    int left_to_free = RED_RELEASE_BUNCH_SIZE;
>      DisplayChannelClient *dcc;
>      GListIter iter;
>  
> -    spice_debug("#draw=%d, #glz_draw=%d", display->priv->drawable_count,
> -                display->priv->encoder_shared_data.glz_drawable_count);
> +    /* As we are trying to release some memory as requested by guest driver
> +     * loop througth Glz freeing drawables which have no corresponding
> Drawable
> +     * as this will cause guest memory to be freed. */
> +    spice_debug("#draw=%d", display->priv->drawable_count);
>      FOREACH_DCC(display, iter, dcc) {
>          ImageEncoders *encoders = dcc_get_encoders(dcc);
>  
> -        // encoding using the dictionary is prevented since the following
> operations might
> -        // change the dictionary
> -        if (image_encoders_glz_encode_lock(encoders)) {
> -            n =
> image_encoders_free_some_independent_glz_drawables(encoders);
> -        }
> +        left_to_free -=
> image_encoders_free_some_independent_glz_drawables(encoders, left_to_free);
>      }
>  
> -    while (!ring_is_empty(&display->priv->current_list) && n++ <
> RED_RELEASE_BUNCH_SIZE) {
> -        free_one_drawable(display, TRUE);
> -    }
> +    if (left_to_free > 0) {
> +        int saved_to_free = left_to_free;
>  
> -    FOREACH_DCC(display, iter, dcc) {
> -        ImageEncoders *encoders = dcc_get_encoders(dcc);
> +        while (left_to_free > 0 &&
> !ring_is_empty(&display->priv->current_list)) {
> +            free_one_drawable(display);
> +            --left_to_free;
> +        }
>  
> -        image_encoders_glz_encode_unlock(encoders);
> +        /* We freed Drawables in the loop above but the underlaying
> RedDrawables
> +         * could still be referenced by Glz so scan again the Glz for
> independent
> +         * drawables. Note that there is a 1-to-1 relatioship between
> Drawable
> +         * and RedDrawable so in theory there will be a maximum of
> saved_to_free
> +         * RedDrawables to free. Use this limit in any case, this should not
> be
> +         * a problem and will make sure we won't free more items that we are
> +         * supposed to free. */
> +        left_to_free = saved_to_free;
> +        FOREACH_DCC(display, iter, dcc) {
> +            ImageEncoders *encoders = dcc_get_encoders(dcc);
> +            left_to_free -=
> image_encoders_free_some_independent_glz_drawables(encoders, left_to_free);
> +        }
>      }
>  }
>  
> @@ -1285,7 +1292,7 @@ static Drawable
> *display_channel_drawable_try_new(DisplayChannel *display,
>      Drawable *drawable;
>  
>      while (!(drawable = drawable_try_new(display))) {
> -        if (!free_one_drawable(display, FALSE))
> +        if (!free_one_drawable(display))
>              return NULL;
>      }
>  
> @@ -1362,7 +1369,7 @@ void drawable_unref(Drawable *drawable)
>      drawable_unref_surface_deps(display, drawable);
>      display_channel_surface_unref(display, drawable->surface_id);
>  
> -    glz_retention_detach_drawables(&drawable->glz_retention);
> +    glz_retention_destroy(&drawable->glz_retention);
>  
>      if (drawable->red_drawable) {
>          red_drawable_unref(drawable->red_drawable);
> @@ -1866,9 +1873,8 @@ static void on_disconnect(RedChannelClient *rcc)
>      display_channel_compress_stats_print(display);
>  
>      // this was the last channel client
> -    spice_debug("#draw=%d, #glz_draw=%d",
> -                display->priv->drawable_count,
> -                display->priv->encoder_shared_data.glz_drawable_count);
> +    spice_debug("#draw=%d",
> +                display->priv->drawable_count);
>  }
>  
>  static int handle_migrate_flush_mark(RedChannelClient *rcc)
> @@ -2117,10 +2123,9 @@ void display_channel_debug_oom(DisplayChannel
> *display, const char *msg)
>  {
>      RedChannel *channel = RED_CHANNEL(display);
>  
> -    spice_debug("%s #draw=%u, #glz_draw=%u current %u pipes %u",
> +    spice_debug("%s #draw=%u, current %u pipes %u",
>                  msg,
>                  display->priv->drawable_count,
> -                display->priv->encoder_shared_data.glz_drawable_count,
>                  display->priv->current_size,
>                  red_channel_sum_pipes_size(channel));
>  }
> diff --git a/server/image-encoders.c b/server/image-encoders.c
> index 39aca6c..951fe96 100644
> --- a/server/image-encoders.c
> +++ b/server/image-encoders.c
> @@ -30,11 +30,6 @@
>  
>  #define ENCODER_MESSAGE_SIZE 512
>  
> -#define MAX_GLZ_DRAWABLE_INSTANCES 2
> -
> -typedef struct RedGlzDrawable RedGlzDrawable;
> -typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
> -
>  struct GlzSharedDictionary {
>      GlzEncDictContext *dict;
>      uint32_t refs;
> @@ -44,37 +39,28 @@ struct GlzSharedDictionary {
>      RedClient *client; // channel clients of the same client share the dict
>  };
>  
> -/* for each qxl drawable, there may be several instances of lz drawables */
> -/* TODO - reuse this stuff for the top level. I just added a second level of
> multiplicity
> - * at the Drawable by keeping a ring, so:
> - * Drawable -> (ring of) RedGlzDrawable -> (up to 2) GlzDrawableInstanceItem
> - * and it should probably (but need to be sure...) be
> - * Drawable -> ring of GlzDrawableInstanceItem.
> +/**
> + * This structure is used to retain RedDrawable objects so they don't
> + * disappear while we are using their memory.
> + * This is because Glz will keep using image memory for future compressions.
>   */
> -struct GlzDrawableInstanceItem {
> -    RingItem glz_link;
> +typedef struct GlzDictItem {
> +    /** linked to ImageEncoders::glz_dict_items. Always inserted */
> +    RingItem glz_context_link;
> +    /** linked to GlzImageRetention::ring or ImageEncoders::orphans. Always
> inserted */
> +    RingItem retention_link;
> +    /** linked to ImageEncoders::glz_dict_items_to_free */
>      RingItem free_link;
> -    GlzEncDictImageContext *context;
> -    RedGlzDrawable         *glz_drawable;
> -};
> -
> -struct RedGlzDrawable {
> -    RingItem link;    // ordered by the time it was encoded
> -    RingItem drawable_link;
> +    /** context pointer from Glz encoder, used to free the drawable */
> +    GlzEncDictImageContext *dict_image_context;
> +    /** where the drawable came from */
> +    ImageEncoders *enc;
> +    /** hold a reference to this object, this to make sure Glz
> +     *  can continue to use its memory */
>      RedDrawable *red_drawable;
> -    GlzDrawableInstanceItem instances_pool[MAX_GLZ_DRAWABLE_INSTANCES];
> -    Ring instances;
> -    uint8_t instances_count;
> -    gboolean has_drawable;
> -    ImageEncoders *encoders;
> -};
> -
> -#define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
> -                                           drawable_link)
> -#define DRAWABLE_FOREACH_GLZ_SAFE(drawable, link, next, glz) \
> -    SAFE_FOREACH(link, next, drawable, &(drawable)->glz_retention.ring, glz,
> LINK_TO_GLZ(link))
> +} GlzDictItem;
>  
> -static void glz_drawable_instance_item_free(GlzDrawableInstanceItem
> *instance);
> +static gboolean glz_context_free(GlzDictItem *glz_item);
>  static void encoder_data_init(EncoderData *data);
>  static void encoder_data_reset(EncoderData *data);
>  static void image_encoders_release_glz(ImageEncoders *enc);
> @@ -381,23 +367,24 @@ static void image_encoders_init_lz(ImageEncoders *enc)
>  static void glz_usr_free_image(GlzEncoderUsrContext *usr, GlzUsrImageContext
>  *image)
>  {
>      GlzData *lz_data = (GlzData *)usr;
> -    GlzDrawableInstanceItem *glz_drawable_instance =
> (GlzDrawableInstanceItem *)image;
> -    ImageEncoders *drawable_enc =
> glz_drawable_instance->glz_drawable->encoders;
> +    GlzDictItem *glz_item = (GlzDictItem *)image;
> +    ImageEncoders *drawable_enc = glz_item->enc;
>      ImageEncoders *this_enc = SPICE_CONTAINEROF(lz_data, ImageEncoders,
>      glz_data);
> +    glz_item->dict_image_context = NULL;
>      if (this_enc == drawable_enc) {
> -        glz_drawable_instance_item_free(glz_drawable_instance);
> +        glz_context_free(glz_item);
>      } else {
>          /* The glz dictionary is shared between all DisplayChannelClient
> -         * instances that belong to the same client, and glz_usr_free_image
> +         * items that belong to the same client, and glz_usr_free_image
>           * can be called by the dictionary code
>           * (glz_dictionary_window_remove_head). Thus this function can be
>           * called from any DisplayChannelClient thread, hence the need for
>           * this check.
>           */
> -        pthread_mutex_lock(&drawable_enc->glz_drawables_inst_to_free_lock);
> -        ring_add_before(&glz_drawable_instance->free_link,
> -                        &drawable_enc->glz_drawables_inst_to_free);
> -
> pthread_mutex_unlock(&drawable_enc->glz_drawables_inst_to_free_lock);
> +        pthread_mutex_lock(&drawable_enc->glz_dict_items_to_free_lock);
> +        ring_add_before(&glz_item->free_link,
> +                        &drawable_enc->glz_dict_items_to_free);
> +        pthread_mutex_unlock(&drawable_enc->glz_dict_items_to_free_lock);
>      }
>  }
>  
> @@ -456,9 +443,10 @@ void image_encoders_init(ImageEncoders *enc,
> ImageEncoderSharedData *shared_data
>      spice_assert(shared_data);
>      enc->shared_data = shared_data;
>  
> -    ring_init(&enc->glz_drawables);
> -    ring_init(&enc->glz_drawables_inst_to_free);
> -    pthread_mutex_init(&enc->glz_drawables_inst_to_free_lock, NULL);
> +    ring_init(&enc->glz_orphans);
> +    ring_init(&enc->glz_dict_items);
> +    ring_init(&enc->glz_dict_items_to_free);
> +    pthread_mutex_init(&enc->glz_dict_items_to_free_lock, NULL);
>  
>      image_encoders_init_glz_data(enc);
>      image_encoders_init_quic(enc);
> @@ -488,121 +476,63 @@ void image_encoders_free(ImageEncoders *enc)
>  #endif
>      zlib_encoder_destroy(enc->zlib);
>      enc->zlib = NULL;
> -    pthread_mutex_destroy(&enc->glz_drawables_inst_to_free_lock);
> +    pthread_mutex_destroy(&enc->glz_dict_items_to_free_lock);
>  }
>  
>  /* Remove from the to_free list and the instances_list.
> -   When no instance is left - the RedGlzDrawable is released too. (and the
> qxl drawable too, if
> -   it is not used by Drawable).
> -   NOTE - 1) can be called only by the display channel that created the
> drawable
> -          2) it is assumed that the instance was already removed from the
> dictionary*/
> -static void glz_drawable_instance_item_free(GlzDrawableInstanceItem
> *instance)
> +   Returns TRUE if a RedDrawable was freed.
> +   NOTE - can be called only by the display channel that created the
> drawable */
> +static gboolean glz_context_free(GlzDictItem *glz_item)
>  {
> -    RedGlzDrawable *glz_drawable;
> -
> -    spice_assert(instance);
> -    spice_assert(instance->glz_drawable);
> +    gboolean ret;
>  
> -    glz_drawable = instance->glz_drawable;
> +    spice_assert(glz_item);
>  
> -    spice_assert(glz_drawable->instances_count > 0);
> -
> -    ring_remove(&instance->glz_link);
> -    glz_drawable->instances_count--;
> +    ring_remove(&glz_item->glz_context_link);
> +    ring_remove(&glz_item->retention_link);
>  
>      // when the remove callback is performed from the channel that the
> -    // drawable belongs to, the instance is not added to the 'to_free' list
> -    if (ring_item_is_linked(&instance->free_link)) {
> -        ring_remove(&instance->free_link);
> -    }
> -
> -    if (ring_is_empty(&glz_drawable->instances)) {
> -        spice_assert(glz_drawable->instances_count == 0);
> -
> -        if (glz_drawable->has_drawable) {
> -            ring_remove(&glz_drawable->drawable_link);
> -        }
> -        red_drawable_unref(glz_drawable->red_drawable);
> -        glz_drawable->encoders->shared_data->glz_drawable_count--;
> -        if (ring_item_is_linked(&glz_drawable->link)) {
> -            ring_remove(&glz_drawable->link);
> -        }
> -        free(glz_drawable);
> -    }
> -}
> -
> -/*
> - * Releases all the instances of the drawable from the dictionary and the
> display channel client.
> - * The release of the last instance will also release the drawable itself
> and the qxl drawable
> - * if possible.
> - * NOTE - the caller should prevent encoding using the dictionary during
> this operation
> - */
> -static void red_glz_drawable_free(RedGlzDrawable *glz_drawable)
> -{
> -    ImageEncoders *enc = glz_drawable->encoders;
> -    RingItem *head_instance = ring_get_head(&glz_drawable->instances);
> -    int cont = (head_instance != NULL);
> -
> -    while (cont) {
> -        if (glz_drawable->instances_count == 1) {
> -            /* Last instance: glz_drawable_instance_item_free will free the
> glz_drawable */
> -            cont = FALSE;
> -        }
> -        GlzDrawableInstanceItem *instance = SPICE_CONTAINEROF(head_instance,
> -
> GlzDrawableInstanceItem,
> -                                                        glz_link);
> -        if (!ring_item_is_linked(&instance->free_link)) {
> -            // the instance didn't get out from window yet
> -            glz_enc_dictionary_remove_image(enc->glz_dict->dict,
> -                                            instance->context,
> -                                            &enc->glz_data.usr);
> -        }
> -        glz_drawable_instance_item_free(instance);
> -
> -        if (cont) {
> -            head_instance = ring_get_head(&glz_drawable->instances);
> -        }
> +    // drawable belongs to, the item is not added to the 'to_free' list
> +    if (ring_item_is_linked(&glz_item->free_link)) {
> +        ring_remove(&glz_item->free_link);
> +    } else if (glz_item->dict_image_context) {
> +        ImageEncoders *enc = glz_item->enc;
> +
> +        // the item didn't get out from window yet
> +        glz_enc_dictionary_remove_image(enc->glz_dict->dict,
> +                                        glz_item->dict_image_context,
> +                                        &enc->glz_data.usr);
>      }
> -}
>  
> -gboolean image_encoders_glz_encode_lock(ImageEncoders *enc)
> -{
> -    if (enc->glz_dict) {
> -        pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
> -        return TRUE;
> -    }
> -    return FALSE;
> -}
> +    ret = red_drawable_unref(glz_item->red_drawable);
> +    free(glz_item);
>  
> -void image_encoders_glz_encode_unlock(ImageEncoders *enc)
> -{
> -    if (enc->glz_dict) {
> -        pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
> -    }
> +    return ret;
>  }
>  
>  /*
> - * Remove from the global lz dictionary some glz_drawables that have no
> reference to
> + * Remove from the global lz dictionary some glz_dict_items that have no
> reference to
>   * Drawable (their qxl drawables are released too).
> - * NOTE - the caller should prevent encoding using the dictionary during the
> operation
>   */
> -int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc)
> +int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc,
> int max_to_free)
>  {
>      RingItem *ring_link;
>      int n = 0;
>  
> -    if (!enc) {
> -        return 0;
> +    if (!enc || !enc->glz_dict || max_to_free <= 0) {
> +        return n;
>      }
> -    ring_link = ring_get_head(&enc->glz_drawables);
> -    while ((n < RED_RELEASE_BUNCH_SIZE) && (ring_link != NULL)) {
> -        RedGlzDrawable *glz_drawable = SPICE_CONTAINEROF(ring_link,
> RedGlzDrawable, link);
> -        ring_link = ring_next(&enc->glz_drawables, ring_link);
> -        if (!glz_drawable->has_drawable) {
> -            red_glz_drawable_free(glz_drawable);
> -            n++;
> +
> +    // prevent encoding using the dictionary during the operation
> +    pthread_rwlock_wrlock(&enc->glz_dict->encode_lock);
> +    while (max_to_free > n
> +           && (ring_link = ring_get_head(&enc->glz_orphans)) != NULL) {
> +        GlzDictItem *glz_item = SPICE_CONTAINEROF(ring_link, GlzDictItem,
> retention_link);
> +        if (glz_context_free(glz_item)) {
> +            ++n;
>          }
>      }
> +    pthread_rwlock_unlock(&enc->glz_dict->encode_lock);
>      return n;
>  }
>  
> @@ -613,14 +543,12 @@ void
> image_encoders_free_glz_drawables_to_free(ImageEncoders* enc)
>      if (!enc->glz_dict) {
>          return;
>      }
> -    pthread_mutex_lock(&enc->glz_drawables_inst_to_free_lock);
> -    while ((ring_link = ring_get_head(&enc->glz_drawables_inst_to_free))) {
> -        GlzDrawableInstanceItem *drawable_instance =
> SPICE_CONTAINEROF(ring_link,
> -
> GlzDrawableInstanceItem,
> -                                                                 free_link);
> -        glz_drawable_instance_item_free(drawable_instance);
> +    pthread_mutex_lock(&enc->glz_dict_items_to_free_lock);
> +    while ((ring_link = ring_get_head(&enc->glz_dict_items_to_free))) {
> +        GlzDictItem * glz_item = SPICE_CONTAINEROF(ring_link, GlzDictItem,
> free_link);
> +        glz_context_free(glz_item);
>      }
> -    pthread_mutex_unlock(&enc->glz_drawables_inst_to_free_lock);
> +    pthread_mutex_unlock(&enc->glz_dict_items_to_free_lock);
>  }
>  
>  /* Clear all lz drawables - enforce their removal from the global
>  dictionary.
> @@ -636,31 +564,25 @@ void image_encoders_free_glz_drawables(ImageEncoders
> *enc)
>  
>      // assure no display channel is during global lz encoding
>      pthread_rwlock_wrlock(&glz_dict->encode_lock);
> -    while ((ring_link = ring_get_head(&enc->glz_drawables))) {
> -        RedGlzDrawable *drawable = SPICE_CONTAINEROF(ring_link,
> RedGlzDrawable, link);
> +    while ((ring_link = ring_get_head(&enc->glz_dict_items))) {
> +        GlzDictItem *glz_item = SPICE_CONTAINEROF(ring_link, GlzDictItem,
> glz_context_link);
>          // no need to lock the to_free list, since we assured no other
>          thread is encoding and
>          // thus not other thread access the to_free list of the channel
> -        red_glz_drawable_free(drawable);
> +        glz_context_free(glz_item);
>      }
>      pthread_rwlock_unlock(&glz_dict->encode_lock);
>  }
>  
> -void glz_retention_free_drawables(GlzImageRetention *ret)
> +void glz_retention_destroy(GlzImageRetention *retention)
>  {
> -    RingItem *glz_item, *next_item;
> -    RedGlzDrawable *glz;
> -    SAFE_FOREACH(glz_item, next_item, TRUE, &ret->ring, glz,
> LINK_TO_GLZ(glz_item)) {
> -        red_glz_drawable_free(glz);
> -    }
> -}
> +    RingItem *head;
>  
> -void glz_retention_detach_drawables(GlzImageRetention *ret)
> -{
> -    RingItem *item, *next;
> +    /* append all ring to orphans one */
> +    while ((head = ring_get_head(&retention->ring)) != NULL) {
> +        GlzDictItem *glz_item = SPICE_CONTAINEROF(head, GlzDictItem,
> retention_link);
>  
> -    RING_FOREACH_SAFE(item, next, &ret->ring) {
> -        SPICE_CONTAINEROF(item, RedGlzDrawable, drawable_link)->has_drawable
> = FALSE;
> -        ring_remove(item);
> +        ring_remove(head);
> +        ring_add_before(head, &glz_item->enc->glz_orphans);
>      }
>  }
>  
> @@ -1153,52 +1075,22 @@ int image_encoders_compress_lz4(ImageEncoders *enc,
> SpiceImage *dest,
>  
>  /* if already exists, returns it. Otherwise allocates and adds it (1) to the
>  ring tail
>     in the channel (2) to the Drawable*/
> -static RedGlzDrawable *get_glz_drawable(ImageEncoders *enc, RedDrawable
> *red_drawable,
> -                                        GlzImageRetention *glz_retention)
> +static GlzDictItem *get_glz_context(ImageEncoders *enc, RedDrawable
> *red_drawable,
> +                                   GlzImageRetention *glz_retention)
>  {
> -    RedGlzDrawable *ret;
> -    RingItem *item, *next;
> -
> -    // TODO - I don't really understand what's going on here, so doing the
> technical equivalent
> -    // now that we have multiple glz_dicts, so the only way to go from dcc
> to drawable glz is to go
> -    // over the glz_ring (unless adding some better data structure then a
> ring)
> -    SAFE_FOREACH(item, next, TRUE, &glz_retention->ring, ret,
> LINK_TO_GLZ(item)) {
> -        if (ret->encoders == enc) {
> -            return ret;
> -        }
> -    }
> -
> -    ret = spice_new(RedGlzDrawable, 1);
> -
> -    ret->encoders = enc;
> -    ret->red_drawable = red_drawable_ref(red_drawable);
> -    ret->has_drawable = TRUE;
> -    ret->instances_count = 0;
> -    ring_init(&ret->instances);
> -
> -    ring_item_init(&ret->link);
> -    ring_item_init(&ret->drawable_link);
> -    ring_add_before(&ret->link, &enc->glz_drawables);
> -    ring_add(&glz_retention->ring, &ret->drawable_link);
> -    enc->shared_data->glz_drawable_count++;
> -    return ret;
> -}
> +    GlzDictItem *ret;
>  
> -/* allocates new instance and adds it to instances in the given drawable.
> -   NOTE - the caller should set the glz_instance returned by the encoder by
> itself.*/
> -static GlzDrawableInstanceItem *add_glz_drawable_instance(RedGlzDrawable
> *glz_drawable)
> -{
> -    spice_assert(glz_drawable->instances_count <
> MAX_GLZ_DRAWABLE_INSTANCES);
> -    // NOTE: We assume the additions are performed consecutively, without
> removals in the middle
> -    GlzDrawableInstanceItem *ret = glz_drawable->instances_pool +
> glz_drawable->instances_count;
> -    glz_drawable->instances_count++;
> +    ret = spice_new(GlzDictItem, 1);
>  
> +    ring_item_init(&ret->glz_context_link);
> +    ring_item_init(&ret->retention_link);
>      ring_item_init(&ret->free_link);
> -    ring_item_init(&ret->glz_link);
> -    ring_add(&glz_drawable->instances, &ret->glz_link);
> -    ret->context = NULL;
> -    ret->glz_drawable = glz_drawable;
>  
> +    ret->enc = enc;
> +    ret->red_drawable = red_drawable_ref(red_drawable);
> +
> +    ring_add_before(&ret->glz_context_link, &enc->glz_dict_items);
> +    ring_add(&glz_retention->ring, &ret->retention_link);
>      return ret;
>  }
>  
> @@ -1217,8 +1109,7 @@ int image_encoders_compress_glz(ImageEncoders *enc,
>      GlzData *glz_data = &enc->glz_data;
>      ZlibData *zlib_data;
>      LzImageType type = bitmap_fmt_to_lz_image_type[src->format];
> -    RedGlzDrawable *glz_drawable;
> -    GlzDrawableInstanceItem *glz_drawable_instance;
> +    GlzDictItem *glz_item;
>      int glz_size;
>      int zlib_size;
>  
> @@ -1239,8 +1130,7 @@ int image_encoders_compress_glz(ImageEncoders *enc,
>  
>      encoder_data_init(&glz_data->data);
>  
> -    glz_drawable = get_glz_drawable(enc, red_drawable, glz_retention);
> -    glz_drawable_instance = add_glz_drawable_instance(glz_drawable);
> +    glz_item = get_glz_context(enc, red_drawable, glz_retention);
>  
>      glz_data->data.u.lines_data.chunks = src->data;
>      glz_data->data.u.lines_data.stride = src->stride;
> @@ -1251,8 +1141,8 @@ int image_encoders_compress_glz(ImageEncoders *enc,
>                            (src->flags & SPICE_BITMAP_FLAGS_TOP_DOWN), NULL,
>                            0,
>                            src->stride, glz_data->data.bufs_head->buf.bytes,
>                            sizeof(glz_data->data.bufs_head->buf),
> -                          glz_drawable_instance,
> -                          &glz_drawable_instance->context);
> +                          glz_item,
> +                          &glz_item->dict_image_context);
>  
>      stat_compress_add(&enc->shared_data->glz_stat, start_time, src->stride *
>      src->y, glz_size);
>  
> diff --git a/server/image-encoders.h b/server/image-encoders.h
> index 6542edd..4bac606 100644
> --- a/server/image-encoders.h
> +++ b/server/image-encoders.h
> @@ -45,16 +45,18 @@ void image_encoder_shared_stat_print(const
> ImageEncoderSharedData *shared_data);
>  
>  void image_encoders_init(ImageEncoders *enc, ImageEncoderSharedData
>  *shared_data);
>  void image_encoders_free(ImageEncoders *enc);
> -int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc);
> +int image_encoders_free_some_independent_glz_drawables(ImageEncoders *enc,
> int max_to_free);
>  void image_encoders_free_glz_drawables(ImageEncoders *enc);
>  void image_encoders_free_glz_drawables_to_free(ImageEncoders* enc);
>  gboolean image_encoders_glz_create(ImageEncoders *enc, uint8_t id);
>  void image_encoders_glz_get_restore_data(ImageEncoders *enc,
>                                           uint8_t *out_id,
>                                           GlzEncDictRestoreData *out_data);
> -gboolean image_encoders_glz_encode_lock(ImageEncoders *enc);
> -void image_encoders_glz_encode_unlock(ImageEncoders *enc);
> -void glz_retention_free_drawables(GlzImageRetention *ret);
> -void glz_retention_detach_drawables(GlzImageRetention *ret);
> +
> +/**
> + * Free retention structure so Glz code that drawables
> + * are no more owned by something else.
> + */
> +void glz_retention_destroy(GlzImageRetention *retention);
>  
>  #define RED_COMPRESS_BUF_SIZE (1024 * 64)
>  struct RedCompressBuf {
> @@ -137,14 +139,12 @@ struct GlzImageRetention {
>      Ring ring;
>  };
>  
> -static inline void glz_retention_init(GlzImageRetention *ret)
> +static inline void glz_retention_init(GlzImageRetention *retention)
>  {
> -    ring_init(&ret->ring);
> +    ring_init(&retention->ring);
>  }
>  
>  struct ImageEncoderSharedData {
> -    uint32_t glz_drawable_count;
> -
>      stat_info_t off_stat;
>      stat_info_t lz_stat;
>      stat_info_t glz_stat;
> @@ -184,9 +184,12 @@ struct ImageEncoders {
>      GlzEncoderContext *glz;
>      GlzData glz_data;
>  
> -    Ring glz_drawables;               // all the living lz drawable, ordered
> by encoding time
> -    Ring glz_drawables_inst_to_free;               // list of instances to
> be freed
> -    pthread_mutex_t glz_drawables_inst_to_free_lock;
> +    /** list to all Glz contexts that are owned only by Glz and could
> +     *  be freed in case of OOM */
> +    Ring glz_orphans;
> +    Ring glz_dict_items;               // all the living lz drawable,
> ordered by encoding time
> +    Ring glz_dict_items_to_free;               // list of items to be freed
> +    pthread_mutex_t glz_dict_items_to_free_lock;
>  };
>  
>  typedef struct compress_send_data_t {
> diff --git a/server/red-worker.c b/server/red-worker.c
> index 678856b..b419f91 100644
> --- a/server/red-worker.c
> +++ b/server/red-worker.c
> @@ -97,14 +97,15 @@ static int display_is_connected(RedWorker *worker)
>          &worker->display_channel->common.base));
>  }
>  
> -void red_drawable_unref(RedDrawable *red_drawable)
> +gboolean red_drawable_unref(RedDrawable *red_drawable)
>  {
>      if (--red_drawable->refs) {
> -        return;
> +        return FALSE;
>      }
>      red_qxl_release_resource(red_drawable->qxl,
>      red_drawable->release_info_ext);
>      red_put_drawable(red_drawable);
>      free(red_drawable);
> +    return TRUE;
>  }
>  
>  static int red_process_cursor(RedWorker *worker, int *ring_is_empty)
> diff --git a/server/red-worker.h b/server/red-worker.h
> index dc2ff24..6d3be94 100644
> --- a/server/red-worker.h
> +++ b/server/red-worker.h
> @@ -30,6 +30,6 @@ RedWorker* red_worker_new(QXLInstance *qxl,
>                            const ClientCbs *client_display_cbs);
>  bool       red_worker_run(RedWorker *worker);
>  
> -void red_drawable_unref(RedDrawable *red_drawable);
> +gboolean red_drawable_unref(RedDrawable *red_drawable);
>  
>  #endif
> --
> 2.7.4
> 
> 


More information about the Spice-devel mailing list