On Sat, 2007-04-21 at 17:56 -0400, Neil Conway wrote:
> Right, I'm envisioning doing a conditional LockAcquire and then
> heap_open() / heap_getnext() by hand. That will be relatively slow, but
> code that emits a deadlock error message is almost by definition not
> performance critical.
... although it turns out you'd need to conditionally lock a *lot* of
system catalogs to guarantee that you're not going to block on a lock at
some point. Needless to say, that approach would be pretty ugly and
fragile.
> BTW, another alternative would be to set a global variable instructing
> LockAcquire() to not block waiting for a lock; instead, it would
> longjmp(), a la elog(ERROR). You could even construct something similar
> to PG_TRY()
Attached is a very quick hack of a patch to do this.
-Neil
Index: src/backend/storage/lmgr/deadlock.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/storage/lmgr/deadlock.c,v
retrieving revision 1.47
diff -c -p -r1.47 deadlock.c
*** src/backend/storage/lmgr/deadlock.c 20 Apr 2007 20:15:52 -0000 1.47
--- src/backend/storage/lmgr/deadlock.c 21 Apr 2007 23:43:13 -0000
***************
*** 25,34 ****
--- 25,39 ----
*/
#include "postgres.h"
+ #include "access/htup.h"
+ #include "commands/dbcommands.h"
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "storage/proc.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
#include "utils/memutils.h"
+ #include "utils/syscache.h"
/* One edge in the waits-for graph */
*************** static bool FindLockCycleRecurse(PGPROC
*** 73,78 ****
--- 78,85 ----
static bool ExpandConstraints(EDGE *constraints, int nConstraints);
static bool TopoSort(LOCK *lock, EDGE *constraints, int nConstraints,
PGPROC **ordering);
+ static char *format_relation(Oid reloid);
+ static const char *format_database(Oid dboid);
#ifdef DEBUG_DEADLOCK
static void PrintLockQueue(LOCK *lock, const char *info);
*************** DescribeLockTag(StringInfo buf, const LO
*** 846,875 ****
{
case LOCKTAG_RELATION:
appendStringInfo(buf,
! _("relation %u of database %u"),
! lock->locktag_field2,
! lock->locktag_field1);
break;
case LOCKTAG_RELATION_EXTEND:
appendStringInfo(buf,
! _("extension of relation %u of database %u"),
! lock->locktag_field2,
! lock->locktag_field1);
break;
case LOCKTAG_PAGE:
appendStringInfo(buf,
! _("page %u of relation %u of database %u"),
lock->locktag_field3,
! lock->locktag_field2,
! lock->locktag_field1);
break;
case LOCKTAG_TUPLE:
appendStringInfo(buf,
! _("tuple (%u,%u) of relation %u of database %u"),
lock->locktag_field3,
lock->locktag_field4,
! lock->locktag_field2,
! lock->locktag_field1);
break;
case LOCKTAG_TRANSACTION:
appendStringInfo(buf,
--- 853,882 ----
{
case LOCKTAG_RELATION:
appendStringInfo(buf,
! _("relation %s of database %s"),
! format_relation(lock->locktag_field2),
! format_database(lock->locktag_field1));
break;
case LOCKTAG_RELATION_EXTEND:
appendStringInfo(buf,
! _("extension of relation %s of database %s"),
! format_relation(lock->locktag_field2),
! format_database(lock->locktag_field1));
break;
case LOCKTAG_PAGE:
appendStringInfo(buf,
! _("page %u of relation %s of database %s"),
lock->locktag_field3,
! format_relation(lock->locktag_field2),
! format_database(lock->locktag_field1));
break;
case LOCKTAG_TUPLE:
appendStringInfo(buf,
! _("tuple (%u,%u) of relation %s of database %s"),
lock->locktag_field3,
lock->locktag_field4,
! format_relation(lock->locktag_field2),
! format_database(lock->locktag_field1));
break;
case LOCKTAG_TRANSACTION:
appendStringInfo(buf,
*************** DescribeLockTag(StringInfo buf, const LO
*** 878,887 ****
break;
case LOCKTAG_OBJECT:
appendStringInfo(buf,
! _("object %u of class %u of database %u"),
lock->locktag_field3,
lock->locktag_field2,
! lock->locktag_field1);
break;
case LOCKTAG_USERLOCK:
/* reserved for old contrib code, now on pgfoundry */
--- 885,894 ----
break;
case LOCKTAG_OBJECT:
appendStringInfo(buf,
! _("object %u of class %u of database %s"),
lock->locktag_field3,
lock->locktag_field2,
! format_database(lock->locktag_field1));
break;
case LOCKTAG_USERLOCK:
/* reserved for old contrib code, now on pgfoundry */
*************** DescribeLockTag(StringInfo buf, const LO
*** 907,912 ****
--- 914,977 ----
}
}
+ static char *
+ format_relation(Oid reloid)
+ {
+ char *result;
+
+ PG_LOCK_NOWAIT();
+ {
+ HeapTuple class_tup;
+
+ class_tup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(reloid),
+ 0, 0, 0);
+
+ if (HeapTupleIsValid(class_tup))
+ {
+ Form_pg_class classform = (Form_pg_class) GETSTRUCT(class_tup);
+ char *relname;
+ char *nspname;
+
+ relname = NameStr(classform->relname);
+ nspname = get_namespace_name(classform->relnamespace);
+ result = quote_qualified_identifier(nspname, relname);
+
+ ReleaseSysCache(class_tup);
+ return result;
+ }
+ }
+ PG_LOCK_FAILED();
+ {
+ ;
+ }
+ PG_END_LOCK_NOWAIT();
+
+ /*
+ * Either we failed to acquire a necessary lmgr lock, or else the
+ * rel isn't in our database, so just return the OID
+ */
+ result = (char *) palloc(NAMEDATALEN);
+ snprintf(result, NAMEDATALEN, "%u", reloid);
+ return result;
+ }
+
+ static const char *
+ format_database(Oid dboid)
+ {
+ PG_LOCK_NOWAIT();
+ {
+ return quote_identifier(get_database_name(dboid));
+ }
+ PG_LOCK_FAILED();
+ {
+ char *result = (char *) palloc(NAMEDATALEN);
+ snprintf(result, NAMEDATALEN, "%u", dboid);
+ return result;
+ }
+ PG_END_LOCK_NOWAIT();
+ }
+
/*
* Report a detected DS_HARD_DEADLOCK, with available details.
*/
Index: src/backend/storage/lmgr/lock.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/storage/lmgr/lock.c,v
retrieving revision 1.176
diff -c -p -r1.176 lock.c
*** src/backend/storage/lmgr/lock.c 1 Feb 2007 19:10:28 -0000 1.176
--- src/backend/storage/lmgr/lock.c 21 Apr 2007 23:38:32 -0000
***************
*** 46,51 ****
--- 46,54 ----
/* This configuration variable is used to set the lock table size */
int max_locks_per_xact; /* set by guc.c */
+ int lock_no_wait_count = 0;
+ sigjmp_buf *lock_no_wait_stack = NULL;
+
#define NLOCKENTS() \
mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))
*************** LockAcquire(const LOCKTAG *locktag,
*** 752,758 ****
* blocking, remove useless table entries and return NOT_AVAIL without
* waiting.
*/
! if (dontWait)
{
if (proclock->holdMask == 0)
{
--- 755,761 ----
* blocking, remove useless table entries and return NOT_AVAIL without
* waiting.
*/
! if (dontWait || lock_no_wait_count > 0)
{
if (proclock->holdMask == 0)
{
*************** LockAcquire(const LOCKTAG *locktag,
*** 775,781 ****
LWLockRelease(partitionLock);
if (locallock->nLocks == 0)
RemoveLocalLock(locallock);
! return LOCKACQUIRE_NOT_AVAIL;
}
/*
--- 778,794 ----
LWLockRelease(partitionLock);
if (locallock->nLocks == 0)
RemoveLocalLock(locallock);
!
! if (dontWait)
! return LOCKACQUIRE_NOT_AVAIL;
!
! /*
! * If we're inside a PG_LOCK_NOWAIT() block, longjmp
! * instead of waiting.
! */
! if (lock_no_wait_stack == NULL)
! elog(FATAL, "lmgr nowait lock stack corrupted");
! siglongjmp(*lock_no_wait_stack, 1);
}
/*
Index: src/include/storage/lock.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/storage/lock.h,v
retrieving revision 1.104
diff -c -p -r1.104 lock.h
*** src/include/storage/lock.h 3 Mar 2007 18:46:40 -0000 1.104
--- src/include/storage/lock.h 21 Apr 2007 23:34:43 -0000
*************** extern void DumpLocks(PGPROC *proc);
*** 462,465 ****
--- 462,491 ----
extern void DumpAllLocks(void);
#endif
+ #define PG_LOCK_NOWAIT() \
+ do { \
+ sigjmp_buf *save_lock_stack = lock_no_wait_stack; \
+ int save_lock_count = lock_no_wait_count; \
+ sigjmp_buf local_sigjmp_buf; \
+ if (sigsetjmp(local_sigjmp_buf, 0) == 0) \
+ { \
+ lock_no_wait_stack = &local_sigjmp_buf; \
+ lock_no_wait_count++
+
+ #define PG_LOCK_FAILED() \
+ } \
+ else \
+ { \
+ lock_no_wait_stack = save_lock_stack; \
+ lock_no_wait_count = save_lock_count;
+
+ #define PG_END_LOCK_NOWAIT() \
+ } \
+ lock_no_wait_stack = save_lock_stack; \
+ lock_no_wait_count = save_lock_count; \
+ } while (0)
+
+ extern int lock_no_wait_count;
+ extern sigjmp_buf *lock_no_wait_stack;
+
#endif /* LOCK_H */
---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?
http://www.postgresql.org/docs/faq