/src/postgres/src/backend/utils/resowner/resowner.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * resowner.c |
4 | | * POSTGRES resource owner management code. |
5 | | * |
6 | | * Query-lifespan resources are tracked by associating them with |
7 | | * ResourceOwner objects. This provides a simple mechanism for ensuring |
8 | | * that such resources are freed at the right time. |
9 | | * See utils/resowner/README for more info on how to use it. |
10 | | * |
11 | | * The implementation consists of a small fixed-size array and a hash table. |
12 | | * New entries are inserted to the fixed-size array, and when the array |
13 | | * fills up, all the entries are moved to the hash table. This way, the |
14 | | * array always contains a few most recently remembered references. To find |
15 | | * a particular reference, you need to search both the array and the hash |
16 | | * table. |
17 | | * |
18 | | * The most frequent usage is that a resource is remembered, and forgotten |
19 | | * shortly thereafter. For example, pin a buffer, read one tuple from it, |
20 | | * release the pin. Linearly scanning the small array handles that case |
21 | | * efficiently. However, some resources are held for a longer time, and |
22 | | * sometimes a lot of resources need to be held simultaneously. The hash |
23 | | * table handles those cases. |
24 | | * |
25 | | * When it's time to release the resources, we sort them according to the |
26 | | * release-priority of each resource, and release them in that order. |
27 | | * |
28 | | * Local lock references are special, they are not stored in the array or |
29 | | * the hash table. Instead, each resource owner has a separate small cache |
30 | | * of locks it owns. The lock manager has the same information in its local |
31 | | * lock hash table, and we fall back on that if the cache overflows, but |
32 | | * traversing the hash table is slower when there are a lot of locks |
33 | | * belonging to other resource owners. This is to speed up bulk releasing |
34 | | * or reassigning locks from a resource owner to its parent. |
35 | | * |
36 | | * |
37 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
38 | | * Portions Copyright (c) 1994, Regents of the University of California |
39 | | * |
40 | | * |
41 | | * IDENTIFICATION |
42 | | * src/backend/utils/resowner/resowner.c |
43 | | * |
44 | | *------------------------------------------------------------------------- |
45 | | */ |
46 | | #include "postgres.h" |
47 | | |
48 | | #include "common/hashfn.h" |
49 | | #include "common/int.h" |
50 | | #include "lib/ilist.h" |
51 | | #include "storage/aio.h" |
52 | | #include "storage/ipc.h" |
53 | | #include "storage/predicate.h" |
54 | | #include "storage/proc.h" |
55 | | #include "utils/memutils.h" |
56 | | #include "utils/resowner.h" |
57 | | |
58 | | /* |
59 | | * ResourceElem represents a reference associated with a resource owner. |
60 | | * |
61 | | * All objects managed by this code are required to fit into a Datum, |
62 | | * which is fine since they are generally pointers or integers. |
63 | | */ |
64 | | typedef struct ResourceElem |
65 | | { |
66 | | Datum item; |
67 | | const ResourceOwnerDesc *kind; /* NULL indicates a free hash table slot */ |
68 | | } ResourceElem; |
69 | | |
70 | | /* |
71 | | * Size of the fixed-size array to hold most-recently remembered resources. |
72 | | */ |
73 | 0 | #define RESOWNER_ARRAY_SIZE 32 |
74 | | |
75 | | /* |
76 | | * Initially allocated size of a ResourceOwner's hash table. Must be power of |
77 | | * two because we use (capacity - 1) as mask for hashing. |
78 | | */ |
79 | 0 | #define RESOWNER_HASH_INIT_SIZE 64 |
80 | | |
81 | | /* |
82 | | * How many items may be stored in a hash table of given capacity. When this |
83 | | * number is reached, we must resize. |
84 | | * |
85 | | * The hash table must always have enough free space that we can copy the |
86 | | * entries from the array to it, in ResourceOwnerSort. We also insist that |
87 | | * the initial size is large enough that we don't hit the max size immediately |
88 | | * when it's created. Aside from those limitations, 0.75 is a reasonable fill |
89 | | * factor. |
90 | | */ |
91 | | #define RESOWNER_HASH_MAX_ITEMS(capacity) \ |
92 | 0 | Min(capacity - RESOWNER_ARRAY_SIZE, (capacity)/4 * 3) |
93 | | |
94 | | StaticAssertDecl(RESOWNER_HASH_MAX_ITEMS(RESOWNER_HASH_INIT_SIZE) >= RESOWNER_ARRAY_SIZE, |
95 | | "initial hash size too small compared to array size"); |
96 | | |
97 | | /* |
98 | | * MAX_RESOWNER_LOCKS is the size of the per-resource owner locks cache. It's |
99 | | * chosen based on some testing with pg_dump with a large schema. When the |
100 | | * tests were done (on 9.2), resource owners in a pg_dump run contained up |
101 | | * to 9 locks, regardless of the schema size, except for the top resource |
102 | | * owner which contained much more (overflowing the cache). 15 seems like a |
103 | | * nice round number that's somewhat higher than what pg_dump needs. Note that |
104 | | * making this number larger is not free - the bigger the cache, the slower |
105 | | * it is to release locks (in retail), when a resource owner holds many locks. |
106 | | */ |
107 | 0 | #define MAX_RESOWNER_LOCKS 15 |
108 | | |
109 | | /* |
110 | | * ResourceOwner objects look like this |
111 | | */ |
112 | | struct ResourceOwnerData |
113 | | { |
114 | | ResourceOwner parent; /* NULL if no parent (toplevel owner) */ |
115 | | ResourceOwner firstchild; /* head of linked list of children */ |
116 | | ResourceOwner nextchild; /* next child of same parent */ |
117 | | const char *name; /* name (just for debugging) */ |
118 | | |
119 | | /* |
120 | | * When ResourceOwnerRelease is called, we sort the 'hash' and 'arr' by |
121 | | * the release priority. After that, no new resources can be remembered |
122 | | * or forgotten in retail. We have separate flags because |
123 | | * ResourceOwnerReleaseAllOfKind() temporarily sets 'releasing' without |
124 | | * sorting the arrays. |
125 | | */ |
126 | | bool releasing; |
127 | | bool sorted; /* are 'hash' and 'arr' sorted by priority? */ |
128 | | |
129 | | /* |
130 | | * Number of items in the locks cache, array, and hash table respectively. |
131 | | * (These are packed together to avoid padding in the struct.) |
132 | | */ |
133 | | uint8 nlocks; /* number of owned locks */ |
134 | | uint8 narr; /* how many items are stored in the array */ |
135 | | uint32 nhash; /* how many items are stored in the hash */ |
136 | | |
137 | | /* |
138 | | * The fixed-size array for recent resources. |
139 | | * |
140 | | * If 'sorted' is set, the contents are sorted by release priority. |
141 | | */ |
142 | | ResourceElem arr[RESOWNER_ARRAY_SIZE]; |
143 | | |
144 | | /* |
145 | | * The hash table. Uses open-addressing. 'nhash' is the number of items |
146 | | * present; if it would exceed 'grow_at', we enlarge it and re-hash. |
147 | | * 'grow_at' should be rather less than 'capacity' so that we don't waste |
148 | | * too much time searching for empty slots. |
149 | | * |
150 | | * If 'sorted' is set, the contents are no longer hashed, but sorted by |
151 | | * release priority. The first 'nhash' elements are occupied, the rest |
152 | | * are empty. |
153 | | */ |
154 | | ResourceElem *hash; |
155 | | uint32 capacity; /* allocated length of hash[] */ |
156 | | uint32 grow_at; /* grow hash when reach this */ |
157 | | |
158 | | /* The local locks cache. */ |
159 | | LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */ |
160 | | |
161 | | /* |
162 | | * AIO handles need be registered in critical sections and therefore |
163 | | * cannot use the normal ResourceElem mechanism. |
164 | | */ |
165 | | dlist_head aio_handles; |
166 | | }; |
167 | | |
168 | | |
169 | | /***************************************************************************** |
170 | | * GLOBAL MEMORY * |
171 | | *****************************************************************************/ |
172 | | |
173 | | ResourceOwner CurrentResourceOwner = NULL; |
174 | | ResourceOwner CurTransactionResourceOwner = NULL; |
175 | | ResourceOwner TopTransactionResourceOwner = NULL; |
176 | | ResourceOwner AuxProcessResourceOwner = NULL; |
177 | | |
178 | | /* #define RESOWNER_STATS */ |
179 | | |
180 | | #ifdef RESOWNER_STATS |
181 | | static int narray_lookups = 0; |
182 | | static int nhash_lookups = 0; |
183 | | #endif |
184 | | |
185 | | /* |
186 | | * List of add-on callbacks for resource releasing |
187 | | */ |
188 | | typedef struct ResourceReleaseCallbackItem |
189 | | { |
190 | | struct ResourceReleaseCallbackItem *next; |
191 | | ResourceReleaseCallback callback; |
192 | | void *arg; |
193 | | } ResourceReleaseCallbackItem; |
194 | | |
195 | | static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL; |
196 | | |
197 | | |
198 | | /* Internal routines */ |
199 | | static inline uint32 hash_resource_elem(Datum value, const ResourceOwnerDesc *kind); |
200 | | static void ResourceOwnerAddToHash(ResourceOwner owner, Datum value, |
201 | | const ResourceOwnerDesc *kind); |
202 | | static int resource_priority_cmp(const void *a, const void *b); |
203 | | static void ResourceOwnerSort(ResourceOwner owner); |
204 | | static void ResourceOwnerReleaseAll(ResourceOwner owner, |
205 | | ResourceReleasePhase phase, |
206 | | bool printLeakWarnings); |
207 | | static void ResourceOwnerReleaseInternal(ResourceOwner owner, |
208 | | ResourceReleasePhase phase, |
209 | | bool isCommit, |
210 | | bool isTopLevel); |
211 | | static void ReleaseAuxProcessResourcesCallback(int code, Datum arg); |
212 | | |
213 | | |
214 | | /***************************************************************************** |
215 | | * INTERNAL ROUTINES * |
216 | | *****************************************************************************/ |
217 | | |
218 | | /* |
219 | | * Hash function for value+kind combination. |
220 | | */ |
221 | | static inline uint32 |
222 | | hash_resource_elem(Datum value, const ResourceOwnerDesc *kind) |
223 | 0 | { |
224 | | /* |
225 | | * Most resource kinds store a pointer in 'value', and pointers are unique |
226 | | * all on their own. But some resources store plain integers (Files and |
227 | | * Buffers as of this writing), so we want to incorporate the 'kind' in |
228 | | * the hash too, otherwise those resources will collide a lot. But |
229 | | * because there are only a few resource kinds like that - and only a few |
230 | | * resource kinds to begin with - we don't need to work too hard to mix |
231 | | * 'kind' into the hash. Just add it with hash_combine(), it perturbs the |
232 | | * result enough for our purposes. |
233 | | */ |
234 | 0 | return hash_combine64(murmurhash64((uint64) value), |
235 | 0 | (uint64) (uintptr_t) kind); |
236 | 0 | } |
237 | | |
238 | | /* |
239 | | * Adds 'value' of given 'kind' to the ResourceOwner's hash table |
240 | | */ |
241 | | static void |
242 | | ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind) |
243 | 0 | { |
244 | 0 | uint32 mask = owner->capacity - 1; |
245 | 0 | uint32 idx; |
246 | |
|
247 | 0 | Assert(kind != NULL); |
248 | | |
249 | | /* Insert into first free slot at or after hash location. */ |
250 | 0 | idx = hash_resource_elem(value, kind) & mask; |
251 | 0 | for (;;) |
252 | 0 | { |
253 | 0 | if (owner->hash[idx].kind == NULL) |
254 | 0 | break; /* found a free slot */ |
255 | 0 | idx = (idx + 1) & mask; |
256 | 0 | } |
257 | 0 | owner->hash[idx].item = value; |
258 | 0 | owner->hash[idx].kind = kind; |
259 | 0 | owner->nhash++; |
260 | 0 | } |
261 | | |
262 | | /* |
263 | | * Comparison function to sort by release phase and priority |
264 | | */ |
265 | | static int |
266 | | resource_priority_cmp(const void *a, const void *b) |
267 | 0 | { |
268 | 0 | const ResourceElem *ra = (const ResourceElem *) a; |
269 | 0 | const ResourceElem *rb = (const ResourceElem *) b; |
270 | | |
271 | | /* Note: reverse order */ |
272 | 0 | if (ra->kind->release_phase == rb->kind->release_phase) |
273 | 0 | return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority); |
274 | 0 | else if (ra->kind->release_phase > rb->kind->release_phase) |
275 | 0 | return -1; |
276 | 0 | else |
277 | 0 | return 1; |
278 | 0 | } |
279 | | |
280 | | /* |
281 | | * Sort resources in reverse release priority. |
282 | | * |
283 | | * If the hash table is in use, all the elements from the fixed-size array are |
284 | | * moved to the hash table, and then the hash table is sorted. If there is no |
285 | | * hash table, then the fixed-size array is sorted directly. In either case, |
286 | | * the result is one sorted array that contains all the resources. |
287 | | */ |
288 | | static void |
289 | | ResourceOwnerSort(ResourceOwner owner) |
290 | 0 | { |
291 | 0 | ResourceElem *items; |
292 | 0 | uint32 nitems; |
293 | |
|
294 | 0 | if (owner->nhash == 0) |
295 | 0 | { |
296 | 0 | items = owner->arr; |
297 | 0 | nitems = owner->narr; |
298 | 0 | } |
299 | 0 | else |
300 | 0 | { |
301 | | /* |
302 | | * Compact the hash table, so that all the elements are in the |
303 | | * beginning of the 'hash' array, with no empty elements. |
304 | | */ |
305 | 0 | uint32 dst = 0; |
306 | |
|
307 | 0 | for (uint32 idx = 0; idx < owner->capacity; idx++) |
308 | 0 | { |
309 | 0 | if (owner->hash[idx].kind != NULL) |
310 | 0 | { |
311 | 0 | if (dst != idx) |
312 | 0 | owner->hash[dst] = owner->hash[idx]; |
313 | 0 | dst++; |
314 | 0 | } |
315 | 0 | } |
316 | | |
317 | | /* |
318 | | * Move all entries from the fixed-size array to 'hash'. |
319 | | * |
320 | | * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough |
321 | | * free space to move all the elements from the fixed-size array to |
322 | | * the hash. |
323 | | */ |
324 | 0 | Assert(dst + owner->narr <= owner->capacity); |
325 | 0 | for (int idx = 0; idx < owner->narr; idx++) |
326 | 0 | { |
327 | 0 | owner->hash[dst] = owner->arr[idx]; |
328 | 0 | dst++; |
329 | 0 | } |
330 | 0 | Assert(dst == owner->nhash + owner->narr); |
331 | 0 | owner->narr = 0; |
332 | 0 | owner->nhash = dst; |
333 | |
|
334 | 0 | items = owner->hash; |
335 | 0 | nitems = owner->nhash; |
336 | 0 | } |
337 | |
|
338 | 0 | qsort(items, nitems, sizeof(ResourceElem), resource_priority_cmp); |
339 | 0 | } |
340 | | |
341 | | /* |
342 | | * Call the ReleaseResource callback on entries with given 'phase'. |
343 | | */ |
344 | | static void |
345 | | ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase, |
346 | | bool printLeakWarnings) |
347 | 0 | { |
348 | 0 | ResourceElem *items; |
349 | 0 | uint32 nitems; |
350 | 0 | bool using_arr; |
351 | | |
352 | | /* |
353 | | * ResourceOwnerSort must've been called already. All the resources are |
354 | | * either in the array or the hash. |
355 | | */ |
356 | 0 | Assert(owner->releasing); |
357 | 0 | Assert(owner->sorted); |
358 | 0 | if (owner->nhash == 0) |
359 | 0 | { |
360 | 0 | items = owner->arr; |
361 | 0 | nitems = owner->narr; |
362 | 0 | using_arr = true; |
363 | 0 | } |
364 | 0 | else |
365 | 0 | { |
366 | 0 | Assert(owner->narr == 0); |
367 | 0 | items = owner->hash; |
368 | 0 | nitems = owner->nhash; |
369 | 0 | using_arr = false; |
370 | 0 | } |
371 | | |
372 | | /* |
373 | | * The resources are sorted in reverse priority order. Release them |
374 | | * starting from the end, until we hit the end of the phase that we are |
375 | | * releasing now. We will continue from there when called again for the |
376 | | * next phase. |
377 | | */ |
378 | 0 | while (nitems > 0) |
379 | 0 | { |
380 | 0 | uint32 idx = nitems - 1; |
381 | 0 | Datum value = items[idx].item; |
382 | 0 | const ResourceOwnerDesc *kind = items[idx].kind; |
383 | |
|
384 | 0 | if (kind->release_phase > phase) |
385 | 0 | break; |
386 | 0 | Assert(kind->release_phase == phase); |
387 | |
|
388 | 0 | if (printLeakWarnings) |
389 | 0 | { |
390 | 0 | char *res_str; |
391 | |
|
392 | 0 | res_str = kind->DebugPrint ? |
393 | 0 | kind->DebugPrint(value) |
394 | 0 | : psprintf("%s %p", kind->name, DatumGetPointer(value)); |
395 | 0 | elog(WARNING, "resource was not closed: %s", res_str); |
396 | 0 | pfree(res_str); |
397 | 0 | } |
398 | | |
399 | | /* |
400 | | * Update stored count to forget the item before calling its |
401 | | * ReleaseResource method. This avoids double-free crashes in case an |
402 | | * error gets thrown within ReleaseResource. |
403 | | */ |
404 | 0 | nitems--; |
405 | 0 | if (using_arr) |
406 | 0 | owner->narr = nitems; |
407 | 0 | else |
408 | 0 | owner->nhash = nitems; |
409 | |
|
410 | 0 | kind->ReleaseResource(value); |
411 | 0 | } |
412 | 0 | } |
413 | | |
414 | | |
415 | | /***************************************************************************** |
416 | | * EXPORTED ROUTINES * |
417 | | *****************************************************************************/ |
418 | | |
419 | | |
420 | | /* |
421 | | * ResourceOwnerCreate |
422 | | * Create an empty ResourceOwner. |
423 | | * |
424 | | * All ResourceOwner objects are kept in TopMemoryContext, since they should |
425 | | * only be freed explicitly. |
426 | | */ |
427 | | ResourceOwner |
428 | | ResourceOwnerCreate(ResourceOwner parent, const char *name) |
429 | 0 | { |
430 | 0 | ResourceOwner owner; |
431 | |
|
432 | 0 | owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext, |
433 | 0 | sizeof(struct ResourceOwnerData)); |
434 | 0 | owner->name = name; |
435 | |
|
436 | 0 | if (parent) |
437 | 0 | { |
438 | 0 | owner->parent = parent; |
439 | 0 | owner->nextchild = parent->firstchild; |
440 | 0 | parent->firstchild = owner; |
441 | 0 | } |
442 | |
|
443 | 0 | dlist_init(&owner->aio_handles); |
444 | |
|
445 | 0 | return owner; |
446 | 0 | } |
447 | | |
448 | | /* |
449 | | * Make sure there is room for at least one more resource in an array. |
450 | | * |
451 | | * This is separate from actually inserting a resource because if we run out |
452 | | * of memory, it's critical to do so *before* acquiring the resource. |
453 | | * |
454 | | * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between |
455 | | * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that |
456 | | * you reserved the space for! |
457 | | */ |
458 | | void |
459 | | ResourceOwnerEnlarge(ResourceOwner owner) |
460 | 0 | { |
461 | | /* |
462 | | * Mustn't try to remember more resources after we have already started |
463 | | * releasing |
464 | | */ |
465 | 0 | if (owner->releasing) |
466 | 0 | elog(ERROR, "ResourceOwnerEnlarge called after release started"); |
467 | | |
468 | 0 | if (owner->narr < RESOWNER_ARRAY_SIZE) |
469 | 0 | return; /* no work needed */ |
470 | | |
471 | | /* |
472 | | * Is there space in the hash? If not, enlarge it. |
473 | | */ |
474 | 0 | if (owner->narr + owner->nhash >= owner->grow_at) |
475 | 0 | { |
476 | 0 | uint32 i, |
477 | 0 | oldcap, |
478 | 0 | newcap; |
479 | 0 | ResourceElem *oldhash; |
480 | 0 | ResourceElem *newhash; |
481 | |
|
482 | 0 | oldhash = owner->hash; |
483 | 0 | oldcap = owner->capacity; |
484 | | |
485 | | /* Double the capacity (it must stay a power of 2!) */ |
486 | 0 | newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE; |
487 | 0 | newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext, |
488 | 0 | newcap * sizeof(ResourceElem)); |
489 | | |
490 | | /* |
491 | | * We assume we can't fail below this point, so OK to scribble on the |
492 | | * owner |
493 | | */ |
494 | 0 | owner->hash = newhash; |
495 | 0 | owner->capacity = newcap; |
496 | 0 | owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap); |
497 | 0 | owner->nhash = 0; |
498 | |
|
499 | 0 | if (oldhash != NULL) |
500 | 0 | { |
501 | | /* |
502 | | * Transfer any pre-existing entries into the new hash table; they |
503 | | * don't necessarily go where they were before, so this simple |
504 | | * logic is the best way. |
505 | | */ |
506 | 0 | for (i = 0; i < oldcap; i++) |
507 | 0 | { |
508 | 0 | if (oldhash[i].kind != NULL) |
509 | 0 | ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind); |
510 | 0 | } |
511 | | |
512 | | /* And release old hash table. */ |
513 | 0 | pfree(oldhash); |
514 | 0 | } |
515 | 0 | } |
516 | | |
517 | | /* Move items from the array to the hash */ |
518 | 0 | for (int i = 0; i < owner->narr; i++) |
519 | 0 | ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind); |
520 | 0 | owner->narr = 0; |
521 | |
|
522 | 0 | Assert(owner->nhash <= owner->grow_at); |
523 | 0 | } |
524 | | |
525 | | /* |
526 | | * Remember that an object is owned by a ResourceOwner |
527 | | * |
528 | | * Caller must have previously done ResourceOwnerEnlarge() |
529 | | */ |
530 | | void |
531 | | ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind) |
532 | 0 | { |
533 | 0 | uint32 idx; |
534 | | |
535 | | /* sanity check the ResourceOwnerDesc */ |
536 | 0 | Assert(kind->release_phase != 0); |
537 | 0 | Assert(kind->release_priority != 0); |
538 | | |
539 | | /* |
540 | | * Mustn't try to remember more resources after we have already started |
541 | | * releasing. We already checked this in ResourceOwnerEnlarge. |
542 | | */ |
543 | 0 | Assert(!owner->releasing); |
544 | 0 | Assert(!owner->sorted); |
545 | |
|
546 | 0 | if (owner->narr >= RESOWNER_ARRAY_SIZE) |
547 | 0 | { |
548 | | /* forgot to call ResourceOwnerEnlarge? */ |
549 | 0 | elog(ERROR, "ResourceOwnerRemember called but array was full"); |
550 | 0 | } |
551 | | |
552 | | /* Append to the array. */ |
553 | 0 | idx = owner->narr; |
554 | 0 | owner->arr[idx].item = value; |
555 | 0 | owner->arr[idx].kind = kind; |
556 | 0 | owner->narr++; |
557 | 0 | } |
558 | | |
559 | | /* |
560 | | * Forget that an object is owned by a ResourceOwner |
561 | | * |
562 | | * Note: If same resource ID is associated with the ResourceOwner more than |
563 | | * once, one instance is removed. |
564 | | * |
565 | | * Note: Forgetting a resource does not guarantee that there is room to |
566 | | * remember a new resource. One exception is when you forget the most |
567 | | * recently remembered resource; that does make room for a new remember call. |
568 | | * Some code callers rely on that exception. |
569 | | */ |
570 | | void |
571 | | ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind) |
572 | 0 | { |
573 | | /* |
574 | | * Mustn't call this after we have already started releasing resources. |
575 | | * (Release callback functions are not allowed to release additional |
576 | | * resources.) |
577 | | */ |
578 | 0 | if (owner->releasing) |
579 | 0 | elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name); |
580 | 0 | Assert(!owner->sorted); |
581 | | |
582 | | /* Search through all items in the array first. */ |
583 | 0 | for (int i = owner->narr - 1; i >= 0; i--) |
584 | 0 | { |
585 | 0 | if (owner->arr[i].item == value && |
586 | 0 | owner->arr[i].kind == kind) |
587 | 0 | { |
588 | 0 | owner->arr[i] = owner->arr[owner->narr - 1]; |
589 | 0 | owner->narr--; |
590 | |
|
591 | | #ifdef RESOWNER_STATS |
592 | | narray_lookups++; |
593 | | #endif |
594 | 0 | return; |
595 | 0 | } |
596 | 0 | } |
597 | | |
598 | | /* Search hash */ |
599 | 0 | if (owner->nhash > 0) |
600 | 0 | { |
601 | 0 | uint32 mask = owner->capacity - 1; |
602 | 0 | uint32 idx; |
603 | |
|
604 | 0 | idx = hash_resource_elem(value, kind) & mask; |
605 | 0 | for (uint32 i = 0; i < owner->capacity; i++) |
606 | 0 | { |
607 | 0 | if (owner->hash[idx].item == value && |
608 | 0 | owner->hash[idx].kind == kind) |
609 | 0 | { |
610 | 0 | owner->hash[idx].item = (Datum) 0; |
611 | 0 | owner->hash[idx].kind = NULL; |
612 | 0 | owner->nhash--; |
613 | |
|
614 | | #ifdef RESOWNER_STATS |
615 | | nhash_lookups++; |
616 | | #endif |
617 | 0 | return; |
618 | 0 | } |
619 | 0 | idx = (idx + 1) & mask; |
620 | 0 | } |
621 | 0 | } |
622 | | |
623 | | /* |
624 | | * Use %p to print the reference, since most objects tracked by a resource |
625 | | * owner are pointers. It's a bit misleading if it's not a pointer, but |
626 | | * this is a programmer error, anyway. |
627 | | */ |
628 | 0 | elog(ERROR, "%s %p is not owned by resource owner %s", |
629 | 0 | kind->name, DatumGetPointer(value), owner->name); |
630 | 0 | } |
631 | | |
632 | | /* |
633 | | * ResourceOwnerRelease |
634 | | * Release all resources owned by a ResourceOwner and its descendants, |
635 | | * but don't delete the owner objects themselves. |
636 | | * |
637 | | * Note that this executes just one phase of release, and so typically |
638 | | * must be called three times. We do it this way because (a) we want to |
639 | | * do all the recursion separately for each phase, thereby preserving |
640 | | * the needed order of operations; and (b) xact.c may have other operations |
641 | | * to do between the phases. |
642 | | * |
643 | | * phase: release phase to execute |
644 | | * isCommit: true for successful completion of a query or transaction, |
645 | | * false for unsuccessful |
646 | | * isTopLevel: true if completing a main transaction, else false |
647 | | * |
648 | | * isCommit is passed because some modules may expect that their resources |
649 | | * were all released already if the transaction or portal finished normally. |
650 | | * If so it is reasonable to give a warning (NOT an error) should any |
651 | | * unreleased resources be present. When isCommit is false, such warnings |
652 | | * are generally inappropriate. |
653 | | * |
654 | | * isTopLevel is passed when we are releasing TopTransactionResourceOwner |
655 | | * at completion of a main transaction. This generally means that *all* |
656 | | * resources will be released, and so we can optimize things a bit. |
657 | | * |
658 | | * NOTE: After starting the release process, by calling this function, no new |
659 | | * resources can be remembered in the resource owner. You also cannot call |
660 | | * ResourceOwnerForget on any previously remembered resources to release |
661 | | * resources "in retail" after that, you must let the bulk release take care |
662 | | * of them. |
663 | | */ |
664 | | void |
665 | | ResourceOwnerRelease(ResourceOwner owner, |
666 | | ResourceReleasePhase phase, |
667 | | bool isCommit, |
668 | | bool isTopLevel) |
669 | 0 | { |
670 | | /* There's not currently any setup needed before recursing */ |
671 | 0 | ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel); |
672 | |
|
673 | | #ifdef RESOWNER_STATS |
674 | | if (isTopLevel) |
675 | | { |
676 | | elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d", |
677 | | narray_lookups, nhash_lookups); |
678 | | narray_lookups = 0; |
679 | | nhash_lookups = 0; |
680 | | } |
681 | | #endif |
682 | 0 | } |
683 | | |
684 | | static void |
685 | | ResourceOwnerReleaseInternal(ResourceOwner owner, |
686 | | ResourceReleasePhase phase, |
687 | | bool isCommit, |
688 | | bool isTopLevel) |
689 | 0 | { |
690 | 0 | ResourceOwner child; |
691 | 0 | ResourceOwner save; |
692 | 0 | ResourceReleaseCallbackItem *item; |
693 | 0 | ResourceReleaseCallbackItem *next; |
694 | | |
695 | | /* Recurse to handle descendants */ |
696 | 0 | for (child = owner->firstchild; child != NULL; child = child->nextchild) |
697 | 0 | ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel); |
698 | | |
699 | | /* |
700 | | * To release the resources in the right order, sort them by phase and |
701 | | * priority. |
702 | | * |
703 | | * The ReleaseResource callback functions are not allowed to remember or |
704 | | * forget any other resources after this. Otherwise we lose track of where |
705 | | * we are in processing the hash/array. |
706 | | */ |
707 | 0 | if (!owner->releasing) |
708 | 0 | { |
709 | 0 | Assert(phase == RESOURCE_RELEASE_BEFORE_LOCKS); |
710 | 0 | Assert(!owner->sorted); |
711 | 0 | owner->releasing = true; |
712 | 0 | } |
713 | 0 | else |
714 | 0 | { |
715 | | /* |
716 | | * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not |
717 | | * the first call to ResourceOwnerRelease. But if an error happens |
718 | | * between the release phases, we might get called again for the same |
719 | | * ResourceOwner from AbortTransaction. |
720 | | */ |
721 | 0 | } |
722 | 0 | if (!owner->sorted) |
723 | 0 | { |
724 | 0 | ResourceOwnerSort(owner); |
725 | 0 | owner->sorted = true; |
726 | 0 | } |
727 | | |
728 | | /* |
729 | | * Make CurrentResourceOwner point to me, so that the release callback |
730 | | * functions know which resource owner is been released. |
731 | | */ |
732 | 0 | save = CurrentResourceOwner; |
733 | 0 | CurrentResourceOwner = owner; |
734 | |
|
735 | 0 | if (phase == RESOURCE_RELEASE_BEFORE_LOCKS) |
736 | 0 | { |
737 | | /* |
738 | | * Release all resources that need to be released before the locks. |
739 | | * |
740 | | * During a commit, there shouldn't be any remaining resources --- |
741 | | * that would indicate failure to clean up the executor correctly --- |
742 | | * so issue warnings. In the abort case, just clean up quietly. |
743 | | */ |
744 | 0 | ResourceOwnerReleaseAll(owner, phase, isCommit); |
745 | |
|
746 | 0 | while (!dlist_is_empty(&owner->aio_handles)) |
747 | 0 | { |
748 | 0 | dlist_node *node = dlist_head_node(&owner->aio_handles); |
749 | |
|
750 | 0 | pgaio_io_release_resowner(node, !isCommit); |
751 | 0 | } |
752 | 0 | } |
753 | 0 | else if (phase == RESOURCE_RELEASE_LOCKS) |
754 | 0 | { |
755 | 0 | if (isTopLevel) |
756 | 0 | { |
757 | | /* |
758 | | * For a top-level xact we are going to release all locks (or at |
759 | | * least all non-session locks), so just do a single lmgr call at |
760 | | * the top of the recursion. |
761 | | */ |
762 | 0 | if (owner == TopTransactionResourceOwner) |
763 | 0 | { |
764 | 0 | ProcReleaseLocks(isCommit); |
765 | 0 | ReleasePredicateLocks(isCommit, false); |
766 | 0 | } |
767 | 0 | } |
768 | 0 | else |
769 | 0 | { |
770 | | /* |
771 | | * Release locks retail. Note that if we are committing a |
772 | | * subtransaction, we do NOT release its locks yet, but transfer |
773 | | * them to the parent. |
774 | | */ |
775 | 0 | LOCALLOCK **locks; |
776 | 0 | int nlocks; |
777 | |
|
778 | 0 | Assert(owner->parent != NULL); |
779 | | |
780 | | /* |
781 | | * Pass the list of locks owned by this resource owner to the lock |
782 | | * manager, unless it has overflowed. |
783 | | */ |
784 | 0 | if (owner->nlocks > MAX_RESOWNER_LOCKS) |
785 | 0 | { |
786 | 0 | locks = NULL; |
787 | 0 | nlocks = 0; |
788 | 0 | } |
789 | 0 | else |
790 | 0 | { |
791 | 0 | locks = owner->locks; |
792 | 0 | nlocks = owner->nlocks; |
793 | 0 | } |
794 | |
|
795 | 0 | if (isCommit) |
796 | 0 | LockReassignCurrentOwner(locks, nlocks); |
797 | 0 | else |
798 | 0 | LockReleaseCurrentOwner(locks, nlocks); |
799 | 0 | } |
800 | 0 | } |
801 | 0 | else if (phase == RESOURCE_RELEASE_AFTER_LOCKS) |
802 | 0 | { |
803 | | /* |
804 | | * Release all resources that need to be released after the locks. |
805 | | */ |
806 | 0 | ResourceOwnerReleaseAll(owner, phase, isCommit); |
807 | 0 | } |
808 | | |
809 | | /* Let add-on modules get a chance too */ |
810 | 0 | for (item = ResourceRelease_callbacks; item; item = next) |
811 | 0 | { |
812 | | /* allow callbacks to unregister themselves when called */ |
813 | 0 | next = item->next; |
814 | 0 | item->callback(phase, isCommit, isTopLevel, item->arg); |
815 | 0 | } |
816 | |
|
817 | 0 | CurrentResourceOwner = save; |
818 | 0 | } |
819 | | |
820 | | /* |
821 | | * ResourceOwnerReleaseAllOfKind |
822 | | * Release all resources of a certain type held by this owner. |
823 | | */ |
824 | | void |
825 | | ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind) |
826 | 0 | { |
827 | | /* Mustn't call this after we have already started releasing resources. */ |
828 | 0 | if (owner->releasing) |
829 | 0 | elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name); |
830 | 0 | Assert(!owner->sorted); |
831 | | |
832 | | /* |
833 | | * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember |
834 | | * while we're scanning the owner. Enlarging the hash would cause us to |
835 | | * lose track of the point we're scanning. |
836 | | */ |
837 | 0 | owner->releasing = true; |
838 | | |
839 | | /* Array first */ |
840 | 0 | for (int i = 0; i < owner->narr; i++) |
841 | 0 | { |
842 | 0 | if (owner->arr[i].kind == kind) |
843 | 0 | { |
844 | 0 | Datum value = owner->arr[i].item; |
845 | |
|
846 | 0 | owner->arr[i] = owner->arr[owner->narr - 1]; |
847 | 0 | owner->narr--; |
848 | 0 | i--; |
849 | |
|
850 | 0 | kind->ReleaseResource(value); |
851 | 0 | } |
852 | 0 | } |
853 | | |
854 | | /* Then hash */ |
855 | 0 | for (uint32 i = 0; i < owner->capacity; i++) |
856 | 0 | { |
857 | 0 | if (owner->hash[i].kind == kind) |
858 | 0 | { |
859 | 0 | Datum value = owner->hash[i].item; |
860 | |
|
861 | 0 | owner->hash[i].item = (Datum) 0; |
862 | 0 | owner->hash[i].kind = NULL; |
863 | 0 | owner->nhash--; |
864 | |
|
865 | 0 | kind->ReleaseResource(value); |
866 | 0 | } |
867 | 0 | } |
868 | 0 | owner->releasing = false; |
869 | 0 | } |
870 | | |
871 | | /* |
872 | | * ResourceOwnerDelete |
873 | | * Delete an owner object and its descendants. |
874 | | * |
875 | | * The caller must have already released all resources in the object tree. |
876 | | */ |
877 | | void |
878 | | ResourceOwnerDelete(ResourceOwner owner) |
879 | 0 | { |
880 | | /* We had better not be deleting CurrentResourceOwner ... */ |
881 | 0 | Assert(owner != CurrentResourceOwner); |
882 | | |
883 | | /* And it better not own any resources, either */ |
884 | 0 | Assert(owner->narr == 0); |
885 | 0 | Assert(owner->nhash == 0); |
886 | 0 | Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1); |
887 | | |
888 | | /* |
889 | | * Delete children. The recursive call will delink the child from me, so |
890 | | * just iterate as long as there is a child. |
891 | | */ |
892 | 0 | while (owner->firstchild != NULL) |
893 | 0 | ResourceOwnerDelete(owner->firstchild); |
894 | | |
895 | | /* |
896 | | * We delink the owner from its parent before deleting it, so that if |
897 | | * there's an error we won't have deleted/busted owners still attached to |
898 | | * the owner tree. Better a leak than a crash. |
899 | | */ |
900 | 0 | ResourceOwnerNewParent(owner, NULL); |
901 | | |
902 | | /* And free the object. */ |
903 | 0 | if (owner->hash) |
904 | 0 | pfree(owner->hash); |
905 | 0 | pfree(owner); |
906 | 0 | } |
907 | | |
908 | | /* |
909 | | * Fetch parent of a ResourceOwner (returns NULL if top-level owner) |
910 | | */ |
911 | | ResourceOwner |
912 | | ResourceOwnerGetParent(ResourceOwner owner) |
913 | 0 | { |
914 | 0 | return owner->parent; |
915 | 0 | } |
916 | | |
917 | | /* |
918 | | * Reassign a ResourceOwner to have a new parent |
919 | | */ |
920 | | void |
921 | | ResourceOwnerNewParent(ResourceOwner owner, |
922 | | ResourceOwner newparent) |
923 | 0 | { |
924 | 0 | ResourceOwner oldparent = owner->parent; |
925 | |
|
926 | 0 | if (oldparent) |
927 | 0 | { |
928 | 0 | if (owner == oldparent->firstchild) |
929 | 0 | oldparent->firstchild = owner->nextchild; |
930 | 0 | else |
931 | 0 | { |
932 | 0 | ResourceOwner child; |
933 | |
|
934 | 0 | for (child = oldparent->firstchild; child; child = child->nextchild) |
935 | 0 | { |
936 | 0 | if (owner == child->nextchild) |
937 | 0 | { |
938 | 0 | child->nextchild = owner->nextchild; |
939 | 0 | break; |
940 | 0 | } |
941 | 0 | } |
942 | 0 | } |
943 | 0 | } |
944 | |
|
945 | 0 | if (newparent) |
946 | 0 | { |
947 | 0 | Assert(owner != newparent); |
948 | 0 | owner->parent = newparent; |
949 | 0 | owner->nextchild = newparent->firstchild; |
950 | 0 | newparent->firstchild = owner; |
951 | 0 | } |
952 | 0 | else |
953 | 0 | { |
954 | 0 | owner->parent = NULL; |
955 | 0 | owner->nextchild = NULL; |
956 | 0 | } |
957 | 0 | } |
958 | | |
959 | | /* |
960 | | * Register or deregister callback functions for resource cleanup |
961 | | * |
962 | | * These functions can be used by dynamically loaded modules. These used |
963 | | * to be the only way for an extension to register custom resource types |
964 | | * with a resource owner, but nowadays it is easier to define a new |
965 | | * ResourceOwnerDesc with custom callbacks. |
966 | | */ |
967 | | void |
968 | | RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg) |
969 | 0 | { |
970 | 0 | ResourceReleaseCallbackItem *item; |
971 | |
|
972 | 0 | item = (ResourceReleaseCallbackItem *) |
973 | 0 | MemoryContextAlloc(TopMemoryContext, |
974 | 0 | sizeof(ResourceReleaseCallbackItem)); |
975 | 0 | item->callback = callback; |
976 | 0 | item->arg = arg; |
977 | 0 | item->next = ResourceRelease_callbacks; |
978 | 0 | ResourceRelease_callbacks = item; |
979 | 0 | } |
980 | | |
981 | | void |
982 | | UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg) |
983 | 0 | { |
984 | 0 | ResourceReleaseCallbackItem *item; |
985 | 0 | ResourceReleaseCallbackItem *prev; |
986 | |
|
987 | 0 | prev = NULL; |
988 | 0 | for (item = ResourceRelease_callbacks; item; prev = item, item = item->next) |
989 | 0 | { |
990 | 0 | if (item->callback == callback && item->arg == arg) |
991 | 0 | { |
992 | 0 | if (prev) |
993 | 0 | prev->next = item->next; |
994 | 0 | else |
995 | 0 | ResourceRelease_callbacks = item->next; |
996 | 0 | pfree(item); |
997 | 0 | break; |
998 | 0 | } |
999 | 0 | } |
1000 | 0 | } |
1001 | | |
1002 | | /* |
1003 | | * Establish an AuxProcessResourceOwner for the current process. |
1004 | | */ |
1005 | | void |
1006 | | CreateAuxProcessResourceOwner(void) |
1007 | 0 | { |
1008 | 0 | Assert(AuxProcessResourceOwner == NULL); |
1009 | 0 | Assert(CurrentResourceOwner == NULL); |
1010 | 0 | AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess"); |
1011 | 0 | CurrentResourceOwner = AuxProcessResourceOwner; |
1012 | | |
1013 | | /* |
1014 | | * Register a shmem-exit callback for cleanup of aux-process resource |
1015 | | * owner. (This needs to run after, e.g., ShutdownXLOG.) |
1016 | | */ |
1017 | 0 | on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0); |
1018 | 0 | } |
1019 | | |
1020 | | /* |
1021 | | * Convenience routine to release all resources tracked in |
1022 | | * AuxProcessResourceOwner (but that resowner is not destroyed here). |
1023 | | * Warn about leaked resources if isCommit is true. |
1024 | | */ |
1025 | | void |
1026 | | ReleaseAuxProcessResources(bool isCommit) |
1027 | 0 | { |
1028 | | /* |
1029 | | * At this writing, the only thing that could actually get released is |
1030 | | * buffer pins; but we may as well do the full release protocol. |
1031 | | */ |
1032 | 0 | ResourceOwnerRelease(AuxProcessResourceOwner, |
1033 | 0 | RESOURCE_RELEASE_BEFORE_LOCKS, |
1034 | 0 | isCommit, true); |
1035 | 0 | ResourceOwnerRelease(AuxProcessResourceOwner, |
1036 | 0 | RESOURCE_RELEASE_LOCKS, |
1037 | 0 | isCommit, true); |
1038 | 0 | ResourceOwnerRelease(AuxProcessResourceOwner, |
1039 | 0 | RESOURCE_RELEASE_AFTER_LOCKS, |
1040 | 0 | isCommit, true); |
1041 | | /* allow it to be reused */ |
1042 | 0 | AuxProcessResourceOwner->releasing = false; |
1043 | 0 | AuxProcessResourceOwner->sorted = false; |
1044 | 0 | } |
1045 | | |
1046 | | /* |
1047 | | * Shmem-exit callback for the same. |
1048 | | * Warn about leaked resources if process exit code is zero (ie normal). |
1049 | | */ |
1050 | | static void |
1051 | | ReleaseAuxProcessResourcesCallback(int code, Datum arg) |
1052 | 0 | { |
1053 | 0 | bool isCommit = (code == 0); |
1054 | |
|
1055 | 0 | ReleaseAuxProcessResources(isCommit); |
1056 | 0 | } |
1057 | | |
1058 | | /* |
1059 | | * Remember that a Local Lock is owned by a ResourceOwner |
1060 | | * |
1061 | | * This is different from the generic ResourceOwnerRemember in that the list of |
1062 | | * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries, |
1063 | | * and when it overflows, we stop tracking locks. The point of only remembering |
1064 | | * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held, |
1065 | | * ResourceOwnerForgetLock doesn't need to scan through a large array to find |
1066 | | * the entry. |
1067 | | */ |
1068 | | void |
1069 | | ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock) |
1070 | 0 | { |
1071 | 0 | Assert(locallock != NULL); |
1072 | |
|
1073 | 0 | if (owner->nlocks > MAX_RESOWNER_LOCKS) |
1074 | 0 | return; /* we have already overflowed */ |
1075 | | |
1076 | 0 | if (owner->nlocks < MAX_RESOWNER_LOCKS) |
1077 | 0 | owner->locks[owner->nlocks] = locallock; |
1078 | 0 | else |
1079 | 0 | { |
1080 | | /* overflowed */ |
1081 | 0 | } |
1082 | 0 | owner->nlocks++; |
1083 | 0 | } |
1084 | | |
1085 | | /* |
1086 | | * Forget that a Local Lock is owned by a ResourceOwner |
1087 | | */ |
1088 | | void |
1089 | | ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock) |
1090 | 0 | { |
1091 | 0 | int i; |
1092 | |
|
1093 | 0 | if (owner->nlocks > MAX_RESOWNER_LOCKS) |
1094 | 0 | return; /* we have overflowed */ |
1095 | | |
1096 | 0 | Assert(owner->nlocks > 0); |
1097 | 0 | for (i = owner->nlocks - 1; i >= 0; i--) |
1098 | 0 | { |
1099 | 0 | if (locallock == owner->locks[i]) |
1100 | 0 | { |
1101 | 0 | owner->locks[i] = owner->locks[owner->nlocks - 1]; |
1102 | 0 | owner->nlocks--; |
1103 | 0 | return; |
1104 | 0 | } |
1105 | 0 | } |
1106 | 0 | elog(ERROR, "lock reference %p is not owned by resource owner %s", |
1107 | 0 | locallock, owner->name); |
1108 | 0 | } |
1109 | | |
1110 | | void |
1111 | | ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node) |
1112 | 0 | { |
1113 | 0 | dlist_push_tail(&owner->aio_handles, ioh_node); |
1114 | 0 | } |
1115 | | |
1116 | | void |
1117 | | ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node) |
1118 | 0 | { |
1119 | 0 | dlist_delete_from(&owner->aio_handles, ioh_node); |
1120 | 0 | } |