Age Owner 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-2023, 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
2033 andres 54 CBC 10361 : InitializeSession(void)
55 : {
56 10361 : CurrentSession = MemoryContextAllocZero(TopMemoryContext, sizeof(Session));
57 10361 : }
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 403 : 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 403 : if (CurrentSession->segment != NULL)
88 348 : return dsm_segment_handle(CurrentSession->segment);
89 :
90 : /* Otherwise, prepare to set one up. */
91 55 : old_context = MemoryContextSwitchTo(TopMemoryContext);
92 55 : shm_toc_initialize_estimator(&estimator);
93 :
94 : /* Estimate space for the per-session DSA area. */
95 55 : shm_toc_estimate_keys(&estimator, 1);
96 55 : shm_toc_estimate_chunk(&estimator, SESSION_DSA_SIZE);
97 :
98 : /* Estimate space for the per-session record typmod registry. */
99 55 : typmod_registry_size = SharedRecordTypmodRegistryEstimate();
100 55 : shm_toc_estimate_keys(&estimator, 1);
101 55 : shm_toc_estimate_chunk(&estimator, typmod_registry_size);
102 :
103 : /* Set up segment and TOC. */
104 55 : size = shm_toc_estimate(&estimator);
105 55 : seg = dsm_create(size, DSM_CREATE_NULL_IF_MAXSEGMENTS);
106 55 : if (seg == NULL)
107 : {
2033 andres 108 UBC 0 : MemoryContextSwitchTo(old_context);
109 :
110 0 : return DSM_HANDLE_INVALID;
111 : }
2033 andres 112 CBC 55 : toc = shm_toc_create(SESSION_MAGIC,
113 : dsm_segment_address(seg),
114 : size);
115 :
116 : /* Create per-session DSA area. */
117 55 : dsa_space = shm_toc_allocate(toc, SESSION_DSA_SIZE);
118 55 : dsa = dsa_create_in_place(dsa_space,
119 : SESSION_DSA_SIZE,
120 : LWTRANCHE_PER_SESSION_DSA,
121 : seg);
122 55 : shm_toc_insert(toc, SESSION_KEY_DSA, dsa_space);
123 :
124 :
125 : /* Create session-scoped shared record typmod registry. */
126 55 : typmod_registry_space = shm_toc_allocate(toc, typmod_registry_size);
127 55 : SharedRecordTypmodRegistryInit((SharedRecordTypmodRegistry *)
128 : typmod_registry_space, seg, dsa);
129 55 : 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 55 : dsm_pin_mapping(seg);
140 55 : dsa_pin_mapping(dsa);
141 :
142 : /* Make segment and area available via CurrentSession. */
143 55 : CurrentSession->segment = seg;
144 55 : CurrentSession->area = dsa;
145 :
146 55 : MemoryContextSwitchTo(old_context);
147 :
148 55 : 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 1298 : 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 1298 : old_context = MemoryContextSwitchTo(TopMemoryContext);
165 :
166 : /* Attach to the DSM segment. */
167 1298 : seg = dsm_attach(handle);
168 1298 : if (seg == NULL)
2033 andres 169 UBC 0 : elog(ERROR, "could not attach to per-session DSM segment");
2033 andres 170 CBC 1298 : toc = shm_toc_attach(SESSION_MAGIC, dsm_segment_address(seg));
171 :
172 : /* Attach to the DSA area. */
173 1298 : dsa_space = shm_toc_lookup(toc, SESSION_KEY_DSA, false);
174 1298 : dsa = dsa_attach_in_place(dsa_space, seg);
175 :
176 : /* Make them available via the current session. */
177 1298 : CurrentSession->segment = seg;
178 1298 : CurrentSession->area = dsa;
179 :
180 : /* Attach to the shared record typmod registry. */
181 : typmod_registry_space =
182 1298 : shm_toc_lookup(toc, SESSION_KEY_RECORD_TYPMOD_REGISTRY, false);
183 1298 : SharedRecordTypmodRegistryAttach((SharedRecordTypmodRegistry *)
184 : typmod_registry_space);
185 :
186 : /* Remain attached until end of backend or DetachSession(). */
187 1298 : dsm_pin_mapping(seg);
188 1298 : dsa_pin_mapping(dsa);
189 :
190 1298 : MemoryContextSwitchTo(old_context);
191 1298 : }
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 1295 : DetachSession(void)
202 : {
203 : /* Runs detach hooks. */
204 1295 : dsm_detach(CurrentSession->segment);
205 1295 : CurrentSession->segment = NULL;
206 1295 : dsa_detach(CurrentSession->area);
207 1295 : CurrentSession->area = NULL;
208 1295 : }
|