summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Haas2016-01-30 13:35:56 +0000
committerRobert Haas2016-02-01 21:13:15 +0000
commit874b3b86a898d07beea6e2143ab9827153cc8fc0 (patch)
tree9634e659e5ce849d59d0d1440f806b5fab6135da
parent1b4d0cffd00248df5c1c57239284e985ba82cf86 (diff)
Hack on lock transfer stuff.gathertest
XXX: I don't think this is the right design. It will require the workers to wait for the leader to perform the associated lock acquisitions, and then the leader will need to tell them, afterwards, that they can go exit. There's no obvious way to make that signalling work. Instead, I wonder if we shouldn't including bookkeeping information in the PROCLOCK structures somehow, so that the worker actually reassigns the locks to the leader directly. However, that has a couple of problems. One is that the leader might pick a bad time to die, orphaning locks. Another is that the leader wouldn't know how to associate the locks with its own resource owners. Hmm, need to think more about this.
-rw-r--r--src/backend/access/transam/parallel.c32
-rw-r--r--src/backend/storage/lmgr/lock.c45
-rw-r--r--src/include/storage/lock.h3
3 files changed, 79 insertions, 1 deletions
diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c
index 3915750419..31b23a785b 100644
--- a/src/backend/access/transam/parallel.c
+++ b/src/backend/access/transam/parallel.c
@@ -807,6 +807,33 @@ HandleParallelMessage(ParallelContext *pcxt, int i, StringInfo msg)
break;
}
+ case 'L': /* Lock information */
+ {
+ int i;
+
+ Assert(msg->len > 1 && (msg->len % sizeof(LOCALLOCKTAG)) == 1);
+
+ for (i = 1; i < msg->len; i += sizeof(LOCALLOCKTAG))
+ {
+ LOCALLOCKTAG locallocktag;
+
+ memcpy((char *) &locallocktag, &msg->data[i], sizeof(LOCALLOCKTAG));
+ /* XXX. Now what? */
+#if 0
+ ereport(NOTICE,
+ (errmsg("worker has lock %u/%u/%u/%u type %u method %u mode %u",
+ locallocktag.lock.locktag_field1,
+ locallocktag.lock.locktag_field2,
+ locallocktag.lock.locktag_field3,
+ locallocktag.lock.locktag_field4,
+ locallocktag.lock.locktag_type,
+ locallocktag.lock.locktag_lockmethodid,
+ locallocktag.mode)));
+#endif
+ }
+ break;
+ }
+
case 'X': /* Terminate, indicating clean exit */
{
pfree(pcxt->worker[i].error_mqh);
@@ -1038,6 +1065,11 @@ ParallelWorkerMain(Datum main_arg)
/* Must pop active snapshot so resowner.c doesn't complain. */
PopActiveSnapshot();
+ /* Send a message to the leader with the heavyweight locks we still retain. */
+ pq_beginmessage(&msgbuf, 'L');
+ if (GetMyLocks(&msgbuf) != 0)
+ pq_endmessage(&msgbuf);
+
/* Shut down the parallel-worker transaction. */
EndParallelWorkerTransaction();
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 7c7f250878..c308531ff1 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -707,7 +707,6 @@ LockAcquireExtended(const LOCKTAG *locktag,
lockMethodTable = LockMethods[lockmethodid];
if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes)
elog(ERROR, "unrecognized lock mode: %d", lockmode);
- Assert(!IsInParallelMode() || MyProc->lockGroupLeader != NULL);
if (RecoveryInProgress() && !InRecovery &&
(locktag->locktag_type == LOCKTAG_OBJECT ||
@@ -3538,6 +3537,50 @@ GetLockStatusData(void)
}
/*
+ * GetMyLocks -- Write all locks we hold into the provided StringInfo.
+ *
+ * For each lock, we write the LOCKTAG and LOCKMODE, a concept conveniently
+ * encapsulated by the LOCALLOCKTAG data type.
+ *
+ * We return the number of locks written.
+ */
+int
+GetMyLocks(StringInfo buf)
+{
+ HASH_SEQ_STATUS status;
+ LOCALLOCK *locallock;
+ int count = 0;
+
+ /*
+ * We choose to implement this by iterating over the local lock table.
+ * This carries the intrinsic risk that if any our lock management code
+ * is buggy, we might enumerate a different set of locks here than the
+ * shared lock table believes we actually hold. We could eliminate that
+ * risk by doing this based on the shared memory data structures rather
+ * than our local bookkeeping, but that would require acquiring every lock
+ * manager partition lock in turn. We prefer to minimize contention.
+ */
+ hash_seq_init(&status, LockMethodLocalHash);
+
+ while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
+ {
+ /*
+ * Normally we remove entries from the hash table when nLocks == 0,
+ * but not if we run out of shared memory while setting up the lock.
+ * Skip any such unheld locks.
+ */
+ if (locallock->nLocks == 0)
+ continue;
+
+ appendBinaryStringInfo(buf, (char *) &locallock->tag,
+ sizeof(LOCALLOCKTAG));
+ ++count;
+ }
+
+ return count;
+}
+
+/*
* Returns a list of currently held AccessExclusiveLocks, for use by
* LogStandbySnapshot(). The result is a palloc'd array,
* with the number of elements returned into *nlocks.
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 6b4e3655f8..e54c613929 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -23,6 +23,8 @@
#include "storage/lwlock.h"
#include "storage/shmem.h"
+/* avoid including lib/stringinfo.h */
+struct StringInfoData;
/* struct PGPROC is declared in proc.h, but must forward-reference it */
typedef struct PGPROC PGPROC;
@@ -520,6 +522,7 @@ extern void GrantAwaitedLock(void);
extern void RemoveFromWaitQueue(PGPROC *proc, uint32 hashcode);
extern Size LockShmemSize(void);
extern LockData *GetLockStatusData(void);
+extern int GetMyLocks(struct StringInfoData *buf);
extern xl_standby_lock *GetRunningTransactionLocks(int *nlocks);
extern const char *GetLockmodeName(LOCKMETHODID lockmethodid, LOCKMODE mode);