summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Paul <brianp@vmware.com>2009-04-09 10:55:06 -0600
committerBrian Paul <brianp@vmware.com>2009-04-09 10:55:06 -0600
commitd805c82068feffda03266855a843de261a45865c (patch)
tree61ced1e4ba4ca9a6eff09c0bbd4355e66daed1f8
parent6c3bec4387d7483d4b9652c7eb9313e3ec415898 (diff)
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)
-rw-r--r--src/mesa/main/hash.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/src/mesa/main/hash.c b/src/mesa/main/hash.c
index 976f9d999b7..08c64568c83 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);
}