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