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

Frediano Ziglio fziglio at redhat.com
Tue Oct 18 09:28:25 UTC 2016


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