Mesa (gallium-mesa-7.4): mesa: fix potential recursive locking deadlock in _mesa_HashWalk()

Alan Hourihane alanh at kemper.freedesktop.org
Thu Apr 9 10:04:48 PDT 2009


Module: Mesa
Branch: gallium-mesa-7.4
Commit: d805c82068feffda03266855a843de261a45865c
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=d805c82068feffda03266855a843de261a45865c

Author: Brian Paul <brianp at vmware.com>
Date:   Thu Apr  9 10:55:06 2009 -0600

mesa: fix potential recursive locking deadlock in _mesa_HashWalk()

If the walk callback called _mesa_HashRemove() we'd deadlock.

(cherry picked from master, commit deff09921563419a77bd1aad0054afa34214ed1a)

---

 src/mesa/main/hash.c |   18 ++++++++++++++----
 1 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/mesa/main/hash.c b/src/mesa/main/hash.c
index 976f9d9..08c6456 100644
--- a/src/mesa/main/hash.c
+++ b/src/mesa/main/hash.c
@@ -63,6 +63,7 @@ struct _mesa_HashTable {
    struct HashEntry *Table[TABLE_SIZE];  /**< the lookup table */
    GLuint MaxKey;                        /**< highest key inserted so far */
    _glthread_Mutex Mutex;                /**< mutual exclusion lock */
+   _glthread_Mutex WalkMutex;            /**< for _mesa_HashWalk() */
    GLboolean InDeleteAll;                /**< Debug check */
 };
 
@@ -79,6 +80,7 @@ _mesa_NewHashTable(void)
    struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);
    if (table) {
       _glthread_INIT_MUTEX(table->Mutex);
+      _glthread_INIT_MUTEX(table->WalkMutex);
    }
    return table;
 }
@@ -111,6 +113,7 @@ _mesa_DeleteHashTable(struct _mesa_HashTable *table)
       }
    }
    _glthread_DESTROY_MUTEX(table->Mutex);
+   _glthread_DESTROY_MUTEX(table->WalkMutex);
    _mesa_free(table);
 }
 
@@ -285,6 +288,11 @@ _mesa_HashDeleteAll(struct _mesa_HashTable *table,
 
 /**
  * Walk over all entries in a hash table, calling callback function for each.
+ * Note: we use a separate mutex in this function to avoid a recursive
+ * locking deadlock (in case the callback calls _mesa_HashRemove()) and to
+ * prevent multiple threads/contexts from getting tangled up.
+ * A lock-less version of this function could be used when the table will
+ * not be modified.
  * \param table  the hash table to walk
  * \param callback  the callback function
  * \param userData  arbitrary pointer to pass along to the callback
@@ -300,14 +308,16 @@ _mesa_HashWalk(const struct _mesa_HashTable *table,
    GLuint pos;
    ASSERT(table);
    ASSERT(callback);
-   _glthread_LOCK_MUTEX(table2->Mutex);
+   _glthread_LOCK_MUTEX(table2->WalkMutex);
    for (pos = 0; pos < TABLE_SIZE; pos++) {
-      struct HashEntry *entry;
-      for (entry = table->Table[pos]; entry; entry = entry->Next) {
+      struct HashEntry *entry, *next;
+      for (entry = table->Table[pos]; entry; entry = next) {
+         /* save 'next' pointer now in case the callback deletes the entry */
+         next = entry->Next;
          callback(entry->Key, entry->Data, userData);
       }
    }
-   _glthread_UNLOCK_MUTEX(table2->Mutex);
+   _glthread_UNLOCK_MUTEX(table2->WalkMutex);
 }
 
 



More information about the mesa-commit mailing list