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