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