Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit b25b6c9

Browse files
committed
Once again allow LWLocks to be used within DSM segments.
Prior to commit 7882c3b, it was possible to use LWLocks within DSM segments, but that commit broke this use case by switching from a doubly linked list to a circular linked list. Switch back, using a new bit of general infrastructure for maintaining lists of PGPROCs. Thomas Munro, reviewed by me.
1 parent 2bf06f7 commit b25b6c9

File tree

6 files changed

+240
-35
lines changed

6 files changed

+240
-35
lines changed

src/backend/storage/lmgr/lwlock.c

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
#include "storage/ipc.h"
8585
#include "storage/predicate.h"
8686
#include "storage/proc.h"
87+
#include "storage/proclist.h"
8788
#include "storage/spin.h"
8889
#include "utils/memutils.h"
8990

@@ -717,7 +718,7 @@ LWLockInitialize(LWLock *lock, int tranche_id)
717718
pg_atomic_init_u32(&lock->nwaiters, 0);
718719
#endif
719720
lock->tranche = tranche_id;
720-
dlist_init(&lock->waiters);
721+
proclist_init(&lock->waiters);
721722
}
722723

723724
/*
@@ -920,25 +921,25 @@ LWLockWakeup(LWLock *lock)
920921
{
921922
bool new_release_ok;
922923
bool wokeup_somebody = false;
923-
dlist_head wakeup;
924-
dlist_mutable_iter iter;
924+
proclist_head wakeup;
925+
proclist_mutable_iter iter;
925926

926-
dlist_init(&wakeup);
927+
proclist_init(&wakeup);
927928

928929
new_release_ok = true;
929930

930931
/* lock wait list while collecting backends to wake up */
931932
LWLockWaitListLock(lock);
932933

933-
dlist_foreach_modify(iter, &lock->waiters)
934+
proclist_foreach_modify(iter, &lock->waiters, lwWaitLink)
934935
{
935-
PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur);
936+
PGPROC *waiter = GetPGProcByNumber(iter.cur);
936937

937938
if (wokeup_somebody && waiter->lwWaitMode == LW_EXCLUSIVE)
938939
continue;
939940

940-
dlist_delete(&waiter->lwWaitLink);
941-
dlist_push_tail(&wakeup, &waiter->lwWaitLink);
941+
proclist_delete(&lock->waiters, iter.cur, lwWaitLink);
942+
proclist_push_tail(&wakeup, iter.cur, lwWaitLink);
942943

943944
if (waiter->lwWaitMode != LW_WAIT_UNTIL_FREE)
944945
{
@@ -963,7 +964,7 @@ LWLockWakeup(LWLock *lock)
963964
break;
964965
}
965966

966-
Assert(dlist_is_empty(&wakeup) || pg_atomic_read_u32(&lock->state) & LW_FLAG_HAS_WAITERS);
967+
Assert(proclist_is_empty(&wakeup) || pg_atomic_read_u32(&lock->state) & LW_FLAG_HAS_WAITERS);
967968

968969
/* unset required flags, and release lock, in one fell swoop */
969970
{
@@ -982,7 +983,7 @@ LWLockWakeup(LWLock *lock)
982983
else
983984
desired_state &= ~LW_FLAG_RELEASE_OK;
984985

985-
if (dlist_is_empty(&wakeup))
986+
if (proclist_is_empty(&wakeup))
986987
desired_state &= ~LW_FLAG_HAS_WAITERS;
987988

988989
desired_state &= ~LW_FLAG_LOCKED; /* release lock */
@@ -994,12 +995,12 @@ LWLockWakeup(LWLock *lock)
994995
}
995996

996997
/* Awaken any waiters I removed from the queue. */
997-
dlist_foreach_modify(iter, &wakeup)
998+
proclist_foreach_modify(iter, &wakeup, lwWaitLink)
998999
{
999-
PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur);
1000+
PGPROC *waiter = GetPGProcByNumber(iter.cur);
10001001

10011002
LOG_LWDEBUG("LWLockRelease", lock, "release waiter");
1002-
dlist_delete(&waiter->lwWaitLink);
1003+
proclist_delete(&wakeup, iter.cur, lwWaitLink);
10031004

