aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-03-22 13:01:23 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-03-22 13:01:23 -0400
commite2d64f81a6a6502799e0cf7b7b8aa4b764e657f9 (patch)
tree3bf39a9e56c45cdd902afb9907addb6effd37723 /src
parent151e463c91e0a83a5d277dccae43a7724ffe1e76 (diff)
downloadpostgresql-e2d64f81a6a6502799e0cf7b7b8aa4b764e657f9.tar.gz
postgresql-e2d64f81a6a6502799e0cf7b7b8aa4b764e657f9.zip
Avoid potential deadlock in InitCatCachePhase2().
Opening a catcache's index could require reading from that cache's own catalog, which of course would acquire AccessShareLock on the catalog. So the original coding here risks locking index before heap, which could deadlock against another backend trying to get exclusive locks in the normal order. Because InitCatCachePhase2 is only called when a backend has to start up without a relcache init file, the deadlock was seldom seen in the field. (And by the same token, there's no need to worry about any performance disadvantage; so not much point in trying to distinguish exactly which catalogs have the risk.) Bug report, diagnosis, and patch by Nikhil Sontakke. Additional commentary by me. Back-patch to all supported branches.
Diffstat (limited to 'src')
-rw-r--r--src/backend/utils/cache/catcache.c9
-rw-r--r--src/backend/utils/cache/relcache.c6
2 files changed, 15 insertions, 0 deletions
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index a8658116283..0b27bc034c0 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -24,6 +24,7 @@
#ifdef CATCACHE_STATS
#include "storage/ipc.h" /* for on_proc_exit */
#endif
+#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/memutils.h"
@@ -1009,8 +1010,16 @@ InitCatCachePhase2(CatCache *cache, bool touch_index)
{
Relation idesc;
+ /*
+ * We must lock the underlying catalog before opening the index to
+ * avoid deadlock, since index_open could possibly result in reading
+ * this same catalog, and if anyone else is exclusive-locking this
+ * catalog and index they'll be doing it in that order.
+ */
+ LockRelationOid(cache->cc_reloid, AccessShareLock);
idesc = index_open(cache->cc_indexoid, AccessShareLock);
index_close(idesc, AccessShareLock);
+ UnlockRelationOid(cache->cc_reloid, AccessShareLock);
}
}
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index f6853899b3c..59648a5ddec 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1541,6 +1541,12 @@ RelationClose(Relation relation)
* We assume that at the time we are called, we have at least AccessShareLock
* on the target index. (Note: in the calls from RelationClearRelation,
* this is legitimate because we know the rel has positive refcount.)
+ *
+ * If the target index is an index on pg_class or pg_index, we'd better have
+ * previously gotten at least AccessShareLock on its underlying catalog,
+ * else we are at risk of deadlock against someone trying to exclusive-lock
+ * the heap and index in that order. This is ensured in current usage by
+ * only applying this to indexes being opened or having positive refcount.
*/
static void
RelationReloadClassinfo(Relation relation)