diff options
Diffstat (limited to 'src/backend/access/heap/heapam.c')
-rw-r--r-- | src/backend/access/heap/heapam.c | 83 |
1 files changed, 62 insertions, 21 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 8ac0f8a5134..d768b9b061c 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -95,7 +95,7 @@ static void GetMultiXactIdHintBits(MultiXactId multi, uint16 *new_infomask, static TransactionId MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask); static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, - LockTupleMode lockmode); + LockTupleMode lockmode, bool *current_is_member); static void MultiXactIdWait(MultiXactId multi, MultiXactStatus status, uint16 infomask, Relation rel, ItemPointer ctid, XLTW_Oper oper, int *remaining); @@ -2547,15 +2547,20 @@ l1: */ if (infomask & HEAP_XMAX_IS_MULTI) { - /* wait for multixact */ + bool current_is_member = false; + if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask, - LockTupleExclusive)) + LockTupleExclusive, ¤t_is_member)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); - /* acquire tuple lock, if necessary */ - heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive, - LockWaitBlock, &have_tuple_lock); + /* + * Acquire the lock, if necessary (but skip it when we're + * requesting a lock and already have one; avoids deadlock). + */ + if (!current_is_member) + heap_acquire_tuplock(relation, &(tp.t_self), LockTupleExclusive, + LockWaitBlock, &have_tuple_lock); /* wait for multixact */ MultiXactIdWait((MultiXactId) xwait, MultiXactStatusUpdate, infomask, @@ -3126,15 +3131,20 @@ l2: { TransactionId update_xact; int remain; + bool current_is_member = false; if (DoesMultiXactIdConflict((MultiXactId) xwait, infomask, - *lockmode)) + *lockmode, ¤t_is_member)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); - /* acquire tuple lock, if necessary */ - heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode, - LockWaitBlock, &have_tuple_lock); + /* + * Acquire the lock, if necessary (but skip it when we're + * requesting a lock and already have one; avoids deadlock). + */ + if (!current_is_member) + heap_acquire_tuplock(relation, &(oldtup.t_self), *lockmode, + LockWaitBlock, &have_tuple_lock); /* wait for multixact */ MultiXactIdWait((MultiXactId) xwait, mxact_status, infomask, @@ -3981,6 +3991,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, new_infomask, new_infomask2; bool first_time = true; + bool skip_tuple_lock = false; bool have_tuple_lock = false; bool cleared_all_frozen = false; @@ -4081,6 +4092,21 @@ l3: result = TM_Ok; goto out_unlocked; } + else + { + /* + * Disable acquisition of the heavyweight tuple lock. + * Otherwise, when promoting a weaker lock, we might + * deadlock with another locker that has acquired the + * heavyweight tuple lock and is waiting for our + * transaction to finish. + * + * Note that in this case we still need to wait for + * the multixact if required, to avoid acquiring + * conflicting locks. + */ + skip_tuple_lock = true; + } } if (members) @@ -4235,7 +4261,7 @@ l3: if (infomask & HEAP_XMAX_IS_MULTI) { if (!DoesMultiXactIdConflict((MultiXactId) xwait, infomask, - mode)) + mode, NULL)) { /* * No conflict, but if the xmax changed under us in the @@ -4312,13 +4338,15 @@ l3: /* * Acquire tuple lock to establish our priority for the tuple, or * die trying. LockTuple will release us when we are next-in-line - * for the tuple. We must do this even if we are share-locking. + * for the tuple. We must do this even if we are share-locking, + * but not if we already have a weaker lock on the tuple. * * If we are forced to "start over" below, we keep the tuple lock; * this arranges that we stay at the head of the line while * rechecking tuple state. */ - if (!heap_acquire_tuplock(relation, tid, mode, wait_policy, + if (!skip_tuple_lock && + !heap_acquire_tuplock(relation, tid, mode, wait_policy, &have_tuple_lock)) { /* @@ -6516,10 +6544,13 @@ HeapTupleGetUpdateXid(HeapTupleHeader tuple) * tuple lock of the given strength? * * The passed infomask pairs up with the given multixact in the tuple header. + * + * If current_is_member is not NULL, it is set to 'true' if the current + * transaction is a member of the given multixact. */ static bool DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, - LockTupleMode lockmode) + LockTupleMode lockmode, bool *current_is_member) { int nmembers; MultiXactMember *members; @@ -6540,15 +6571,24 @@ DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, TransactionId memxid; LOCKMODE memlockmode; - memlockmode = LOCKMODE_from_mxstatus(members[i].status); + if (result && (current_is_member == NULL || *current_is_member)) + break; - /* ignore members that don't conflict with the lock we want */ - if (!DoLockModesConflict(memlockmode, wanted)) - continue; + memlockmode = LOCKMODE_from_mxstatus(members[i].status); - /* ignore members from current xact */ + /* ignore members from current xact (but track their presence) */ memxid = members[i].xid; if (TransactionIdIsCurrentTransactionId(memxid)) + { + if (current_is_member != NULL) + *current_is_member = true; + continue; + } + else if (result) + continue; + + /* ignore members that don't conflict with the lock we want */ + if (!DoLockModesConflict(memlockmode, wanted)) continue; if (ISUPDATE_from_mxstatus(members[i].status)) @@ -6567,10 +6607,11 @@ DoesMultiXactIdConflict(MultiXactId multi, uint16 infomask, /* * Whatever remains are either live lockers that conflict with our * wanted lock, and updaters that are not aborted. Those conflict - * with what we want, so return true. + * with what we want. Set up to return true, but keep going to + * look for the current transaction among the multixact members, + * if needed. */ result = true; - break; } pfree(members); } |