[vlc-commits] picture_pool: no longer muck with the reference counts, fix races

Rémi Denis-Courmont git at videolan.org
Sat Nov 1 13:31:08 CET 2014


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Sat Nov  1 10:10:37 2014 +0200| [5c9821d845f3b5f3bdd1bca9449335523a044081] | committer: Rémi Denis-Courmont

picture_pool: no longer muck with the reference counts, fix races

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=5c9821d845f3b5f3bdd1bca9449335523a044081
---

 src/misc/picture_pool.c |   79 +++++++++++++++++++++++++++++++++++------------
 1 file changed, 59 insertions(+), 20 deletions(-)

diff --git a/src/misc/picture_pool.c b/src/misc/picture_pool.c
index 9cb7dfc..135b5d6 100644
--- a/src/misc/picture_pool.c
+++ b/src/misc/picture_pool.c
@@ -40,6 +40,7 @@
 struct picture_gc_sys_t {
     picture_pool_t *pool;
     picture_t *picture;
+    bool in_use;
     int64_t tick;
 };
 
@@ -89,6 +90,11 @@ static void picture_pool_ReleasePicture(picture_t *picture)
     if (pool->pic_unlock != NULL)
         pool->pic_unlock(picture);
 
+    vlc_mutex_lock(&pool->lock);
+    assert(sys->in_use);
+    sys->in_use = false;
+    vlc_mutex_unlock(&pool->lock);
+
     picture_pool_Release(pool);
 }
 
@@ -101,6 +107,7 @@ static picture_t *picture_pool_ClonePicture(picture_pool_t *pool,
 
     sys->pool = pool;
     sys->picture = picture;
+    sys->in_use = false;
     sys->tick = 0;
 
     picture_resource_t res = {
@@ -231,62 +238,94 @@ void picture_pool_Delete(picture_pool_t *pool)
 
 picture_t *picture_pool_Get(picture_pool_t *pool)
 {
+    vlc_mutex_lock(&pool->lock);
+    assert(pool->refs > 0);
+
     for (unsigned i = 0; i < pool->picture_count; i++) {
         picture_t *picture = pool->picture[i];
-        uintptr_t refs = 0;
+        picture_gc_sys_t *sys = picture->gc.p_sys;
+        uint64_t tick;
 
-        if (!atomic_compare_exchange_strong(&picture->gc.refcount, &refs, 1))
+        if (sys->in_use)
             continue;
 
+        pool->refs++;
+        tick = ++pool->tick;
+        sys->in_use = true;
+        vlc_mutex_unlock(&pool->lock);
+
         if (pool->pic_lock != NULL && pool->pic_lock(picture) != 0) {
-            atomic_store(&picture->gc.refcount, 0);
+            vlc_mutex_lock(&pool->lock);
+            sys->in_use = false;
+            pool->refs--;
+            vlc_mutex_unlock(&pool->lock);
             continue;
         }
 
-        vlc_mutex_lock(&pool->lock);
-        pool->refs++;
-        picture->gc.p_sys->tick = pool->tick++;
-        vlc_mutex_unlock(&pool->lock);
+        sys->tick = tick;
 
+        assert(atomic_load(&picture->gc.refcount) == 0);
+        atomic_init(&picture->gc.refcount, 1);
         picture->p_next = NULL;
         return picture;
     }
+
+    vlc_mutex_unlock(&pool->lock);
     return NULL;
 }
 
 void picture_pool_Reset(picture_pool_t *pool)
 {
+retry:
+    vlc_mutex_lock(&pool->lock);
+    assert(pool->refs > 0);
+
     for (unsigned i = 0; i < pool->picture_count; i++) {
         picture_t *picture = pool->picture[i];
-        if (atomic_load(&picture->gc.refcount) > 0) {
-            if (pool->pic_unlock != NULL)
-                pool->pic_unlock(picture);
+        picture_gc_sys_t *sys = picture->gc.p_sys;
+
+        if (sys->in_use) {
+            vlc_mutex_unlock(&pool->lock);
+            picture_Release(picture);
+
+            goto retry;
         }
-        atomic_store(&picture->gc.refcount, 0);
     }
+    vlc_mutex_unlock(&pool->lock);
 }
 
 void picture_pool_NonEmpty(picture_pool_t *pool)
 {
     picture_t *oldest = NULL;
+    uint64_t tick = 0;
+
+    vlc_mutex_lock(&pool->lock);
+    assert(pool->refs > 0);
 
     for (unsigned i = 0; i < pool->picture_count; i++) {
         picture_t *picture = pool->picture[i];
-        if (atomic_load(&picture->gc.refcount) == 0)
+        picture_gc_sys_t *sys = picture->gc.p_sys;
+
+        if (!sys->in_use) {
+            vlc_mutex_unlock(&pool->lock);
             return; /* Nothing to do */
+        }
 
-        if (oldest == NULL || picture->gc.p_sys->tick < oldest->gc.p_sys->tick)
+        if (picture->gc.p_sys->tick < tick) {
             oldest = picture;
+            tick = picture->gc.p_sys->tick;
+        }
     }
 
-    if (oldest == NULL)
-        return; /* Cannot fix! */
-
-    if (atomic_load(&oldest->gc.refcount) > 0) {
-        if (pool->pic_unlock != NULL)
-            pool->pic_unlock(oldest);
+    if (oldest != NULL) {
+        while (oldest->gc.p_sys->in_use) {
+            vlc_mutex_unlock(&pool->lock);
+            picture_Release(oldest);
+            vlc_mutex_lock(&pool->lock);
+        }
     }
-    atomic_store(&oldest->gc.refcount, 0);
+
+    vlc_mutex_unlock(&pool->lock);
 }
 
 int picture_pool_GetSize(picture_pool_t *pool)



More information about the vlc-commits mailing list