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