From 2ea11eace69fe5a2d88f374a253a3deb05804f2d Mon Sep 17 00:00:00 2001 From: Robin Watts Date: Mon, 22 Apr 2013 15:14:38 +0100 Subject: Fix various multi-threading problems with the store. When resizing the hash table, we have a special case to cope with someone else resizing the table before we get a chance to. In this rare situation we were unlocking (regardless of whether we should have been), and failing to relock. Fixed here. When storing an item, I recently changed the code to put the new item into the hash before ensuring that we had enough space. This change was motivated by us wanting not to evict to make room only to find that we didn't need the room as there was a duplicate entry anyway. In so doing, this opened up a potential race condition where another thread could 'find' the item from the hash before it had been filled out. To solve this, we move the "filling out" of the item entries earlier in the function. Another problem is found due to the same block of code; as soon as a new item is put into the hash, it can be found elsewhere. Any attempt to manipulate it's linked list will fail. We therefore set all new items with their prev/next pointers pointing to themselves, enabling us to spot this special case and avoid corrupting the linked list. --- fitz/base_hash.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fitz/base_hash.c') diff --git a/fitz/base_hash.c b/fitz/base_hash.c index 48bab138..037988cb 100644 --- a/fitz/base_hash.c +++ b/fitz/base_hash.c @@ -141,6 +141,8 @@ do_hash_insert(fz_context *ctx, fz_hash_table *table, void *key, void *val, unsi } } +/* Entered with the lock taken, held throughout and at exit, UNLESS the lock + * is the alloc lock in which case it may be momentarily dropped. */ static void fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) { @@ -166,8 +168,11 @@ fz_resize_hash(fz_context *ctx, fz_hash_table *table, int newsize) if (table->size >= newsize) { /* Someone else fixed it before we could lock! */ - fz_unlock(ctx, table->lock); + if (table->lock == FZ_LOCK_ALLOC) + fz_unlock(ctx, table->lock); fz_free(ctx, newents); + if (table->lock == FZ_LOCK_ALLOC) + fz_lock(ctx, table->lock); return; } } -- cgit v1.2.3