10041005
/*
10051006
* Guarantee that lwWaiting being unset only becomes visible once the
@@ -1046,9 +1047,9 @@ LWLockQueueSelf(LWLock *lock, LWLockMode mode)
10461047

10471048
/* LW_WAIT_UNTIL_FREE waiters are always at the front of the queue */
10481049
if (mode == LW_WAIT_UNTIL_FREE)
1049-
dlist_push_head(&lock->waiters, &MyProc->lwWaitLink);
1050+
proclist_push_head(&lock->waiters, MyProc->pgprocno, lwWaitLink);
10501051
else
1051-
dlist_push_tail(&lock->waiters, &MyProc->lwWaitLink);
1052+
proclist_push_tail(&lock->waiters, MyProc->pgprocno, lwWaitLink);
10521053

10531054
/* Can release the mutex now */
10541055
LWLockWaitListUnlock(lock);
@@ -1070,7 +1071,7 @@ static void
10701071
LWLockDequeueSelf(LWLock *lock)
10711072
{
10721073
bool found = false;
1073-
dlist_mutable_iter iter;
1074+
proclist_mutable_iter iter;
10741075

10751076
#ifdef LWLOCK_STATS
10761077
lwlock_stats *lwstats;
@@ -1086,19 +1087,17 @@ LWLockDequeueSelf(LWLock *lock)
10861087
* Can't just remove ourselves from the list, but we need to iterate over
10871088
* all entries as somebody else could have unqueued us.
10881089
*/
1089-
dlist_foreach_modify(iter, &lock->waiters)
1090+
proclist_foreach_modify(iter, &lock->waiters, lwWaitLink)
10901091
{
1091-
PGPROC *proc = dlist_container(PGPROC, lwWaitLink, iter.cur);
1092-
1093-
if (proc == MyProc)
1092+
if (iter.cur == MyProc->pgprocno)
10941093
{
10951094
found = true;
1096-
dlist_delete(&proc->lwWaitLink);
1095+
proclist_delete(&lock->waiters, iter.cur, lwWaitLink);
10971096
break;
10981097
}
10991098
}
11001099

1101-
if (dlist_is_empty(&lock->waiters) &&
1100+
if (proclist_is_empty(&lock->waiters) &&
11021101
(pg_atomic_read_u32(&lock->state) & LW_FLAG_HAS_WAITERS) != 0)
11031102
{
11041103
pg_atomic_fetch_and_u32(&lock->state, ~LW_FLAG_HAS_WAITERS);
@@ -1719,12 +1718,12 @@ LWLockWaitForVar(LWLock *lock, uint64 *valptr, uint64 oldval, uint64 *newval)
17191718
void
17201719
LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
17211720
{
1722-
dlist_head wakeup;
1723-
dlist_mutable_iter iter;
1721+
proclist_head wakeup;
1722+
proclist_mutable_iter iter;
17241723

17251724
PRINT_LWDEBUG("LWLockUpdateVar", lock, LW_EXCLUSIVE);
17261725

1727-
dlist_init(&wakeup);
1726+
proclist_init(&wakeup);
17281727

17291728
LWLockWaitListLock(lock);
17301729

@@ -1737,15 +1736,15 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
17371736
* See if there are any LW_WAIT_UNTIL_FREE waiters that need to be woken
17381737
* up. They are always in the front of the queue.
17391738
*/
1740-
dlist_foreach_modify(iter, &lock->waiters)
1739+
proclist_foreach_modify(iter, &lock->waiters, lwWaitLink)
17411740
{
1742-
PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur);
1741+
PGPROC *waiter = GetPGProcByNumber(iter.cur);
17431742

17441743
if (waiter->lwWaitMode != LW_WAIT_UNTIL_FREE)
17451744
break;
17461745

1747-
dlist_delete(&waiter->lwWaitLink);
1748-
dlist_push_tail(&wakeup, &waiter->lwWaitLink);
1746+
proclist_delete(&lock->waiters, iter.cur, lwWaitLink);
1747+
proclist_push_tail(&wakeup, iter.cur, lwWaitLink);
17491748
}
17501749

17511750
/* We are done updating shared state of the lock itself. */
@@ -1754,11 +1753,11 @@ LWLockUpdateVar(LWLock *lock, uint64 *valptr, uint64 val)
17541753
/*
17551754
* Awaken any waiters I removed from the queue.
17561755
*/
1757-
dlist_foreach_modify(iter, &wakeup)
1756+
proclist_foreach_modify(iter, &wakeup, lwWaitLink)
17581757
{
1759-
PGPROC *waiter = dlist_container(PGPROC, lwWaitLink, iter.cur);
1758+
PGPROC *waiter = GetPGProcByNumber(iter.cur);
17601759

1761-
dlist_delete(&waiter->lwWaitLink);
1760+
proclist_delete(&wakeup, iter.cur, lwWaitLink);
17621761
/* check comment in LWLockWakeup() about this barrier */
17631762
pg_write_barrier();
17641763
waiter->lwWaiting = false;

src/include/storage/lwlock.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#error "lwlock.h may not be included from frontend code"
1919
#endif
2020

21-
#include "lib/ilist.h"
21+
#include "storage/proclist_types.h"
2222
#include "storage/s_lock.h"
2323
#include "port/atomics.h"
2424

@@ -59,7 +59,7 @@ typedef struct LWLock
5959
{
6060
uint16 tranche; /* tranche ID */
6161
pg_atomic_uint32 state; /* state of exclusive/nonexclusive lockers */
62-
dlist_head waiters; /* list of waiting PGPROCs */
62+
proclist_head waiters; /* list of waiting PGPROCs */
6363
#ifdef LOCK_DEBUG
6464
pg_atomic_uint32 nwaiters; /* number of waiters */
6565
struct PGPROC *owner; /* last exclusive owner of the lock */

src/include/storage/proc.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "storage/latch.h"
2020
#include "storage/lock.h"
2121
#include "storage/pg_sema.h"
22+
#include "storage/proclist_types.h"
2223

2324
/*
2425
* Each backend advertises up to PGPROC_MAX_CACHED_SUBXIDS TransactionIds
@@ -112,7 +113,7 @@ struct PGPROC
112113
/* Info about LWLock the process is currently waiting for, if any. */
113114
bool lwWaiting; /* true if waiting for an LW lock */
114115
uint8 lwWaitMode; /* lwlock mode being waited for */
115-
dlist_node lwWaitLink; /* position in LW lock wait list */
116+
proclist_node lwWaitLink; /* position in LW lock wait list */
116117

117118
/* Info about lock the process is currently waiting for, if any. */
118119
/* waitLock and waitProcLock are NULL if not currently waiting. */
@@ -243,6 +244,9 @@ extern PROC_HDR *ProcGlobal;
243244

244245
extern PGPROC *PreparedXactProcs;
245246

247+
/* Accessor for PGPROC given a pgprocno. */
248+
#define GetPGProcByNumber(n) (&ProcGlobal->allProcs[(n)])
249+
246250
/*
247251
* We set aside some extra PGPROC structures for auxiliary processes,
248252
* ie things that aren't full-fledged backends but need shmem access.

src/include/storage/proclist.h

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*-------------------------------------------------------------------------
2+
*
3+
* proclist.h
4+
* operations on doubly-linked lists of pgprocnos
5+
*
6+
* The interface is similar to dlist from ilist.h, but uses pgprocno instead
7+
* of pointers. This allows proclist_head to be mapped at different addresses
8+
* in different backends.
9+
*
10+
* See proclist_types.h for the structs that these functions operate on. They
11+
* are separated to break a header dependency cycle with proc.h.
12+
*
13+
* Portions Copyright (c) 2016, PostgreSQL Global Development Group
14+
*
15+
* IDENTIFICATION
16+
* src/include/storage/proclist.h
17+
*-------------------------------------------------------------------------
18+
*/
19+
#ifndef PROCLIST_H
20+
#define PROCLIST_H
21+
22+
#include "storage/proc.h"
23+
#include "storage/proclist_types.h"
24+
25+
/*
26+
* Initialize a proclist.
27+
*/
28+
static inline void
29+
proclist_init(proclist_head *list)
30+
{
31+
list->head = list->tail = INVALID_PGPROCNO;
32+
}
33+
34+
/*
35+
* Is the list empty?
36+
*/
37+
static inline bool
38+
proclist_is_empty(proclist_head *list)
39+
{
40+
return list->head == INVALID_PGPROCNO;
41+
}
42+
43+
/*
44+
* Get a pointer to a proclist_node inside a given PGPROC, given a procno and
45+
* an offset.
46+
*/
47+
static inline proclist_node *
48+
proclist_node_get(int procno, size_t node_offset)
49+
{
50+
char *entry = (char *) GetPGProcByNumber(procno);
51+
52+
return (proclist_node *) (entry + node_offset);
53+
}
54+
55+
/*
56+
* Insert a node at the beginning of a list.
57+
*/
58+
static inline void
59+
proclist_push_head_offset(proclist_head *list, int procno, size_t node_offset)
60+
{
61+
proclist_node *node = proclist_node_get(procno, node_offset);
62+
63+
if (list->head == INVALID_PGPROCNO)
64+
{
65+
Assert(list->tail == INVALID_PGPROCNO);
66+
node->next = node->prev = INVALID_PGPROCNO;
67+
list->head = list->tail = procno;
68+
}
69+
else
70+
{
71+
Assert(list->tail != INVALID_PGPROCNO);
72+
node->next = list->head;
73+
proclist_node_get(node->next, node_offset)->prev = procno;
74+
node->prev = INVALID_PGPROCNO;
75+
list->head = procno;
76+
}
77+
}
78+
79+
/*
80+
* Insert a node a the end of a list.
81+
*/
82+
static inline void
83+
proclist_push_tail_offset(proclist_head *list, int procno, size_t node_offset)
84+
{
85+
proclist_node *node = proclist_node_get(procno, node_offset);
86+
87+
if (list->tail == INVALID_PGPROCNO)
88+
{
89+
Assert(list->head == INVALID_PGPROCNO);
90+
node->next = node->prev = INVALID_PGPROCNO;
91+
list->head = list->tail = procno;
92+
}
93+
else
94+
{
95+
Assert(list->head != INVALID_PGPROCNO);
96+
node->prev = list->tail;
97+
proclist_node_get(node->prev, node_offset)->next = procno;
98+
node->next = INVALID_PGPROCNO;
99+
list->tail = procno;
100+
}
101+
}
102+
103+
/*
104+
* Delete a node. The node must be in the list.
105+
*/
106+
static inline void
107+
proclist_delete_offset(proclist_head *list, int procno, size_t node_offset)
108+
{
109+
proclist_node *node = proclist_node_get(procno, node_offset);
110+
111+
if (node->prev == INVALID_PGPROCNO)
112+
list->head = node->next;
113+
else
114+
proclist_node_get(node->prev, node_offset)->next = node->next;
115+
116+
if (node->next == INVALID_PGPROCNO)
117+
list->tail = node->prev;
118+
else
119+
proclist_node_get(node->next, node_offset)->prev = node->prev;
120+
}
121+
122+
/*
123+
* Helper macros to avoid repetition of offsetof(PGPROC, <member>).
124+
* 'link_member' is the name of a proclist_node member in PGPROC.
125+
*/
126+
#define proclist_delete(list, procno, link_member) \
127+
proclist_delete_offset((list), (procno), offsetof(PGPROC, link_member))
128+
#define proclist_push_head(list, procno, link_member) \
129+
proclist_push_head_offset((list), (procno), offsetof(PGPROC, link_member))
130+
#define proclist_push_tail(list, procno, link_member) \
131+
proclist_push_tail_offset((list), (procno), offsetof(PGPROC, link_member))
132+
133+
/*
134+
* Iterate through the list pointed at by 'lhead', storing the current
135+
* position in 'iter'. 'link_member' is the name of a proclist_node member in
136+
* PGPROC. Access the current position with iter.cur.
137+
*
138+
* The only list modification allowed while iterating is deleting the current
139+
* node with proclist_delete(list, iter.cur, node_offset).
140+
*/
141+
#define proclist_foreach_modify(iter, lhead, link_member) \
142+
for (AssertVariableIsOfTypeMacro(iter, proclist_mutable_iter), \
143+
AssertVariableIsOfTypeMacro(lhead, proclist_head *), \
144+
(iter).cur = (lhead)->head, \
145+
(iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO : \
146+
proclist_node_get((iter).cur, \
147+
offsetof(PGPROC, link_member))->next; \
148+
(iter).cur != INVALID_PGPROCNO; \
149+
(iter).cur = (iter).next, \
150+
(iter).next = (iter).cur == INVALID_PGPROCNO ? INVALID_PGPROCNO : \
151+
proclist_node_get((iter).cur, \
152+
offsetof(PGPROC, link_member))->next)
153+
154+
#endif

0 commit comments

Comments
 (0)