Line data Source code
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-2025, 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 : #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 : #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 : 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 : #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 3707842 : hash_resource_elem(Datum value, const ResourceOwnerDesc *kind)
223 : {
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 : #if SIZEOF_DATUM == 8
235 3707842 : return hash_combine64(murmurhash64((uint64) value), (uint64) kind);
236 : #else
237 : return hash_combine(murmurhash32((uint32) value), (uint32) kind);
238 : #endif
239 : }
240 :
241 : /*
242 : * Adds 'value' of given 'kind' to the ResourceOwner's hash table
243 : */
244 : static void
245 2818782 : ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
246 : {
247 2818782 : uint32 mask = owner->capacity - 1;
248 : uint32 idx;
249 :
250 : Assert(kind != NULL);
251 :
252 : /* Insert into first free slot at or after hash location. */
253 2818782 : idx = hash_resource_elem(value, kind) & mask;
254 : for (;;)
255 : {
256 367724386 : if (owner->hash[idx].kind == NULL)
257 2818782 : break; /* found a free slot */
258 364905604 : idx = (idx + 1) & mask;
259 : }
260 2818782 : owner->hash[idx].item = value;
261 2818782 : owner->hash[idx].kind = kind;
262 2818782 : owner->nhash++;
263 2818782 : }
264 :
265 : /*
266 : * Comparison function to sort by release phase and priority
267 : */
268 : static int
269 1121178 : resource_priority_cmp(const void *a, const void *b)
270 : {
271 1121178 : const ResourceElem *ra = (const ResourceElem *) a;
272 1121178 : const ResourceElem *rb = (const ResourceElem *) b;
273 :
274 : /* Note: reverse order */
275 1121178 : if (ra->kind->release_phase == rb->kind->release_phase)
276 868858 : return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
277 252320 : else if (ra->kind->release_phase > rb->kind->release_phase)
278 28976 : return -1;
279 : else
280 223344 : return 1;
281 : }
282 :
283 : /*
284 : * Sort resources in reverse release priority.
285 : *
286 : * If the hash table is in use, all the elements from the fixed-size array are
287 : * moved to the hash table, and then the hash table is sorted. If there is no
288 : * hash table, then the fixed-size array is sorted directly. In either case,
289 : * the result is one sorted array that contains all the resources.
290 : */
291 : static void
292 1606210 : ResourceOwnerSort(ResourceOwner owner)
293 : {
294 : ResourceElem *items;
295 : uint32 nitems;
296 :
297 1606210 : if (owner->nhash == 0)
298 : {
299 1606048 : items = owner->arr;
300 1606048 : nitems = owner->narr;
301 : }
302 : else
303 : {
304 : /*
305 : * Compact the hash table, so that all the elements are in the
306 : * beginning of the 'hash' array, with no empty elements.
307 : */
308 162 : uint32 dst = 0;
309 :
310 1083170 : for (int idx = 0; idx < owner->capacity; idx++)
311 : {
312 1083008 : if (owner->hash[idx].kind != NULL)
313 : {
314 416412 : if (dst != idx)
315 416270 : owner->hash[dst] = owner->hash[idx];
316 416412 : dst++;
317 : }
318 : }
319 :
320 : /*
321 : * Move all entries from the fixed-size array to 'hash'.
322 : *
323 : * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough
324 : * free space to move all the elements from the fixed-size array to
325 : * the hash.
326 : */
327 : Assert(dst + owner->narr <= owner->capacity);
328 1026 : for (int idx = 0; idx < owner->narr; idx++)
329 : {
330 864 : owner->hash[dst] = owner->arr[idx];
331 864 : dst++;
332 : }
333 : Assert(dst == owner->nhash + owner->narr);
334 162 : owner->narr = 0;
335 162 : owner->nhash = dst;
336 :
337 162 : items = owner->hash;
338 162 : nitems = owner->nhash;
339 : }
340 :
341 1606210 : qsort(items, nitems, sizeof(ResourceElem), resource_priority_cmp);
342 1606210 : }
343 :
344 : /*
345 : * Call the ReleaseResource callback on entries with given 'phase'.
346 : */
347 : static void
348 3212424 : ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase,
349 : bool printLeakWarnings)
350 : {
351 : ResourceElem *items;
352 : uint32 nitems;
353 :
354 : /*
355 : * ResourceOwnerSort must've been called already. All the resources are
356 : * either in the array or the hash.
357 : */
358 : Assert(owner->releasing);
359 : Assert(owner->sorted);
360 3212424 : if (owner->nhash == 0)
361 : {
362 3212100 : items = owner->arr;
363 3212100 : nitems = owner->narr;
364 : }
365 : else
366 : {
367 : Assert(owner->narr == 0);
368 324 : items = owner->hash;
369 324 : nitems = owner->nhash;
370 : }
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 3762512 : while (nitems > 0)
379 : {
380 583740 : uint32 idx = nitems - 1;
381 583740 : Datum value = items[idx].item;
382 583740 : const ResourceOwnerDesc *kind = items[idx].kind;
383 :
384 583740 : if (kind->release_phase > phase)
385 33652 : break;
386 : Assert(kind->release_phase == phase);
387 :
388 550088 : if (printLeakWarnings)
389 : {
390 : char *res_str;
391 :
392 4 : res_str = kind->DebugPrint ?
393 2 : kind->DebugPrint(value)
394 2 : : psprintf("%s %p", kind->name, DatumGetPointer(value));
395 2 : elog(WARNING, "resource was not closed: %s", res_str);
396 2 : pfree(res_str);
397 : }
398 550088 : kind->ReleaseResource(value);
399 550088 : nitems--;
400 : }
401 3212424 : if (owner->nhash == 0)
402 3212100 : owner->narr = nitems;
403 : else
404 324 : owner->nhash = nitems;
405 3212424 : }
406 :
407 :
408 : /*****************************************************************************
409 : * EXPORTED ROUTINES *
410 : *****************************************************************************/
411 :
412 :
413 : /*
414 : * ResourceOwnerCreate
415 : * Create an empty ResourceOwner.
416 : *
417 : * All ResourceOwner objects are kept in TopMemoryContext, since they should
418 : * only be freed explicitly.
419 : */
420 : ResourceOwner
421 1607082 : ResourceOwnerCreate(ResourceOwner parent, const char *name)
422 : {
423 : ResourceOwner owner;
424 :
425 1607082 : owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
426 : sizeof(struct ResourceOwnerData));
427 1607082 : owner->name = name;
428 :
429 1607082 : if (parent)
430 : {
431 776568 : owner->parent = parent;
432 776568 : owner->nextchild = parent->firstchild;
433 776568 : parent->firstchild = owner;
434 : }
435 :
436 1607082 : dlist_init(&owner->aio_handles);
437 :
438 1607082 : return owner;
439 : }
440 :
441 : /*
442 : * Make sure there is room for at least one more resource in an array.
443 : *
444 : * This is separate from actually inserting a resource because if we run out
445 : * of memory, it's critical to do so *before* acquiring the resource.
446 : *
447 : * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between
448 : * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that
449 : * you reserved the space for!
450 : */
451 : void
452 345101678 : ResourceOwnerEnlarge(ResourceOwner owner)
453 : {
454 : /*
455 : * Mustn't try to remember more resources after we have already started
456 : * releasing
457 : */
458 345101678 : if (owner->releasing)
459 2 : elog(ERROR, "ResourceOwnerEnlarge called after release started");
460 :
461 345101676 : if (owner->narr < RESOWNER_ARRAY_SIZE)
462 345060354 : return; /* no work needed */
463 :
464 : /*
465 : * Is there space in the hash? If not, enlarge it.
466 : */
467 41322 : if (owner->narr + owner->nhash >= owner->grow_at)
468 : {
469 : uint32 i,
470 : oldcap,
471 : newcap;
472 : ResourceElem *oldhash;
473 : ResourceElem *newhash;
474 :
475 13160 : oldhash = owner->hash;
476 13160 : oldcap = owner->capacity;
477 :
478 : /* Double the capacity (it must stay a power of 2!) */
479 13160 : newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE;
480 13160 : newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext,
481 : newcap * sizeof(ResourceElem));
482 :
483 : /*
484 : * We assume we can't fail below this point, so OK to scribble on the
485 : * owner
486 : */
487 13160 : owner->hash = newhash;
488 13160 : owner->capacity = newcap;
489 13160 : owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap);
490 13160 : owner->nhash = 0;
491 :
492 13160 : if (oldhash != NULL)
493 : {
494 : /*
495 : * Transfer any pre-existing entries into the new hash table; they
496 : * don't necessarily go where they were before, so this simple
497 : * logic is the best way.
498 : */
499 2175464 : for (i = 0; i < oldcap; i++)
500 : {
501 2171264 : if (oldhash[i].kind != NULL)
502 1496478 : ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
503 : }
504 :
505 : /* And release old hash table. */
506 4200 : pfree(oldhash);
507 : }
508 : }
509 :
510 : /* Move items from the array to the hash */
511 1363626 : for (int i = 0; i < owner->narr; i++)
512 1322304 : ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
513 41322 : owner->narr = 0;
514 :
515 : Assert(owner->nhash <= owner->grow_at);
516 : }
517 :
518 : /*
519 : * Remember that an object is owned by a ResourceOwner
520 : *
521 : * Caller must have previously done ResourceOwnerEnlarge()
522 : */
523 : void
524 337817548 : ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
525 : {
526 : uint32 idx;
527 :
528 : /* sanity check the ResourceOwnerDesc */
529 : Assert(kind->release_phase != 0);
530 : Assert(kind->release_priority != 0);
531 :
532 : /*
533 : * Mustn't try to remember more resources after we have already started
534 : * releasing. We already checked this in ResourceOwnerEnlarge.
535 : */
536 : Assert(!owner->releasing);
537 : Assert(!owner->sorted);
538 :
539 337817548 : if (owner->narr >= RESOWNER_ARRAY_SIZE)
540 : {
541 : /* forgot to call ResourceOwnerEnlarge? */
542 0 : elog(ERROR, "ResourceOwnerRemember called but array was full");
543 : }
544 :
545 : /* Append to the array. */
546 337817548 : idx = owner->narr;
547 337817548 : owner->arr[idx].item = value;
548 337817548 : owner->arr[idx].kind = kind;
549 337817548 : owner->narr++;
550 337817548 : }
551 :
552 : /*
553 : * Forget that an object is owned by a ResourceOwner
554 : *
555 : * Note: If same resource ID is associated with the ResourceOwner more than
556 : * once, one instance is removed.
557 : *
558 : * Note: Forgetting a resource does not guarantee that there is room to
559 : * remember a new resource. One exception is when you forget the most
560 : * recently remembered resource; that does make room for a new remember call.
561 : * Some code callers rely on that exception.
562 : */
563 : void
564 337195578 : ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
565 : {
566 : /*
567 : * Mustn't call this after we have already started releasing resources.
568 : * (Release callback functions are not allowed to release additional
569 : * resources.)
570 : */
571 337195578 : if (owner->releasing)
572 2 : elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
573 : Assert(!owner->sorted);
574 :
575 : /* Search through all items in the array first. */
576 443206188 : for (int i = owner->narr - 1; i >= 0; i--)
577 : {
578 442317128 : if (owner->arr[i].item == value &&
579 336307068 : owner->arr[i].kind == kind)
580 : {
581 336306516 : owner->arr[i] = owner->arr[owner->narr - 1];
582 336306516 : owner->narr--;
583 :
584 : #ifdef RESOWNER_STATS
585 : narray_lookups++;
586 : #endif
587 336306516 : return;
588 : }
589 : }
590 :
591 : /* Search hash */
592 889060 : if (owner->nhash > 0)
593 : {
594 889060 : uint32 mask = owner->capacity - 1;
595 : uint32 idx;
596 :
597 889060 : idx = hash_resource_elem(value, kind) & mask;
598 199192640 : for (uint32 i = 0; i < owner->capacity; i++)
599 : {
600 199192640 : if (owner->hash[idx].item == value &&
601 893456 : owner->hash[idx].kind == kind)
602 : {
603 889060 : owner->hash[idx].item = (Datum) 0;
604 889060 : owner->hash[idx].kind = NULL;
605 889060 : owner->nhash--;
606 :
607 : #ifdef RESOWNER_STATS
608 : nhash_lookups++;
609 : #endif
610 889060 : return;
611 : }
612 198303580 : idx = (idx + 1) & mask;
613 : }
614 : }
615 :
616 : /*
617 : * Use %p to print the reference, since most objects tracked by a resource
618 : * owner are pointers. It's a bit misleading if it's not a pointer, but
619 : * this is a programmer error, anyway.
620 : */
621 0 : elog(ERROR, "%s %p is not owned by resource owner %s",
622 : kind->name, DatumGetPointer(value), owner->name);
623 : }
624 :
625 : /*
626 : * ResourceOwnerRelease
627 : * Release all resources owned by a ResourceOwner and its descendants,
628 : * but don't delete the owner objects themselves.
629 : *
630 : * Note that this executes just one phase of release, and so typically
631 : * must be called three times. We do it this way because (a) we want to
632 : * do all the recursion separately for each phase, thereby preserving
633 : * the needed order of operations; and (b) xact.c may have other operations
634 : * to do between the phases.
635 : *
636 : * phase: release phase to execute
637 : * isCommit: true for successful completion of a query or transaction,
638 : * false for unsuccessful
639 : * isTopLevel: true if completing a main transaction, else false
640 : *
641 : * isCommit is passed because some modules may expect that their resources
642 : * were all released already if the transaction or portal finished normally.
643 : * If so it is reasonable to give a warning (NOT an error) should any
644 : * unreleased resources be present. When isCommit is false, such warnings
645 : * are generally inappropriate.
646 : *
647 : * isTopLevel is passed when we are releasing TopTransactionResourceOwner
648 : * at completion of a main transaction. This generally means that *all*
649 : * resources will be released, and so we can optimize things a bit.
650 : *
651 : * NOTE: After starting the release process, by calling this function, no new
652 : * resources can be remembered in the resource owner. You also cannot call
653 : * ResourceOwnerForget on any previously remembered resources to release
654 : * resources "in retail" after that, you must let the bulk release take care
655 : * of them.
656 : */
657 : void
658 4612198 : ResourceOwnerRelease(ResourceOwner owner,
659 : ResourceReleasePhase phase,
660 : bool isCommit,
661 : bool isTopLevel)
662 : {
663 : /* There's not currently any setup needed before recursing */
664 4612198 : ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
665 :
666 : #ifdef RESOWNER_STATS
667 : if (isTopLevel)
668 : {
669 : elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
670 : narray_lookups, nhash_lookups);
671 : narray_lookups = 0;
672 : nhash_lookups = 0;
673 : }
674 : #endif
675 4612198 : }
676 :
677 : static void
678 4818634 : ResourceOwnerReleaseInternal(ResourceOwner owner,
679 : ResourceReleasePhase phase,
680 : bool isCommit,
681 : bool isTopLevel)
682 : {
683 : ResourceOwner child;
684 : ResourceOwner save;
685 : ResourceReleaseCallbackItem *item;
686 : ResourceReleaseCallbackItem *next;
687 :
688 : /* Recurse to handle descendants */
689 5025070 : for (child = owner->firstchild; child != NULL; child = child->nextchild)
690 206436 : ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
691 :
692 : /*
693 : * To release the resources in the right order, sort them by phase and
694 : * priority.
695 : *
696 : * The ReleaseResource callback functions are not allowed to remember or
697 : * forget any other resources after this. Otherwise we lose track of where
698 : * we are in processing the hash/array.
699 : */
700 4818634 : if (!owner->releasing)
701 : {
702 : Assert(phase == RESOURCE_RELEASE_BEFORE_LOCKS);
703 : Assert(!owner->sorted);
704 1606210 : owner->releasing = true;
705 : }
706 : else
707 : {
708 : /*
709 : * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
710 : * the first call to ResourceOwnerRelease. But if an error happens
711 : * between the release phases, we might get called again for the same
712 : * ResourceOwner from AbortTransaction.
713 : */
714 : }
715 4818634 : if (!owner->sorted)
716 : {
717 1606210 : ResourceOwnerSort(owner);
718 1606210 : owner->sorted = true;
719 : }
720 :
721 : /*
722 : * Make CurrentResourceOwner point to me, so that the release callback
723 : * functions know which resource owner is been released.
724 : */
725 4818634 : save = CurrentResourceOwner;
726 4818634 : CurrentResourceOwner = owner;
727 :
728 4818634 : if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
729 : {
730 : /*
731 : * Release all resources that need to be released before the locks.
732 : *
733 : * During a commit, there shouldn't be any remaining resources ---
734 : * that would indicate failure to clean up the executor correctly ---
735 : * so issue warnings. In the abort case, just clean up quietly.
736 : */
737 1606214 : ResourceOwnerReleaseAll(owner, phase, isCommit);
738 :
739 1606248 : while (!dlist_is_empty(&owner->aio_handles))
740 : {
741 34 : dlist_node *node = dlist_head_node(&owner->aio_handles);
742 :
743 34 : pgaio_io_release_resowner(node, !isCommit);
744 : }
745 : }
746 3212420 : else if (phase == RESOURCE_RELEASE_LOCKS)
747 : {
748 1606210 : if (isTopLevel)
749 : {
750 : /*
751 : * For a top-level xact we are going to release all locks (or at
752 : * least all non-session locks), so just do a single lmgr call at
753 : * the top of the recursion.
754 : */
755 897146 : if (owner == TopTransactionResourceOwner)
756 : {
757 818766 : ProcReleaseLocks(isCommit);
758 818766 : ReleasePredicateLocks(isCommit, false);
759 : }
760 : }
761 : else
762 : {
763 : /*
764 : * Release locks retail. Note that if we are committing a
765 : * subtransaction, we do NOT release its locks yet, but transfer
766 : * them to the parent.
767 : */
768 : LOCALLOCK **locks;
769 : int nlocks;
770 :
771 : Assert(owner->parent != NULL);
772 :
773 : /*
774 : * Pass the list of locks owned by this resource owner to the lock
775 : * manager, unless it has overflowed.
776 : */
777 709064 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
778 : {
779 6910 : locks = NULL;
780 6910 : nlocks = 0;
781 : }
782 : else
783 : {
784 702154 : locks = owner->locks;
785 702154 : nlocks = owner->nlocks;
786 : }
787 :
788 709064 : if (isCommit)
789 698432 : LockReassignCurrentOwner(locks, nlocks);
790 : else
791 10632 : LockReleaseCurrentOwner(locks, nlocks);
792 : }
793 : }
794 1606210 : else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
795 : {
796 : /*
797 : * Release all resources that need to be released after the locks.
798 : */
799 1606210 : ResourceOwnerReleaseAll(owner, phase, isCommit);
800 : }
801 :
802 : /* Let add-on modules get a chance too */
803 4818634 : for (item = ResourceRelease_callbacks; item; item = next)
804 : {
805 : /* allow callbacks to unregister themselves when called */
806 0 : next = item->next;
807 0 : item->callback(phase, isCommit, isTopLevel, item->arg);
808 : }
809 :
810 4818634 : CurrentResourceOwner = save;
811 4818634 : }
812 :
813 : /*
814 : * ResourceOwnerReleaseAllOfKind
815 : * Release all resources of a certain type held by this owner.
816 : */
817 : void
818 16394 : ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
819 : {
820 : /* Mustn't call this after we have already started releasing resources. */
821 16394 : if (owner->releasing)
822 0 : elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
823 : Assert(!owner->sorted);
824 :
825 : /*
826 : * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
827 : * while we're scanning the owner. Enlarging the hash would cause us to
828 : * lose track of the point we're scanning.
829 : */
830 16394 : owner->releasing = true;
831 :
832 : /* Array first */
833 71446 : for (int i = 0; i < owner->narr; i++)
834 : {
835 55052 : if (owner->arr[i].kind == kind)
836 : {
837 55052 : Datum value = owner->arr[i].item;
838 :
839 55052 : owner->arr[i] = owner->arr[owner->narr - 1];
840 55052 : owner->narr--;
841 55052 : i--;
842 :
843 55052 : kind->ReleaseResource(value);
844 : }
845 : }
846 :
847 : /* Then hash */
848 50058 : for (int i = 0; i < owner->capacity; i++)
849 : {
850 33664 : if (owner->hash[i].kind == kind)
851 : {
852 16832 : Datum value = owner->hash[i].item;
853 :
854 16832 : owner->hash[i].item = (Datum) 0;
855 16832 : owner->hash[i].kind = NULL;
856 16832 : owner->nhash--;
857 :
858 16832 : kind->ReleaseResource(value);
859 : }
860 : }
861 16394 : owner->releasing = false;
862 16394 : }
863 :
864 : /*
865 : * ResourceOwnerDelete
866 : * Delete an owner object and its descendants.
867 : *
868 : * The caller must have already released all resources in the object tree.
869 : */
870 : void
871 1596820 : ResourceOwnerDelete(ResourceOwner owner)
872 : {
873 : /* We had better not be deleting CurrentResourceOwner ... */
874 : Assert(owner != CurrentResourceOwner);
875 :
876 : /* And it better not own any resources, either */
877 : Assert(owner->narr == 0);
878 : Assert(owner->nhash == 0);
879 : Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
880 :
881 : /*
882 : * Delete children. The recursive call will delink the child from me, so
883 : * just iterate as long as there is a child.
884 : */
885 1665638 : while (owner->firstchild != NULL)
886 68818 : ResourceOwnerDelete(owner->firstchild);
887 :
888 : /*
889 : * We delink the owner from its parent before deleting it, so that if
890 : * there's an error we won't have deleted/busted owners still attached to
891 : * the owner tree. Better a leak than a crash.
892 : */
893 1596820 : ResourceOwnerNewParent(owner, NULL);
894 :
895 : /* And free the object. */
896 1596820 : if (owner->hash)
897 8960 : pfree(owner->hash);
898 1596820 : pfree(owner);
899 1596820 : }
900 :
901 : /*
902 : * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
903 : */
904 : ResourceOwner
905 698432 : ResourceOwnerGetParent(ResourceOwner owner)
906 : {
907 698432 : return owner->parent;
908 : }
909 :
910 : /*
911 : * Reassign a ResourceOwner to have a new parent
912 : */
913 : void
914 1596894 : ResourceOwnerNewParent(ResourceOwner owner,
915 : ResourceOwner newparent)
916 : {
917 1596894 : ResourceOwner oldparent = owner->parent;
918 :
919 1596894 : if (oldparent)
920 : {
921 776642 : if (owner == oldparent->firstchild)
922 761156 : oldparent->firstchild = owner->nextchild;
923 : else
924 : {
925 : ResourceOwner child;
926 :
927 17966 : for (child = oldparent->firstchild; child; child = child->nextchild)
928 : {
929 17966 : if (owner == child->nextchild)
930 : {
931 15486 : child->nextchild = owner->nextchild;
932 15486 : break;
933 : }
934 : }
935 : }
936 : }
937 :
938 1596894 : if (newparent)
939 : {
940 : Assert(owner != newparent);
941 74 : owner->parent = newparent;
942 74 : owner->nextchild = newparent->firstchild;
943 74 : newparent->firstchild = owner;
944 : }
945 : else
946 : {
947 1596820 : owner->parent = NULL;
948 1596820 : owner->nextchild = NULL;
949 : }
950 1596894 : }
951 :
952 : /*
953 : * Register or deregister callback functions for resource cleanup
954 : *
955 : * These functions can be used by dynamically loaded modules. These used
956 : * to be the only way for an extension to register custom resource types
957 : * with a resource owner, but nowadays it is easier to define a new
958 : * ResourceOwnerDesc with custom callbacks.
959 : */
960 : void
961 0 : RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
962 : {
963 : ResourceReleaseCallbackItem *item;
964 :
965 : item = (ResourceReleaseCallbackItem *)
966 0 : MemoryContextAlloc(TopMemoryContext,
967 : sizeof(ResourceReleaseCallbackItem));
968 0 : item->callback = callback;
969 0 : item->arg = arg;
970 0 : item->next = ResourceRelease_callbacks;
971 0 : ResourceRelease_callbacks = item;
972 0 : }
973 :
974 : void
975 0 : UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
976 : {
977 : ResourceReleaseCallbackItem *item;
978 : ResourceReleaseCallbackItem *prev;
979 :
980 0 : prev = NULL;
981 0 : for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
982 : {
983 0 : if (item->callback == callback && item->arg == arg)
984 : {
985 0 : if (prev)
986 0 : prev->next = item->next;
987 : else
988 0 : ResourceRelease_callbacks = item->next;
989 0 : pfree(item);
990 0 : break;
991 : }
992 : }
993 0 : }
994 :
995 : /*
996 : * Establish an AuxProcessResourceOwner for the current process.
997 : */
998 : void
999 10262 : CreateAuxProcessResourceOwner(void)
1000 : {
1001 : Assert(AuxProcessResourceOwner == NULL);
1002 : Assert(CurrentResourceOwner == NULL);
1003 10262 : AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
1004 10262 : CurrentResourceOwner = AuxProcessResourceOwner;
1005 :
1006 : /*
1007 : * Register a shmem-exit callback for cleanup of aux-process resource
1008 : * owner. (This needs to run after, e.g., ShutdownXLOG.)
1009 : */
1010 10262 : on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
1011 10262 : }
1012 :
1013 : /*
1014 : * Convenience routine to release all resources tracked in
1015 : * AuxProcessResourceOwner (but that resowner is not destroyed here).
1016 : * Warn about leaked resources if isCommit is true.
1017 : */
1018 : void
1019 10882 : ReleaseAuxProcessResources(bool isCommit)
1020 : {
1021 : /*
1022 : * At this writing, the only thing that could actually get released is
1023 : * buffer pins; but we may as well do the full release protocol.
1024 : */
1025 10882 : ResourceOwnerRelease(AuxProcessResourceOwner,
1026 : RESOURCE_RELEASE_BEFORE_LOCKS,
1027 : isCommit, true);
1028 10882 : ResourceOwnerRelease(AuxProcessResourceOwner,
1029 : RESOURCE_RELEASE_LOCKS,
1030 : isCommit, true);
1031 10882 : ResourceOwnerRelease(AuxProcessResourceOwner,
1032 : RESOURCE_RELEASE_AFTER_LOCKS,
1033 : isCommit, true);
1034 : /* allow it to be reused */
1035 10882 : AuxProcessResourceOwner->releasing = false;
1036 10882 : AuxProcessResourceOwner->sorted = false;
1037 10882 : }
1038 :
1039 : /*
1040 : * Shmem-exit callback for the same.
1041 : * Warn about leaked resources if process exit code is zero (ie normal).
1042 : */
1043 : static void
1044 10262 : ReleaseAuxProcessResourcesCallback(int code, Datum arg)
1045 : {
1046 10262 : bool isCommit = (code == 0);
1047 :
1048 10262 : ReleaseAuxProcessResources(isCommit);
1049 10262 : }
1050 :
1051 : /*
1052 : * Remember that a Local Lock is owned by a ResourceOwner
1053 : *
1054 : * This is different from the generic ResourceOwnerRemember in that the list of
1055 : * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
1056 : * and when it overflows, we stop tracking locks. The point of only remembering
1057 : * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
1058 : * ResourceOwnerForgetLock doesn't need to scan through a large array to find
1059 : * the entry.
1060 : */
1061 : void
1062 35585044 : ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
1063 : {
1064 : Assert(locallock != NULL);
1065 :
1066 35585044 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
1067 3878302 : return; /* we have already overflowed */
1068 :
1069 31706742 : if (owner->nlocks < MAX_RESOWNER_LOCKS)
1070 31687696 : owner->locks[owner->nlocks] = locallock;
1071 : else
1072 : {
1073 : /* overflowed */
1074 : }
1075 31706742 : owner->nlocks++;
1076 : }
1077 :
1078 : /*
1079 : * Forget that a Local Lock is owned by a ResourceOwner
1080 : */
1081 : void
1082 35585044 : ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1083 : {
1084 : int i;
1085 :
1086 35585044 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
1087 4183038 : return; /* we have overflowed */
1088 :
1089 : Assert(owner->nlocks > 0);
1090 35425464 : for (i = owner->nlocks - 1; i >= 0; i--)
1091 : {
1092 35425464 : if (locallock == owner->locks[i])
1093 : {
1094 31402006 : owner->locks[i] = owner->locks[owner->nlocks - 1];
1095 31402006 : owner->nlocks--;
1096 31402006 : return;
1097 : }
1098 : }
1099 0 : elog(ERROR, "lock reference %p is not owned by resource owner %s",
1100 : locallock, owner->name);
1101 : }
1102 :
1103 : void
1104 2436110 : ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1105 : {
1106 2436110 : dlist_push_tail(&owner->aio_handles, ioh_node);
1107 2436110 : }
1108 :
1109 : void
1110 2436110 : ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1111 : {
1112 2436110 : dlist_delete_from(&owner->aio_handles, ioh_node);
1113 2436110 : }
|