Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * session.c
4 : : * Encapsulation of user session.
5 : : *
6 : : * This is intended to contain data that needs to be shared between backends
7 : : * performing work for a client session. In particular such a session is
8 : : * shared between the leader and worker processes for parallel queries. At
9 : : * some later point it might also become useful infrastructure for separating
10 : : * backends from client connections, e.g. for the purpose of pooling.
11 : : *
12 : : * Currently this infrastructure is used to share:
13 : : * - typemod registry for ephemeral row-types, i.e. BlessTupleDesc etc.
14 : : *
15 : : * Portions Copyright (c) 2017-2024, PostgreSQL Global Development Group
16 : : *
17 : : * src/backend/access/common/session.c
18 : : *
19 : : *-------------------------------------------------------------------------
20 : : */
21 : : #include "postgres.h"
22 : :
23 : : #include "access/session.h"
24 : : #include "storage/lwlock.h"
25 : : #include "storage/shm_toc.h"
26 : : #include "utils/memutils.h"
27 : : #include "utils/typcache.h"
28 : :
29 : : /* Magic number for per-session DSM TOC. */
30 : : #define SESSION_MAGIC 0xabb0fbc9
31 : :
32 : : /*
33 : : * We want to create a DSA area to store shared state that has the same
34 : : * lifetime as a session. So far, it's only used to hold the shared record
35 : : * type registry. We don't want it to have to create any DSM segments just
36 : : * yet in common cases, so we'll give it enough space to hold a very small
37 : : * SharedRecordTypmodRegistry.
38 : : */
39 : : #define SESSION_DSA_SIZE 0x30000
40 : :
41 : : /*
42 : : * Magic numbers for state sharing in the per-session DSM area.
43 : : */
44 : : #define SESSION_KEY_DSA UINT64CONST(0xFFFFFFFFFFFF0001)
45 : : #define SESSION_KEY_RECORD_TYPMOD_REGISTRY UINT64CONST(0xFFFFFFFFFFFF0002)
46 : :
47 : : /* This backend's current session. */
48 : : Session *CurrentSession = NULL;
49 : :
50 : : /*
51 : : * Set up CurrentSession to point to an empty Session object.
52 : : */
53 : : void
2404 andres@anarazel.de 54 :CBC 13121 : InitializeSession(void)
55 : : {
56 : 13121 : CurrentSession = MemoryContextAllocZero(TopMemoryContext, sizeof(Session));
57 : 13121 : }
58 : :
59 : : /*
60 : : * Initialize the per-session DSM segment if it isn't already initialized, and
61 : : * return its handle so that worker processes can attach to it.
62 : : *
63 : : * Unlike the per-context DSM segment, this segment and its contents are
64 : : * reused for future parallel queries.
65 : : *
66 : : * Return DSM_HANDLE_INVALID if a segment can't be allocated due to lack of
67 : : * resources.
68 : : */
69 : : dsm_handle
70 : 414 : GetSessionDsmHandle(void)
71 : : {
72 : : shm_toc_estimator estimator;
73 : : shm_toc *toc;
74 : : dsm_segment *seg;
75 : : size_t typmod_registry_size;
76 : : size_t size;
77 : : void *dsa_space;
78 : : void *typmod_registry_space;
79 : : dsa_area *dsa;
80 : : MemoryContext old_context;
81 : :
82 : : /*
83 : : * If we have already created a session-scope DSM segment in this backend,
84 : : * return its handle. The same segment will be used for the rest of this
85 : : * backend's lifetime.
86 : : */
87 [ + + ]: 414 : if (CurrentSession->segment != NULL)
88 : 355 : return dsm_segment_handle(CurrentSession->segment);
89 : :
90 : : /* Otherwise, prepare to set one up. */
91 : 59 : old_context = MemoryContextSwitchTo(TopMemoryContext);
92 : 59 : shm_toc_initialize_estimator(&estimator);
93 : :
94 : : /* Estimate space for the per-session DSA area. */
95 : 59 : shm_toc_estimate_keys(&estimator, 1);
96 : 59 : shm_toc_estimate_chunk(&estimator, SESSION_DSA_SIZE);
97 : :
98 : : /* Estimate space for the per-session record typmod registry. */
99 : 59 : typmod_registry_size = SharedRecordTypmodRegistryEstimate();
100 : 59 : shm_toc_estimate_keys(&estimator, 1);
101 : 59 : shm_toc_estimate_chunk(&estimator, typmod_registry_size);
102 : :
103 : : /* Set up segment and TOC. */
104 : 59 : size = shm_toc_estimate(&estimator);
105 : 59 : seg = dsm_create(size, DSM_CREATE_NULL_IF_MAXSEGMENTS);
106 [ - + ]: 59 : if (seg == NULL)
107 : : {
2404 andres@anarazel.de 108 :UBC 0 : MemoryContextSwitchTo(old_context);
109 : :
110 : 0 : return DSM_HANDLE_INVALID;
111 : : }
2404 andres@anarazel.de 112 :CBC 59 : toc = shm_toc_create(SESSION_MAGIC,
113 : : dsm_segment_address(seg),
114 : : size);
115 : :
116 : : /* Create per-session DSA area. */
117 : 59 : dsa_space = shm_toc_allocate(toc, SESSION_DSA_SIZE);
118 : 59 : dsa = dsa_create_in_place(dsa_space,
119 : : SESSION_DSA_SIZE,
120 : : LWTRANCHE_PER_SESSION_DSA,
121 : : seg);
122 : 59 : shm_toc_insert(toc, SESSION_KEY_DSA, dsa_space);
123 : :
124 : :
125 : : /* Create session-scoped shared record typmod registry. */
126 : 59 : typmod_registry_space = shm_toc_allocate(toc, typmod_registry_size);
127 : 59 : SharedRecordTypmodRegistryInit((SharedRecordTypmodRegistry *)
128 : : typmod_registry_space, seg, dsa);
129 : 59 : shm_toc_insert(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY,
130 : : typmod_registry_space);
131 : :
132 : : /*
133 : : * If we got this far, we can pin the shared memory so it stays mapped for
134 : : * the rest of this backend's life. If we don't make it this far, cleanup
135 : : * callbacks for anything we installed above (ie currently
136 : : * SharedRecordTypmodRegistry) will run when the DSM segment is detached
137 : : * by CurrentResourceOwner so we aren't left with a broken CurrentSession.
138 : : */
139 : 59 : dsm_pin_mapping(seg);
140 : 59 : dsa_pin_mapping(dsa);
141 : :
142 : : /* Make segment and area available via CurrentSession. */
143 : 59 : CurrentSession->segment = seg;
144 : 59 : CurrentSession->area = dsa;
145 : :
146 : 59 : MemoryContextSwitchTo(old_context);
147 : :
148 : 59 : return dsm_segment_handle(seg);
149 : : }
150 : :
151 : : /*
152 : : * Attach to a per-session DSM segment provided by a parallel leader.
153 : : */
154 : : void
155 : 1322 : AttachSession(dsm_handle handle)
156 : : {
157 : : dsm_segment *seg;
158 : : shm_toc *toc;
159 : : void *dsa_space;
160 : : void *typmod_registry_space;
161 : : dsa_area *dsa;
162 : : MemoryContext old_context;
163 : :
164 : 1322 : old_context = MemoryContextSwitchTo(TopMemoryContext);
165 : :
166 : : /* Attach to the DSM segment. */
167 : 1322 : seg = dsm_attach(handle);
168 [ - + ]: 1322 : if (seg == NULL)
2404 andres@anarazel.de 169 [ # # ]:UBC 0 : elog(ERROR, "could not attach to per-session DSM segment");
2404 andres@anarazel.de 170 :CBC 1322 : toc = shm_toc_attach(SESSION_MAGIC, dsm_segment_address(seg));
171 : :
172 : : /* Attach to the DSA area. */
173 : 1322 : dsa_space = shm_toc_lookup(toc, SESSION_KEY_DSA, false);
174 : 1322 : dsa = dsa_attach_in_place(dsa_space, seg);
175 : :
176 : : /* Make them available via the current session. */
177 : 1322 : CurrentSession->segment = seg;
178 : 1322 : CurrentSession->area = dsa;
179 : :
180 : : /* Attach to the shared record typmod registry. */
181 : : typmod_registry_space =
182 : 1322 : shm_toc_lookup(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY, false);
183 : 1322 : SharedRecordTypmodRegistryAttach((SharedRecordTypmodRegistry *)
184 : : typmod_registry_space);
185 : :
186 : : /* Remain attached until end of backend or DetachSession(). */
187 : 1322 : dsm_pin_mapping(seg);
188 : 1322 : dsa_pin_mapping(dsa);
189 : :
190 : 1322 : MemoryContextSwitchTo(old_context);
191 : 1322 : }
192 : :
193 : : /*
194 : : * Detach from the current session DSM segment. It's not strictly necessary
195 : : * to do this explicitly since we'll detach automatically at backend exit, but
196 : : * if we ever reuse parallel workers it will become important for workers to
197 : : * detach from one session before attaching to another. Note that this runs
198 : : * detach hooks.
199 : : */
200 : : void
201 : 1319 : DetachSession(void)
202 : : {
203 : : /* Runs detach hooks. */
204 : 1319 : dsm_detach(CurrentSession->segment);
205 : 1319 : CurrentSession->segment = NULL;
206 : 1319 : dsa_detach(CurrentSession->area);
207 : 1319 : CurrentSession->area = NULL;
208 : 1319 : }
|