12
12
*
13
13
*
14
14
* IDENTIFICATION
15
- * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/deadlock.c,v 1.15 2002/11/01 00:40:23 tgl Exp $
15
+ * $Header: /cvsroot/pgsql/src/backend/storage/lmgr/deadlock.c,v 1.16 2003/01/16 21:01:44 tgl Exp $
16
16
*
17
17
* Interface:
18
18
*
19
19
* DeadLockCheck()
20
+ * DeadLockReport()
21
+ * RememberSimpleDeadLock()
20
22
* InitDeadLockChecking()
21
23
*
22
24
*-------------------------------------------------------------------------
@@ -45,12 +47,27 @@ typedef struct
45
47
int nProcs ;
46
48
} WAIT_ORDER ;
47
49
50
+ /*
51
+ * Information saved about each edge in a detected deadlock cycle. This
52
+ * is used to print a diagnostic message upon failure.
53
+ *
54
+ * Note: because we want to examine this info after releasing the LockMgrLock,
55
+ * we can't just store LOCK and PGPROC pointers; we must extract out all the
56
+ * info we want to be able to print.
57
+ */
58
+ typedef struct
59
+ {
60
+ LOCKTAG locktag ; /* ID of awaited lock object */
61
+ LOCKMODE lockmode ; /* type of lock we're waiting for */
62
+ int pid ; /* PID of blocked backend */
63
+ } DEADLOCK_INFO ;
64
+
48
65
49
66
static bool DeadLockCheckRecurse (PGPROC * proc );
50
67
static bool TestConfiguration (PGPROC * startProc );
51
68
static bool FindLockCycle (PGPROC * checkProc ,
52
69
EDGE * softEdges , int * nSoftEdges );
53
- static bool FindLockCycleRecurse (PGPROC * checkProc ,
70
+ static bool FindLockCycleRecurse (PGPROC * checkProc , int depth ,
54
71
EDGE * softEdges , int * nSoftEdges );
55
72
static bool ExpandConstraints (EDGE * constraints , int nConstraints );
56
73
static bool TopoSort (LOCK * lock , EDGE * constraints , int nConstraints ,
@@ -88,6 +105,8 @@ static int maxCurConstraints;
88
105
static EDGE * possibleConstraints ;
89
106
static int nPossibleConstraints ;
90
107
static int maxPossibleConstraints ;
108
+ static DEADLOCK_INFO * deadlockDetails ;
109
+ static int nDeadlockDetails ;
91
110
92
111
93
112
/*
@@ -110,8 +129,10 @@ InitDeadLockChecking(void)
110
129
111
130
/*
112
131
* FindLockCycle needs at most MaxBackends entries in visitedProcs[]
132
+ * and deadlockDetails[].
113
133
*/
114
134
visitedProcs = (PGPROC * * ) palloc (MaxBackends * sizeof (PGPROC * ));
135
+ deadlockDetails = (DEADLOCK_INFO * ) palloc (MaxBackends * sizeof (DEADLOCK_INFO ));
115
136
116
137
/*
117
138
* TopoSort needs to consider at most MaxBackends wait-queue entries,
@@ -176,6 +197,10 @@ InitDeadLockChecking(void)
176
197
* table to have a different masterLock, all locks that can block had
177
198
* better use the same LWLock, else this code will not be adequately
178
199
* interlocked!
200
+ *
201
+ * On failure, deadlock details are recorded in deadlockDetails[] for
202
+ * subsequent printing by DeadLockReport(). That activity is separate
203
+ * because we don't want to do it while holding the master lock.
179
204
*/
180
205
bool
181
206
DeadLockCheck (PGPROC * proc )
@@ -190,7 +215,19 @@ DeadLockCheck(PGPROC *proc)
190
215
191
216
/* Search for deadlocks and possible fixes */
192
217
if (DeadLockCheckRecurse (proc ))
218
+ {
219
+ /*
220
+ * Call FindLockCycle one more time, to record the correct
221
+ * deadlockDetails[] for the basic state with no rearrangements.
222
+ */
223
+ int nSoftEdges ;
224
+
225
+ nWaitOrders = 0 ;
226
+ if (!FindLockCycle (proc , possibleConstraints , & nSoftEdges ))
227
+ elog (FATAL , "DeadLockCheck: deadlock seems to have disappeared" );
228
+
193
229
return true; /* cannot find a non-deadlocked state */
230
+ }
194
231
195
232
/* Apply any needed rearrangements of wait queues */
196
233
for (i = 0 ; i < nWaitOrders ; i ++ )
@@ -357,9 +394,12 @@ TestConfiguration(PGPROC *startProc)
357
394
*
358
395
* Scan outward from the given proc to see if there is a cycle in the
359
396
* waits-for graph that includes this proc. Return TRUE if a cycle
360
- * is found, else FALSE. If a cycle is found, we also return a list of
397
+ * is found, else FALSE. If a cycle is found, we return a list of
361
398
* the "soft edges", if any, included in the cycle. These edges could
362
- * potentially be eliminated by rearranging wait queues.
399
+ * potentially be eliminated by rearranging wait queues. We also fill
400
+ * deadlockDetails[] with information about the detected cycle; this info
401
+ * is not used by the deadlock algorithm itself, only to print a useful
402
+ * message after failing.
363
403
*
364
404
* Since we need to be able to check hypothetical configurations that would
365
405
* exist after wait queue rearrangement, the routine pays attention to the
@@ -372,12 +412,14 @@ FindLockCycle(PGPROC *checkProc,
372
412
int * nSoftEdges ) /* output argument */
373
413
{
374
414
nVisitedProcs = 0 ;
415
+ nDeadlockDetails = 0 ;
375
416
* nSoftEdges = 0 ;
376
- return FindLockCycleRecurse (checkProc , softEdges , nSoftEdges );
417
+ return FindLockCycleRecurse (checkProc , 0 , softEdges , nSoftEdges );
377
418
}
378
419
379
420
static bool
380
421
FindLockCycleRecurse (PGPROC * checkProc ,
422
+ int depth ,
381
423
EDGE * softEdges , /* output argument */
382
424
int * nSoftEdges ) /* output argument */
383
425
{
@@ -402,7 +444,16 @@ FindLockCycleRecurse(PGPROC *checkProc,
402
444
{
403
445
/* If we return to starting point, we have a deadlock cycle */
404
446
if (i == 0 )
447
+ {
448
+ /*
449
+ * record total length of cycle --- outer levels will now
450
+ * fill deadlockDetails[]
451
+ */
452
+ Assert (depth <= MaxBackends );
453
+ nDeadlockDetails = depth ;
454
+
405
455
return true;
456
+ }
406
457
407
458
/*
408
459
* Otherwise, we have a cycle but it does not include the
@@ -449,8 +500,18 @@ FindLockCycleRecurse(PGPROC *checkProc,
449
500
((1 << lm ) & conflictMask ) != 0 )
450
501
{
451
502
/* This proc hard-blocks checkProc */
452
- if (FindLockCycleRecurse (proc , softEdges , nSoftEdges ))
503
+ if (FindLockCycleRecurse (proc , depth + 1 ,
504
+ softEdges , nSoftEdges ))
505
+ {
506
+ /* fill deadlockDetails[] */
507
+ DEADLOCK_INFO * info = & deadlockDetails [depth ];
508
+
509
+ info -> locktag = lock -> tag ;
510
+ info -> lockmode = checkProc -> waitLockMode ;
511
+ info -> pid = checkProc -> pid ;
512
+
453
513
return true;
514
+ }
454
515
/* If no deadlock, we're done looking at this holder */
455
516
break ;
456
517
}
@@ -496,8 +557,16 @@ FindLockCycleRecurse(PGPROC *checkProc,
496
557
if (((1 << proc -> waitLockMode ) & conflictMask ) != 0 )
497
558
{
498
559
/* This proc soft-blocks checkProc */
499
- if (FindLockCycleRecurse (proc , softEdges , nSoftEdges ))
560
+ if (FindLockCycleRecurse (proc , depth + 1 ,
561
+ softEdges , nSoftEdges ))
500
562
{
563
+ /* fill deadlockDetails[] */
564
+ DEADLOCK_INFO * info = & deadlockDetails [depth ];
565
+
566
+ info -> locktag = lock -> tag ;
567
+ info -> lockmode = checkProc -> waitLockMode ;
568
+ info -> pid = checkProc -> pid ;
569
+
501
570
/*
502
571
* Add this edge to the list of soft edges in the
503
572
* cycle
@@ -529,8 +598,16 @@ FindLockCycleRecurse(PGPROC *checkProc,
529
598
if (((1 << proc -> waitLockMode ) & conflictMask ) != 0 )
530
599
{
531
600
/* This proc soft-blocks checkProc */
532
- if (FindLockCycleRecurse (proc , softEdges , nSoftEdges ))
601
+ if (FindLockCycleRecurse (proc , depth + 1 ,
602
+ softEdges , nSoftEdges ))
533
603
{
604
+ /* fill deadlockDetails[] */
605
+ DEADLOCK_INFO * info = & deadlockDetails [depth ];
606
+
607
+ info -> locktag = lock -> tag ;
608
+ info -> lockmode = checkProc -> waitLockMode ;
609
+ info -> pid = checkProc -> pid ;
610
+
534
611
/*
535
612
* Add this edge to the list of soft edges in the
536
613
* cycle
@@ -758,3 +835,67 @@ PrintLockQueue(LOCK *lock, const char *info)
758
835
}
759
836
760
837
#endif
838
+
839
+ /*
840
+ * Report details about a detected deadlock.
841
+ */
842
+ void
843
+ DeadLockReport (void )
844
+ {
845
+ int i ;
846
+
847
+ for (i = 0 ; i < nDeadlockDetails ; i ++ )
848
+ {
849
+ DEADLOCK_INFO * info = & deadlockDetails [i ];
850
+ int nextpid ;
851
+
852
+ /* The last proc waits for the first one... */
853
+ if (i < nDeadlockDetails - 1 )
854
+ nextpid = info [1 ].pid ;
855
+ else
856
+ nextpid = deadlockDetails [0 ].pid ;
857
+
858
+ if (info -> locktag .relId == XactLockTableId && info -> locktag .dbId == 0 )
859
+ {
860
+ /* Lock is for transaction ID */
861
+ elog (NOTICE , "Proc %d waits for %s on transaction %u; blocked by %d" ,
862
+ info -> pid ,
863
+ GetLockmodeName (info -> lockmode ),
864
+ info -> locktag .objId .xid ,
865
+ nextpid );
866
+ }
867
+ else
868
+ {
869
+ /* Lock is for a relation */
870
+ elog (NOTICE , "Proc %d waits for %s on relation %u database %u; blocked by %d" ,
871
+ info -> pid ,
872
+ GetLockmodeName (info -> lockmode ),
873
+ info -> locktag .relId ,
874
+ info -> locktag .dbId ,
875
+ nextpid );
876
+ }
877
+ }
878
+ }
879
+
880
+ /*
881
+ * RememberSimpleDeadLock: set up info for DeadLockReport when ProcSleep
882
+ * detects a trivial (two-way) deadlock. proc1 wants to block for lockmode
883
+ * on lock, but proc2 is already waiting and would be blocked by proc1.
884
+ */
885
+ void
886
+ RememberSimpleDeadLock (PGPROC * proc1 ,
887
+ LOCKMODE lockmode ,
888
+ LOCK * lock ,
889
+ PGPROC * proc2 )
890
+ {
891
+ DEADLOCK_INFO * info = & deadlockDetails [0 ];
892
+
893
+ info -> locktag = lock -> tag ;
894
+ info -> lockmode = lockmode ;
895
+ info -> pid = proc1 -> pid ;
896
+ info ++ ;
897
+ info -> locktag = proc2 -> waitLock -> tag ;
898
+ info -> lockmode = proc2 -> waitLockMode ;
899
+ info -> pid = proc2 -> pid ;
900
+ nDeadlockDetails = 2 ;
901
+ }
0 commit comments