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