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.
10 *
11 *
12 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
13 * Portions Copyright (c) 1994, Regents of the University of California
14 *
15 *
16 * IDENTIFICATION
17 * src/backend/utils/resowner/resowner.c
18 *
19 *-------------------------------------------------------------------------
20 */
21 #include "postgres.h"
22
23 #include "common/cryptohash.h"
24 #include "common/hashfn.h"
25 #include "common/hmac.h"
26 #include "jit/jit.h"
27 #include "storage/bufmgr.h"
28 #include "storage/ipc.h"
29 #include "storage/predicate.h"
30 #include "storage/proc.h"
31 #include "utils/memutils.h"
32 #include "utils/rel.h"
33 #include "utils/resowner_private.h"
34 #include "utils/snapmgr.h"
35
36
37 /*
38 * All resource IDs managed by this code are required to fit into a Datum,
39 * which is fine since they are generally pointers or integers.
40 *
41 * Provide Datum conversion macros for a couple of things that are really
42 * just "int".
43 */
44 #define FileGetDatum(file) Int32GetDatum(file)
45 #define DatumGetFile(datum) ((File) DatumGetInt32(datum))
46 #define BufferGetDatum(buffer) Int32GetDatum(buffer)
47 #define DatumGetBuffer(datum) ((Buffer) DatumGetInt32(datum))
48
49 /*
50 * ResourceArray is a common structure for storing all types of resource IDs.
51 *
52 * We manage small sets of resource IDs by keeping them in a simple array:
53 * itemsarr[k] holds an ID, for 0 <= k < nitems <= maxitems = capacity.
54 *
55 * If a set grows large, we switch over to using open-addressing hashing.
56 * Then, itemsarr[] is a hash table of "capacity" slots, with each
57 * slot holding either an ID or "invalidval". nitems is the number of valid
58 * items present; if it would exceed maxitems, we enlarge the array and
59 * re-hash. In this mode, maxitems should be rather less than capacity so
60 * that we don't waste too much time searching for empty slots.
61 *
62 * In either mode, lastidx remembers the location of the last item inserted
63 * or returned by GetAny; this speeds up searches in ResourceArrayRemove.
64 */
65 typedef struct ResourceArray
66 {
67 Datum *itemsarr; /* buffer for storing values */
68 Datum invalidval; /* value that is considered invalid */
69 uint32 capacity; /* allocated length of itemsarr[] */
70 uint32 nitems; /* how many items are stored in items array */
71 uint32 maxitems; /* current limit on nitems before enlarging */
72 uint32 lastidx; /* index of last item returned by GetAny */
73 } ResourceArray;
74
75 /*
76 * Initially allocated size of a ResourceArray. Must be power of two since
77 * we'll use (arraysize - 1) as mask for hashing.
78 */
79 #define RESARRAY_INIT_SIZE 16
80
81 /*
82 * When to switch to hashing vs. simple array logic in a ResourceArray.
83 */
84 #define RESARRAY_MAX_ARRAY 64
85 #define RESARRAY_IS_ARRAY(resarr) ((resarr)->capacity <= RESARRAY_MAX_ARRAY)
86
87 /*
88 * How many items may be stored in a resource array of given capacity.
89 * When this number is reached, we must resize.
90 */
91 #define RESARRAY_MAX_ITEMS(capacity) \
92 ((capacity) <= RESARRAY_MAX_ARRAY ? (capacity) : (capacity)/4 * 3)
93
94 /*
95 * To speed up bulk releasing or reassigning locks from a resource owner to
96 * its parent, each resource owner has a small cache of locks it owns. The
97 * lock manager has the same information in its local lock hash table, and
98 * we fall back on that if cache overflows, but traversing the hash table
99 * is slower when there are a lot of locks belonging to other resource owners.
100 *
101 * MAX_RESOWNER_LOCKS is the size of the per-resource owner cache. It's
102 * chosen based on some testing with pg_dump with a large schema. When the
103 * tests were done (on 9.2), resource owners in a pg_dump run contained up
104 * to 9 locks, regardless of the schema size, except for the top resource
105 * owner which contained much more (overflowing the cache). 15 seems like a
106 * nice round number that's somewhat higher than what pg_dump needs. Note that
107 * making this number larger is not free - the bigger the cache, the slower
108 * it is to release locks (in retail), when a resource owner holds many locks.
109 */
110 #define MAX_RESOWNER_LOCKS 15
111
112 /*
113 * ResourceOwner objects look like this
114 */
115 typedef struct ResourceOwnerData
116 {
117 ResourceOwner parent; /* NULL if no parent (toplevel owner) */
118 ResourceOwner firstchild; /* head of linked list of children */
119 ResourceOwner nextchild; /* next child of same parent */
120 const char *name; /* name (just for debugging) */
121
122 /* We have built-in support for remembering: */
123 ResourceArray bufferarr; /* owned buffers */
124 ResourceArray catrefarr; /* catcache references */
125 ResourceArray catlistrefarr; /* catcache-list pins */
126 ResourceArray relrefarr; /* relcache references */
127 ResourceArray planrefarr; /* plancache references */
128 ResourceArray tupdescarr; /* tupdesc references */
129 ResourceArray snapshotarr; /* snapshot references */
130 ResourceArray filearr; /* open temporary files */
131 ResourceArray dsmarr; /* dynamic shmem segments */
132 ResourceArray jitarr; /* JIT contexts */
133 ResourceArray cryptohasharr; /* cryptohash contexts */
134 ResourceArray hmacarr; /* HMAC contexts */
135
136 /* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
137 int nlocks; /* number of owned locks */
138 LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
139 } ResourceOwnerData;
140
141
142 /*****************************************************************************
143 * GLOBAL MEMORY *
144 *****************************************************************************/
145
146 ResourceOwner CurrentResourceOwner = NULL;
147 ResourceOwner CurTransactionResourceOwner = NULL;
148 ResourceOwner TopTransactionResourceOwner = NULL;
149 ResourceOwner AuxProcessResourceOwner = NULL;
150
151 /*
152 * List of add-on callbacks for resource releasing
153 */
154 typedef struct ResourceReleaseCallbackItem
155 {
156 struct ResourceReleaseCallbackItem *next;
157 ResourceReleaseCallback callback;
158 void *arg;
159 } ResourceReleaseCallbackItem;
160
161 static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
162
163
164 /* Internal routines */
165 static void ResourceArrayInit(ResourceArray *resarr, Datum invalidval);
166 static void ResourceArrayEnlarge(ResourceArray *resarr);
167 static void ResourceArrayAdd(ResourceArray *resarr, Datum value);
168 static bool ResourceArrayRemove(ResourceArray *resarr, Datum value);
169 static bool ResourceArrayGetAny(ResourceArray *resarr, Datum *value);
170 static void ResourceArrayFree(ResourceArray *resarr);
171 static void ResourceOwnerReleaseInternal(ResourceOwner owner,
172 ResourceReleasePhase phase,
173 bool isCommit,
174 bool isTopLevel);
175 static void ReleaseAuxProcessResourcesCallback(int code, Datum arg);
176 static void PrintRelCacheLeakWarning(Relation rel);
177 static void PrintPlanCacheLeakWarning(CachedPlan *plan);
178 static void PrintTupleDescLeakWarning(TupleDesc tupdesc);
179 static void PrintSnapshotLeakWarning(Snapshot snapshot);
180 static void PrintFileLeakWarning(File file);
181 static void PrintDSMLeakWarning(dsm_segment *seg);
182 static void PrintCryptoHashLeakWarning(Datum handle);
183 static void PrintHMACLeakWarning(Datum handle);
184
185
186 /*****************************************************************************
187 * INTERNAL ROUTINES *
188 *****************************************************************************/
189
190
191 /*
192 * Initialize a ResourceArray
193 */
194 static void
ResourceArrayInit(ResourceArray * resarr,Datum invalidval)195 ResourceArrayInit(ResourceArray *resarr, Datum invalidval)
196 {
197 /* Assert it's empty */
198 Assert(resarr->itemsarr == NULL);
199 Assert(resarr->capacity == 0);
200 Assert(resarr->nitems == 0);
201 Assert(resarr->maxitems == 0);
202 /* Remember the appropriate "invalid" value */
203 resarr->invalidval = invalidval;
204 /* We don't allocate any storage until needed */
205 }
206
207 /*
208 * Make sure there is room for at least one more resource in an array.
209 *
210 * This is separate from actually inserting a resource because if we run out
211 * of memory, it's critical to do so *before* acquiring the resource.
212 */
213 static void
ResourceArrayEnlarge(ResourceArray * resarr)214 ResourceArrayEnlarge(ResourceArray *resarr)
215 {
216 uint32 i,
217 oldcap,
218 newcap;
219 Datum *olditemsarr;
220 Datum *newitemsarr;
221
222 if (resarr->nitems < resarr->maxitems)
223 return; /* no work needed */
224
225 olditemsarr = resarr->itemsarr;
226 oldcap = resarr->capacity;
227
228 /* Double the capacity of the array (capacity must stay a power of 2!) */
229 newcap = (oldcap > 0) ? oldcap * 2 : RESARRAY_INIT_SIZE;
230 newitemsarr = (Datum *) MemoryContextAlloc(TopMemoryContext,
231 newcap * sizeof(Datum));
232 for (i = 0; i < newcap; i++)
233 newitemsarr[i] = resarr->invalidval;
234
235 /* We assume we can't fail below this point, so OK to scribble on resarr */
236 resarr->itemsarr = newitemsarr;
237 resarr->capacity = newcap;
238 resarr->maxitems = RESARRAY_MAX_ITEMS(newcap);
239 resarr->nitems = 0;
240
241 if (olditemsarr != NULL)
242 {
243 /*
244 * Transfer any pre-existing entries into the new array; they don't
245 * necessarily go where they were before, so this simple logic is the
246 * best way. Note that if we were managing the set as a simple array,
247 * the entries after nitems are garbage, but that shouldn't matter
248 * because we won't get here unless nitems was equal to oldcap.
249 */
250 for (i = 0; i < oldcap; i++)
251 {
252 if (olditemsarr[i] != resarr->invalidval)
253 ResourceArrayAdd(resarr, olditemsarr[i]);
254 }
255
256 /* And release old array. */
257 pfree(olditemsarr);
258 }
259
260 Assert(resarr->nitems < resarr->maxitems);
261 }
262
263 /*
264 * Add a resource to ResourceArray
265 *
266 * Caller must have previously done ResourceArrayEnlarge()
267 */
268 static void
ResourceArrayAdd(ResourceArray * resarr,Datum value)269 ResourceArrayAdd(ResourceArray *resarr, Datum value)
270 {
271 uint32 idx;
272
273 Assert(value != resarr->invalidval);
274 Assert(resarr->nitems < resarr->maxitems);
275
276 if (RESARRAY_IS_ARRAY(resarr))
277 {
278 /* Append to linear array. */
279 idx = resarr->nitems;
280 }
281 else
282 {
283 /* Insert into first free slot at or after hash location. */
284 uint32 mask = resarr->capacity - 1;
285
286 idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
287 for (;;)
288 {
289 if (resarr->itemsarr[idx] == resarr->invalidval)
290 break;
291 idx = (idx + 1) & mask;
292 }
293 }
294 resarr->lastidx = idx;
295 resarr->itemsarr[idx] = value;
296 resarr->nitems++;
297 }
298
299 /*
300 * Remove a resource from ResourceArray
301 *
302 * Returns true on success, false if resource was not found.
303 *
304 * Note: if same resource ID appears more than once, one instance is removed.
305 */
306 static bool
ResourceArrayRemove(ResourceArray * resarr,Datum value)307 ResourceArrayRemove(ResourceArray *resarr, Datum value)
308 {
309 uint32 i,
310 idx,
311 lastidx = resarr->lastidx;
312
313 Assert(value != resarr->invalidval);
314
315 /* Search through all items, but try lastidx first. */
316 if (RESARRAY_IS_ARRAY(resarr))
317 {
318 if (lastidx < resarr->nitems &&
319 resarr->itemsarr[lastidx] == value)
320 {
321 resarr->itemsarr[lastidx] = resarr->itemsarr[resarr->nitems - 1];
322 resarr->nitems--;
323 /* Update lastidx to make reverse-order removals fast. */
324 resarr->lastidx = resarr->nitems - 1;
325 return true;
326 }
327 for (i = 0; i < resarr->nitems; i++)
328 {
329 if (resarr->itemsarr[i] == value)
330 {
331 resarr->itemsarr[i] = resarr->itemsarr[resarr->nitems - 1];
332 resarr->nitems--;
333 /* Update lastidx to make reverse-order removals fast. */
334 resarr->lastidx = resarr->nitems - 1;
335 return true;
336 }
337 }
338 }
339 else
340 {
341 uint32 mask = resarr->capacity - 1;
342
343 if (lastidx < resarr->capacity &&
344 resarr->itemsarr[lastidx] == value)
345 {
346 resarr->itemsarr[lastidx] = resarr->invalidval;
347 resarr->nitems--;
348 return true;
349 }
350 idx = DatumGetUInt32(hash_any((void *) &value, sizeof(value))) & mask;
351 for (i = 0; i < resarr->capacity; i++)
352 {
353 if (resarr->itemsarr[idx] == value)
354 {
355 resarr->itemsarr[idx] = resarr->invalidval;
356 resarr->nitems--;
357 return true;
358 }
359 idx = (idx + 1) & mask;
360 }
361 }
362
363 return false;
364 }
365
366 /*
367 * Get any convenient entry in a ResourceArray.
368 *
369 * "Convenient" is defined as "easy for ResourceArrayRemove to remove";
370 * we help that along by setting lastidx to match. This avoids O(N^2) cost
371 * when removing all ResourceArray items during ResourceOwner destruction.
372 *
373 * Returns true if we found an element, or false if the array is empty.
374 */
375 static bool
ResourceArrayGetAny(ResourceArray * resarr,Datum * value)376 ResourceArrayGetAny(ResourceArray *resarr, Datum *value)
377 {
378 if (resarr->nitems == 0)
379 return false;
380
381 if (RESARRAY_IS_ARRAY(resarr))
382 {
383 /* Linear array: just return the first element. */
384 resarr->lastidx = 0;
385 }
386 else
387 {
388 /* Hash: search forward from wherever we were last. */
389 uint32 mask = resarr->capacity - 1;
390
391 for (;;)
392 {
393 resarr->lastidx &= mask;
394 if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
395 break;
396 resarr->lastidx++;
397 }
398 }
399
400 *value = resarr->itemsarr[resarr->lastidx];
401 return true;
402 }
403
404 /*
405 * Trash a ResourceArray (we don't care about its state after this)
406 */
407 static void
ResourceArrayFree(ResourceArray * resarr)408 ResourceArrayFree(ResourceArray *resarr)
409 {
410 if (resarr->itemsarr)
411 pfree(resarr->itemsarr);
412 }
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
ResourceOwnerCreate(ResourceOwner parent,const char * name)428 ResourceOwnerCreate(ResourceOwner parent, const char *name)
429 {
430 ResourceOwner owner;
431
432 owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
433 sizeof(ResourceOwnerData));
434 owner->name = name;
435
436 if (parent)
437 {
438 owner->parent = parent;
439 owner->nextchild = parent->firstchild;
440 parent->firstchild = owner;
441 }
442
443 ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer));
444 ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL));
445 ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL));
446 ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL));
447 ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL));
448 ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL));
449 ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
450 ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
451 ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
452 ResourceArrayInit(&(owner->jitarr), PointerGetDatum(NULL));
453 ResourceArrayInit(&(owner->cryptohasharr), PointerGetDatum(NULL));
454 ResourceArrayInit(&(owner->hmacarr), PointerGetDatum(NULL));
455
456 return owner;
457 }
458
459 /*
460 * ResourceOwnerRelease
461 * Release all resources owned by a ResourceOwner and its descendants,
462 * but don't delete the owner objects themselves.
463 *
464 * Note that this executes just one phase of release, and so typically
465 * must be called three times. We do it this way because (a) we want to
466 * do all the recursion separately for each phase, thereby preserving
467 * the needed order of operations; and (b) xact.c may have other operations
468 * to do between the phases.
469 *
470 * phase: release phase to execute
471 * isCommit: true for successful completion of a query or transaction,
472 * false for unsuccessful
473 * isTopLevel: true if completing a main transaction, else false
474 *
475 * isCommit is passed because some modules may expect that their resources
476 * were all released already if the transaction or portal finished normally.
477 * If so it is reasonable to give a warning (NOT an error) should any
478 * unreleased resources be present. When isCommit is false, such warnings
479 * are generally inappropriate.
480 *
481 * isTopLevel is passed when we are releasing TopTransactionResourceOwner
482 * at completion of a main transaction. This generally means that *all*
483 * resources will be released, and so we can optimize things a bit.
484 */
485 void
ResourceOwnerRelease(ResourceOwner owner,ResourceReleasePhase phase,bool isCommit,bool isTopLevel)486 ResourceOwnerRelease(ResourceOwner owner,
487 ResourceReleasePhase phase,
488 bool isCommit,
489 bool isTopLevel)
490 {
491 /* There's not currently any setup needed before recursing */
492 ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
493 }
494
495 static void
ResourceOwnerReleaseInternal(ResourceOwner owner,ResourceReleasePhase phase,bool isCommit,bool isTopLevel)496 ResourceOwnerReleaseInternal(ResourceOwner owner,
497 ResourceReleasePhase phase,
498 bool isCommit,
499 bool isTopLevel)
500 {
501 ResourceOwner child;
502 ResourceOwner save;
503 ResourceReleaseCallbackItem *item;
504 Datum foundres;
505
506 /* Recurse to handle descendants */
507 for (child = owner->firstchild; child != NULL; child = child->nextchild)
508 ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
509
510 /*
511 * Make CurrentResourceOwner point to me, so that ReleaseBuffer etc don't
512 * get confused.
513 */
514 save = CurrentResourceOwner;
515 CurrentResourceOwner = owner;
516
517 if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
518 {
519 /*
520 * Release buffer pins. Note that ReleaseBuffer will remove the
521 * buffer entry from our array, so we just have to iterate till there
522 * are none.
523 *
524 * During a commit, there shouldn't be any remaining pins --- that
525 * would indicate failure to clean up the executor correctly --- so
526 * issue warnings. In the abort case, just clean up quietly.
527 */
528 while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
529 {
530 Buffer res = DatumGetBuffer(foundres);
531
532 if (isCommit)
533 PrintBufferLeakWarning(res);
534 ReleaseBuffer(res);
535 }
536
537 /* Ditto for relcache references */
538 while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
539 {
540 Relation res = (Relation) DatumGetPointer(foundres);
541
542 if (isCommit)
543 PrintRelCacheLeakWarning(res);
544 RelationClose(res);
545 }
546
547 /* Ditto for dynamic shared memory segments */
548 while (ResourceArrayGetAny(&(owner->dsmarr), &foundres))
549 {
550 dsm_segment *res = (dsm_segment *) DatumGetPointer(foundres);
551
552 if (isCommit)
553 PrintDSMLeakWarning(res);
554 dsm_detach(res);
555 }
556
557 /* Ditto for JIT contexts */
558 while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
559 {
560 JitContext *context = (JitContext *) PointerGetDatum(foundres);
561
562 jit_release_context(context);
563 }
564
565 /* Ditto for cryptohash contexts */
566 while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
567 {
568 pg_cryptohash_ctx *context =
569 (pg_cryptohash_ctx *) PointerGetDatum(foundres);
570
571 if (isCommit)
572 PrintCryptoHashLeakWarning(foundres);
573 pg_cryptohash_free(context);
574 }
575
576 /* Ditto for HMAC contexts */
577 while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
578 {
579 pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres);
580
581 if (isCommit)
582 PrintHMACLeakWarning(foundres);
583 pg_hmac_free(context);
584 }
585 }
586 else if (phase == RESOURCE_RELEASE_LOCKS)
587 {
588 if (isTopLevel)
589 {
590 /*
591 * For a top-level xact we are going to release all locks (or at
592 * least all non-session locks), so just do a single lmgr call at
593 * the top of the recursion.
594 */
595 if (owner == TopTransactionResourceOwner)
596 {
597 ProcReleaseLocks(isCommit);
598 ReleasePredicateLocks(isCommit, false);
599 }
600 }
601 else
602 {
603 /*
604 * Release locks retail. Note that if we are committing a
605 * subtransaction, we do NOT release its locks yet, but transfer
606 * them to the parent.
607 */
608 LOCALLOCK **locks;
609 int nlocks;
610
611 Assert(owner->parent != NULL);
612
613 /*
614 * Pass the list of locks owned by this resource owner to the lock
615 * manager, unless it has overflowed.
616 */
617 if (owner->nlocks > MAX_RESOWNER_LOCKS)
618 {
619 locks = NULL;
620 nlocks = 0;
621 }
622 else
623 {
624 locks = owner->locks;
625 nlocks = owner->nlocks;
626 }
627
628 if (isCommit)
629 LockReassignCurrentOwner(locks, nlocks);
630 else
631 LockReleaseCurrentOwner(locks, nlocks);
632 }
633 }
634 else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
635 {
636 /*
637 * Release catcache references. Note that ReleaseCatCache will remove
638 * the catref entry from our array, so we just have to iterate till
639 * there are none.
640 *
641 * As with buffer pins, warn if any are left at commit time.
642 */
643 while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
644 {
645 HeapTuple res = (HeapTuple) DatumGetPointer(foundres);
646
647 if (isCommit)
648 PrintCatCacheLeakWarning(res);
649 ReleaseCatCache(res);
650 }
651
652 /* Ditto for catcache lists */
653 while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres))
654 {
655 CatCList *res = (CatCList *) DatumGetPointer(foundres);
656
657 if (isCommit)
658 PrintCatCacheListLeakWarning(res);
659 ReleaseCatCacheList(res);
660 }
661
662 /* Ditto for plancache references */
663 while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
664 {
665 CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
666
667 if (isCommit)
668 PrintPlanCacheLeakWarning(res);
669 ReleaseCachedPlan(res, owner);
670 }
671
672 /* Ditto for tupdesc references */
673 while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
674 {
675 TupleDesc res = (TupleDesc) DatumGetPointer(foundres);
676
677 if (isCommit)
678 PrintTupleDescLeakWarning(res);
679 DecrTupleDescRefCount(res);
680 }
681
682 /* Ditto for snapshot references */
683 while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
684 {
685 Snapshot res = (Snapshot) DatumGetPointer(foundres);
686
687 if (isCommit)
688 PrintSnapshotLeakWarning(res);
689 UnregisterSnapshot(res);
690 }
691
692 /* Ditto for temporary files */
693 while (ResourceArrayGetAny(&(owner->filearr), &foundres))
694 {
695 File res = DatumGetFile(foundres);
696
697 if (isCommit)
698 PrintFileLeakWarning(res);
699 FileClose(res);
700 }
701 }
702
703 /* Let add-on modules get a chance too */
704 for (item = ResourceRelease_callbacks; item; item = item->next)
705 item->callback(phase, isCommit, isTopLevel, item->arg);
706
707 CurrentResourceOwner = save;
708 }
709
710 /*
711 * ResourceOwnerReleaseAllPlanCacheRefs
712 * Release the plancache references (only) held by this owner.
713 *
714 * We might eventually add similar functions for other resource types,
715 * but for now, only this is needed.
716 */
717 void
ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner)718 ResourceOwnerReleaseAllPlanCacheRefs(ResourceOwner owner)
719 {
720 Datum foundres;
721
722 while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
723 {
724 CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
725
726 ReleaseCachedPlan(res, owner);
727 }
728 }
729
730 /*
731 * ResourceOwnerDelete
732 * Delete an owner object and its descendants.
733 *
734 * The caller must have already released all resources in the object tree.
735 */
736 void
ResourceOwnerDelete(ResourceOwner owner)737 ResourceOwnerDelete(ResourceOwner owner)
738 {
739 /* We had better not be deleting CurrentResourceOwner ... */
740 Assert(owner != CurrentResourceOwner);
741
742 /* And it better not own any resources, either */
743 Assert(owner->bufferarr.nitems == 0);
744 Assert(owner->catrefarr.nitems == 0);
745 Assert(owner->catlistrefarr.nitems == 0);
746 Assert(owner->relrefarr.nitems == 0);
747 Assert(owner->planrefarr.nitems == 0);
748 Assert(owner->tupdescarr.nitems == 0);
749 Assert(owner->snapshotarr.nitems == 0);
750 Assert(owner->filearr.nitems == 0);
751 Assert(owner->dsmarr.nitems == 0);
752 Assert(owner->jitarr.nitems == 0);
753 Assert(owner->cryptohasharr.nitems == 0);
754 Assert(owner->hmacarr.nitems == 0);
755 Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
756
757 /*
758 * Delete children. The recursive call will delink the child from me, so
759 * just iterate as long as there is a child.
760 */
761 while (owner->firstchild != NULL)
762 ResourceOwnerDelete(owner->firstchild);
763
764 /*
765 * We delink the owner from its parent before deleting it, so that if
766 * there's an error we won't have deleted/busted owners still attached to
767 * the owner tree. Better a leak than a crash.
768 */
769 ResourceOwnerNewParent(owner, NULL);
770
771 /* And free the object. */
772 ResourceArrayFree(&(owner->bufferarr));
773 ResourceArrayFree(&(owner->catrefarr));
774 ResourceArrayFree(&(owner->catlistrefarr));
775 ResourceArrayFree(&(owner->relrefarr));
776 ResourceArrayFree(&(owner->planrefarr));
777 ResourceArrayFree(&(owner->tupdescarr));
778 ResourceArrayFree(&(owner->snapshotarr));
779 ResourceArrayFree(&(owner->filearr));
780 ResourceArrayFree(&(owner->dsmarr));
781 ResourceArrayFree(&(owner->jitarr));
782 ResourceArrayFree(&(owner->cryptohasharr));
783 ResourceArrayFree(&(owner->hmacarr));
784
785 pfree(owner);
786 }
787
788 /*
789 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
790 */
791 ResourceOwner
ResourceOwnerGetParent(ResourceOwner owner)792 ResourceOwnerGetParent(ResourceOwner owner)
793 {
794 return owner->parent;
795 }
796
797 /*
798 * Reassign a ResourceOwner to have a new parent
799 */
800 void
ResourceOwnerNewParent(ResourceOwner owner,ResourceOwner newparent)801 ResourceOwnerNewParent(ResourceOwner owner,
802 ResourceOwner newparent)
803 {
804 ResourceOwner oldparent = owner->parent;
805
806 if (oldparent)
807 {
808 if (owner == oldparent->firstchild)
809 oldparent->firstchild = owner->nextchild;
810 else
811 {
812 ResourceOwner child;
813
814 for (child = oldparent->firstchild; child; child = child->nextchild)
815 {
816 if (owner == child->nextchild)
817 {
818 child->nextchild = owner->nextchild;
819 break;
820 }
821 }
822 }
823 }
824
825 if (newparent)
826 {
827 Assert(owner != newparent);
828 owner->parent = newparent;
829 owner->nextchild = newparent->firstchild;
830 newparent->firstchild = owner;
831 }
832 else
833 {
834 owner->parent = NULL;
835 owner->nextchild = NULL;
836 }
837 }
838
839 /*
840 * Register or deregister callback functions for resource cleanup
841 *
842 * These functions are intended for use by dynamically loaded modules.
843 * For built-in modules we generally just hardwire the appropriate calls.
844 *
845 * Note that the callback occurs post-commit or post-abort, so the callback
846 * functions can only do noncritical cleanup.
847 */
848 void
RegisterResourceReleaseCallback(ResourceReleaseCallback callback,void * arg)849 RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
850 {
851 ResourceReleaseCallbackItem *item;
852
853 item = (ResourceReleaseCallbackItem *)
854 MemoryContextAlloc(TopMemoryContext,
855 sizeof(ResourceReleaseCallbackItem));
856 item->callback = callback;
857 item->arg = arg;
858 item->next = ResourceRelease_callbacks;
859 ResourceRelease_callbacks = item;
860 }
861
862 void
UnregisterResourceReleaseCallback(ResourceReleaseCallback callback,void * arg)863 UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
864 {
865 ResourceReleaseCallbackItem *item;
866 ResourceReleaseCallbackItem *prev;
867
868 prev = NULL;
869 for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
870 {
871 if (item->callback == callback && item->arg == arg)
872 {
873 if (prev)
874 prev->next = item->next;
875 else
876 ResourceRelease_callbacks = item->next;
877 pfree(item);
878 break;
879 }
880 }
881 }
882
883 /*
884 * Establish an AuxProcessResourceOwner for the current process.
885 */
886 void
CreateAuxProcessResourceOwner(void)887 CreateAuxProcessResourceOwner(void)
888 {
889 Assert(AuxProcessResourceOwner == NULL);
890 Assert(CurrentResourceOwner == NULL);
891 AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
892 CurrentResourceOwner = AuxProcessResourceOwner;
893
894 /*
895 * Register a shmem-exit callback for cleanup of aux-process resource
896 * owner. (This needs to run after, e.g., ShutdownXLOG.)
897 */
898 on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
899
900 }
901
902 /*
903 * Convenience routine to release all resources tracked in
904 * AuxProcessResourceOwner (but that resowner is not destroyed here).
905 * Warn about leaked resources if isCommit is true.
906 */
907 void
ReleaseAuxProcessResources(bool isCommit)908 ReleaseAuxProcessResources(bool isCommit)
909 {
910 /*
911 * At this writing, the only thing that could actually get released is
912 * buffer pins; but we may as well do the full release protocol.
913 */
914 ResourceOwnerRelease(AuxProcessResourceOwner,
915 RESOURCE_RELEASE_BEFORE_LOCKS,
916 isCommit, true);
917 ResourceOwnerRelease(AuxProcessResourceOwner,
918 RESOURCE_RELEASE_LOCKS,
919 isCommit, true);
920 ResourceOwnerRelease(AuxProcessResourceOwner,
921 RESOURCE_RELEASE_AFTER_LOCKS,
922 isCommit, true);
923 }
924
925 /*
926 * Shmem-exit callback for the same.
927 * Warn about leaked resources if process exit code is zero (ie normal).
928 */
929 static void
ReleaseAuxProcessResourcesCallback(int code,Datum arg)930 ReleaseAuxProcessResourcesCallback(int code, Datum arg)
931 {
932 bool isCommit = (code == 0);
933
934 ReleaseAuxProcessResources(isCommit);
935 }
936
937
938 /*
939 * Make sure there is room for at least one more entry in a ResourceOwner's
940 * buffer array.
941 *
942 * This is separate from actually inserting an entry because if we run out
943 * of memory, it's critical to do so *before* acquiring the resource.
944 */
945 void
ResourceOwnerEnlargeBuffers(ResourceOwner owner)946 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
947 {
948 /* We used to allow pinning buffers without a resowner, but no more */
949 Assert(owner != NULL);
950 ResourceArrayEnlarge(&(owner->bufferarr));
951 }
952
953 /*
954 * Remember that a buffer pin is owned by a ResourceOwner
955 *
956 * Caller must have previously done ResourceOwnerEnlargeBuffers()
957 */
958 void
ResourceOwnerRememberBuffer(ResourceOwner owner,Buffer buffer)959 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
960 {
961 ResourceArrayAdd(&(owner->bufferarr), BufferGetDatum(buffer));
962 }
963
964 /*
965 * Forget that a buffer pin is owned by a ResourceOwner
966 */
967 void
ResourceOwnerForgetBuffer(ResourceOwner owner,Buffer buffer)968 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
969 {
970 if (!ResourceArrayRemove(&(owner->bufferarr), BufferGetDatum(buffer)))
971 elog(ERROR, "buffer %d is not owned by resource owner %s",
972 buffer, owner->name);
973 }
974
975 /*
976 * Remember that a Local Lock is owned by a ResourceOwner
977 *
978 * This is different from the other Remember functions in that the list of
979 * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
980 * and when it overflows, we stop tracking locks. The point of only remembering
981 * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
982 * ResourceOwnerForgetLock doesn't need to scan through a large array to find
983 * the entry.
984 */
985 void
ResourceOwnerRememberLock(ResourceOwner owner,LOCALLOCK * locallock)986 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
987 {
988 Assert(locallock != NULL);
989
990 if (owner->nlocks > MAX_RESOWNER_LOCKS)
991 return; /* we have already overflowed */
992
993 if (owner->nlocks < MAX_RESOWNER_LOCKS)
994 owner->locks[owner->nlocks] = locallock;
995 else
996 {
997 /* overflowed */
998 }
999 owner->nlocks++;
1000 }
1001
1002 /*
1003 * Forget that a Local Lock is owned by a ResourceOwner
1004 */
1005 void
ResourceOwnerForgetLock(ResourceOwner owner,LOCALLOCK * locallock)1006 ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1007 {
1008 int i;
1009
1010 if (owner->nlocks > MAX_RESOWNER_LOCKS)
1011 return; /* we have overflowed */
1012
1013 Assert(owner->nlocks > 0);
1014 for (i = owner->nlocks - 1; i >= 0; i--)
1015 {
1016 if (locallock == owner->locks[i])
1017 {
1018 owner->locks[i] = owner->locks[owner->nlocks - 1];
1019 owner->nlocks--;
1020 return;
1021 }
1022 }
1023 elog(ERROR, "lock reference %p is not owned by resource owner %s",
1024 locallock, owner->name);
1025 }
1026
1027 /*
1028 * Make sure there is room for at least one more entry in a ResourceOwner's
1029 * catcache reference array.
1030 *
1031 * This is separate from actually inserting an entry because if we run out
1032 * of memory, it's critical to do so *before* acquiring the resource.
1033 */
1034 void
ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)1035 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
1036 {
1037 ResourceArrayEnlarge(&(owner->catrefarr));
1038 }
1039
1040 /*
1041 * Remember that a catcache reference is owned by a ResourceOwner
1042 *
1043 * Caller must have previously done ResourceOwnerEnlargeCatCacheRefs()
1044 */
1045 void
ResourceOwnerRememberCatCacheRef(ResourceOwner owner,HeapTuple tuple)1046 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
1047 {
1048 ResourceArrayAdd(&(owner->catrefarr), PointerGetDatum(tuple));
1049 }
1050
1051 /*
1052 * Forget that a catcache reference is owned by a ResourceOwner
1053 */
1054 void
ResourceOwnerForgetCatCacheRef(ResourceOwner owner,HeapTuple tuple)1055 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
1056 {
1057 if (!ResourceArrayRemove(&(owner->catrefarr), PointerGetDatum(tuple)))
1058 elog(ERROR, "catcache reference %p is not owned by resource owner %s",
1059 tuple, owner->name);
1060 }
1061
1062 /*
1063 * Make sure there is room for at least one more entry in a ResourceOwner's
1064 * catcache-list reference array.
1065 *
1066 * This is separate from actually inserting an entry because if we run out
1067 * of memory, it's critical to do so *before* acquiring the resource.
1068 */
1069 void
ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)1070 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
1071 {
1072 ResourceArrayEnlarge(&(owner->catlistrefarr));
1073 }
1074
1075 /*
1076 * Remember that a catcache-list reference is owned by a ResourceOwner
1077 *
1078 * Caller must have previously done ResourceOwnerEnlargeCatCacheListRefs()
1079 */
1080 void
ResourceOwnerRememberCatCacheListRef(ResourceOwner owner,CatCList * list)1081 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
1082 {
1083 ResourceArrayAdd(&(owner->catlistrefarr), PointerGetDatum(list));
1084 }
1085
1086 /*
1087 * Forget that a catcache-list reference is owned by a ResourceOwner
1088 */
1089 void
ResourceOwnerForgetCatCacheListRef(ResourceOwner owner,CatCList * list)1090 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
1091 {
1092 if (!ResourceArrayRemove(&(owner->catlistrefarr), PointerGetDatum(list)))
1093 elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
1094 list, owner->name);
1095 }
1096
1097 /*
1098 * Make sure there is room for at least one more entry in a ResourceOwner's
1099 * relcache reference array.
1100 *
1101 * This is separate from actually inserting an entry because if we run out
1102 * of memory, it's critical to do so *before* acquiring the resource.
1103 */
1104 void
ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)1105 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
1106 {
1107 ResourceArrayEnlarge(&(owner->relrefarr));
1108 }
1109
1110 /*
1111 * Remember that a relcache reference is owned by a ResourceOwner
1112 *
1113 * Caller must have previously done ResourceOwnerEnlargeRelationRefs()
1114 */
1115 void
ResourceOwnerRememberRelationRef(ResourceOwner owner,Relation rel)1116 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
1117 {
1118 ResourceArrayAdd(&(owner->relrefarr), PointerGetDatum(rel));
1119 }
1120
1121 /*
1122 * Forget that a relcache reference is owned by a ResourceOwner
1123 */
1124 void
ResourceOwnerForgetRelationRef(ResourceOwner owner,Relation rel)1125 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
1126 {
1127 if (!ResourceArrayRemove(&(owner->relrefarr), PointerGetDatum(rel)))
1128 elog(ERROR, "relcache reference %s is not owned by resource owner %s",
1129 RelationGetRelationName(rel), owner->name);
1130 }
1131
1132 /*
1133 * Debugging subroutine
1134 */
1135 static void
PrintRelCacheLeakWarning(Relation rel)1136 PrintRelCacheLeakWarning(Relation rel)
1137 {
1138 elog(WARNING, "relcache reference leak: relation \"%s\" not closed",
1139 RelationGetRelationName(rel));
1140 }
1141
1142 /*
1143 * Make sure there is room for at least one more entry in a ResourceOwner's
1144 * plancache reference array.
1145 *
1146 * This is separate from actually inserting an entry because if we run out
1147 * of memory, it's critical to do so *before* acquiring the resource.
1148 */
1149 void
ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)1150 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
1151 {
1152 ResourceArrayEnlarge(&(owner->planrefarr));
1153 }
1154
1155 /*
1156 * Remember that a plancache reference is owned by a ResourceOwner
1157 *
1158 * Caller must have previously done ResourceOwnerEnlargePlanCacheRefs()
1159 */
1160 void
ResourceOwnerRememberPlanCacheRef(ResourceOwner owner,CachedPlan * plan)1161 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
1162 {
1163 ResourceArrayAdd(&(owner->planrefarr), PointerGetDatum(plan));
1164 }
1165
1166 /*
1167 * Forget that a plancache reference is owned by a ResourceOwner
1168 */
1169 void
ResourceOwnerForgetPlanCacheRef(ResourceOwner owner,CachedPlan * plan)1170 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
1171 {
1172 if (!ResourceArrayRemove(&(owner->planrefarr), PointerGetDatum(plan)))
1173 elog(ERROR, "plancache reference %p is not owned by resource owner %s",
1174 plan, owner->name);
1175 }
1176
1177 /*
1178 * Debugging subroutine
1179 */
1180 static void
PrintPlanCacheLeakWarning(CachedPlan * plan)1181 PrintPlanCacheLeakWarning(CachedPlan *plan)
1182 {
1183 elog(WARNING, "plancache reference leak: plan %p not closed", plan);
1184 }
1185
1186 /*
1187 * Make sure there is room for at least one more entry in a ResourceOwner's
1188 * tupdesc reference array.
1189 *
1190 * This is separate from actually inserting an entry because if we run out
1191 * of memory, it's critical to do so *before* acquiring the resource.
1192 */
1193 void
ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)1194 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
1195 {
1196 ResourceArrayEnlarge(&(owner->tupdescarr));
1197 }
1198
1199 /*
1200 * Remember that a tupdesc reference is owned by a ResourceOwner
1201 *
1202 * Caller must have previously done ResourceOwnerEnlargeTupleDescs()
1203 */
1204 void
ResourceOwnerRememberTupleDesc(ResourceOwner owner,TupleDesc tupdesc)1205 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
1206 {
1207 ResourceArrayAdd(&(owner->tupdescarr), PointerGetDatum(tupdesc));
1208 }
1209
1210 /*
1211 * Forget that a tupdesc reference is owned by a ResourceOwner
1212 */
1213 void
ResourceOwnerForgetTupleDesc(ResourceOwner owner,TupleDesc tupdesc)1214 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
1215 {
1216 if (!ResourceArrayRemove(&(owner->tupdescarr), PointerGetDatum(tupdesc)))
1217 elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
1218 tupdesc, owner->name);
1219 }
1220
1221 /*
1222 * Debugging subroutine
1223 */
1224 static void
PrintTupleDescLeakWarning(TupleDesc tupdesc)1225 PrintTupleDescLeakWarning(TupleDesc tupdesc)
1226 {
1227 elog(WARNING,
1228 "TupleDesc reference leak: TupleDesc %p (%u,%d) still referenced",
1229 tupdesc, tupdesc->tdtypeid, tupdesc->tdtypmod);
1230 }
1231
1232 /*
1233 * Make sure there is room for at least one more entry in a ResourceOwner's
1234 * snapshot reference array.
1235 *
1236 * This is separate from actually inserting an entry because if we run out
1237 * of memory, it's critical to do so *before* acquiring the resource.
1238 */
1239 void
ResourceOwnerEnlargeSnapshots(ResourceOwner owner)1240 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
1241 {
1242 ResourceArrayEnlarge(&(owner->snapshotarr));
1243 }
1244
1245 /*
1246 * Remember that a snapshot reference is owned by a ResourceOwner
1247 *
1248 * Caller must have previously done ResourceOwnerEnlargeSnapshots()
1249 */
1250 void
ResourceOwnerRememberSnapshot(ResourceOwner owner,Snapshot snapshot)1251 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
1252 {
1253 ResourceArrayAdd(&(owner->snapshotarr), PointerGetDatum(snapshot));
1254 }
1255
1256 /*
1257 * Forget that a snapshot reference is owned by a ResourceOwner
1258 */
1259 void
ResourceOwnerForgetSnapshot(ResourceOwner owner,Snapshot snapshot)1260 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
1261 {
1262 if (!ResourceArrayRemove(&(owner->snapshotarr), PointerGetDatum(snapshot)))
1263 elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
1264 snapshot, owner->name);
1265 }
1266
1267 /*
1268 * Debugging subroutine
1269 */
1270 static void
PrintSnapshotLeakWarning(Snapshot snapshot)1271 PrintSnapshotLeakWarning(Snapshot snapshot)
1272 {
1273 elog(WARNING, "Snapshot reference leak: Snapshot %p still referenced",
1274 snapshot);
1275 }
1276
1277
1278 /*
1279 * Make sure there is room for at least one more entry in a ResourceOwner's
1280 * files reference array.
1281 *
1282 * This is separate from actually inserting an entry because if we run out
1283 * of memory, it's critical to do so *before* acquiring the resource.
1284 */
1285 void
ResourceOwnerEnlargeFiles(ResourceOwner owner)1286 ResourceOwnerEnlargeFiles(ResourceOwner owner)
1287 {
1288 ResourceArrayEnlarge(&(owner->filearr));
1289 }
1290
1291 /*
1292 * Remember that a temporary file is owned by a ResourceOwner
1293 *
1294 * Caller must have previously done ResourceOwnerEnlargeFiles()
1295 */
1296 void
ResourceOwnerRememberFile(ResourceOwner owner,File file)1297 ResourceOwnerRememberFile(ResourceOwner owner, File file)
1298 {
1299 ResourceArrayAdd(&(owner->filearr), FileGetDatum(file));
1300 }
1301
1302 /*
1303 * Forget that a temporary file is owned by a ResourceOwner
1304 */
1305 void
ResourceOwnerForgetFile(ResourceOwner owner,File file)1306 ResourceOwnerForgetFile(ResourceOwner owner, File file)
1307 {
1308 if (!ResourceArrayRemove(&(owner->filearr), FileGetDatum(file)))
1309 elog(ERROR, "temporary file %d is not owned by resource owner %s",
1310 file, owner->name);
1311 }
1312
1313 /*
1314 * Debugging subroutine
1315 */
1316 static void
PrintFileLeakWarning(File file)1317 PrintFileLeakWarning(File file)
1318 {
1319 elog(WARNING, "temporary file leak: File %d still referenced",
1320 file);
1321 }
1322
1323 /*
1324 * Make sure there is room for at least one more entry in a ResourceOwner's
1325 * dynamic shmem segment reference array.
1326 *
1327 * This is separate from actually inserting an entry because if we run out
1328 * of memory, it's critical to do so *before* acquiring the resource.
1329 */
1330 void
ResourceOwnerEnlargeDSMs(ResourceOwner owner)1331 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
1332 {
1333 ResourceArrayEnlarge(&(owner->dsmarr));
1334 }
1335
1336 /*
1337 * Remember that a dynamic shmem segment is owned by a ResourceOwner
1338 *
1339 * Caller must have previously done ResourceOwnerEnlargeDSMs()
1340 */
1341 void
ResourceOwnerRememberDSM(ResourceOwner owner,dsm_segment * seg)1342 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
1343 {
1344 ResourceArrayAdd(&(owner->dsmarr), PointerGetDatum(seg));
1345 }
1346
1347 /*
1348 * Forget that a dynamic shmem segment is owned by a ResourceOwner
1349 */
1350 void
ResourceOwnerForgetDSM(ResourceOwner owner,dsm_segment * seg)1351 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
1352 {
1353 if (!ResourceArrayRemove(&(owner->dsmarr), PointerGetDatum(seg)))
1354 elog(ERROR, "dynamic shared memory segment %u is not owned by resource owner %s",
1355 dsm_segment_handle(seg), owner->name);
1356 }
1357
1358 /*
1359 * Debugging subroutine
1360 */
1361 static void
PrintDSMLeakWarning(dsm_segment * seg)1362 PrintDSMLeakWarning(dsm_segment *seg)
1363 {
1364 elog(WARNING, "dynamic shared memory leak: segment %u still referenced",
1365 dsm_segment_handle(seg));
1366 }
1367
1368 /*
1369 * Make sure there is room for at least one more entry in a ResourceOwner's
1370 * JIT context reference array.
1371 *
1372 * This is separate from actually inserting an entry because if we run out of
1373 * memory, it's critical to do so *before* acquiring the resource.
1374 */
1375 void
ResourceOwnerEnlargeJIT(ResourceOwner owner)1376 ResourceOwnerEnlargeJIT(ResourceOwner owner)
1377 {
1378 ResourceArrayEnlarge(&(owner->jitarr));
1379 }
1380
1381 /*
1382 * Remember that a JIT context is owned by a ResourceOwner
1383 *
1384 * Caller must have previously done ResourceOwnerEnlargeJIT()
1385 */
1386 void
ResourceOwnerRememberJIT(ResourceOwner owner,Datum handle)1387 ResourceOwnerRememberJIT(ResourceOwner owner, Datum handle)
1388 {
1389 ResourceArrayAdd(&(owner->jitarr), handle);
1390 }
1391
1392 /*
1393 * Forget that a JIT context is owned by a ResourceOwner
1394 */
1395 void
ResourceOwnerForgetJIT(ResourceOwner owner,Datum handle)1396 ResourceOwnerForgetJIT(ResourceOwner owner, Datum handle)
1397 {
1398 if (!ResourceArrayRemove(&(owner->jitarr), handle))
1399 elog(ERROR, "JIT context %p is not owned by resource owner %s",
1400 DatumGetPointer(handle), owner->name);
1401 }
1402
1403 /*
1404 * Make sure there is room for at least one more entry in a ResourceOwner's
1405 * cryptohash context reference array.
1406 *
1407 * This is separate from actually inserting an entry because if we run out of
1408 * memory, it's critical to do so *before* acquiring the resource.
1409 */
1410 void
ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)1411 ResourceOwnerEnlargeCryptoHash(ResourceOwner owner)
1412 {
1413 ResourceArrayEnlarge(&(owner->cryptohasharr));
1414 }
1415
1416 /*
1417 * Remember that a cryptohash context is owned by a ResourceOwner
1418 *
1419 * Caller must have previously done ResourceOwnerEnlargeCryptoHash()
1420 */
1421 void
ResourceOwnerRememberCryptoHash(ResourceOwner owner,Datum handle)1422 ResourceOwnerRememberCryptoHash(ResourceOwner owner, Datum handle)
1423 {
1424 ResourceArrayAdd(&(owner->cryptohasharr), handle);
1425 }
1426
1427 /*
1428 * Forget that a cryptohash context is owned by a ResourceOwner
1429 */
1430 void
ResourceOwnerForgetCryptoHash(ResourceOwner owner,Datum handle)1431 ResourceOwnerForgetCryptoHash(ResourceOwner owner, Datum handle)
1432 {
1433 if (!ResourceArrayRemove(&(owner->cryptohasharr), handle))
1434 elog(ERROR, "cryptohash context %p is not owned by resource owner %s",
1435 DatumGetPointer(handle), owner->name);
1436 }
1437
1438 /*
1439 * Debugging subroutine
1440 */
1441 static void
PrintCryptoHashLeakWarning(Datum handle)1442 PrintCryptoHashLeakWarning(Datum handle)
1443 {
1444 elog(WARNING, "cryptohash context reference leak: context %p still referenced",
1445 DatumGetPointer(handle));
1446 }
1447
1448 /*
1449 * Make sure there is room for at least one more entry in a ResourceOwner's
1450 * hmac context reference array.
1451 *
1452 * This is separate from actually inserting an entry because if we run out of
1453 * memory, it's critical to do so *before* acquiring the resource.
1454 */
1455 void
ResourceOwnerEnlargeHMAC(ResourceOwner owner)1456 ResourceOwnerEnlargeHMAC(ResourceOwner owner)
1457 {
1458 ResourceArrayEnlarge(&(owner->hmacarr));
1459 }
1460
1461 /*
1462 * Remember that a HMAC context is owned by a ResourceOwner
1463 *
1464 * Caller must have previously done ResourceOwnerEnlargeHMAC()
1465 */
1466 void
ResourceOwnerRememberHMAC(ResourceOwner owner,Datum handle)1467 ResourceOwnerRememberHMAC(ResourceOwner owner, Datum handle)
1468 {
1469 ResourceArrayAdd(&(owner->hmacarr), handle);
1470 }
1471
1472 /*
1473 * Forget that a HMAC context is owned by a ResourceOwner
1474 */
1475 void
ResourceOwnerForgetHMAC(ResourceOwner owner,Datum handle)1476 ResourceOwnerForgetHMAC(ResourceOwner owner, Datum handle)
1477 {
1478 if (!ResourceArrayRemove(&(owner->hmacarr), handle))
1479 elog(ERROR, "HMAC context %p is not owned by resource owner %s",
1480 DatumGetPointer(handle), owner->name);
1481 }
1482
1483 /*
1484 * Debugging subroutine
1485 */
1486 static void
PrintHMACLeakWarning(Datum handle)1487 PrintHMACLeakWarning(Datum handle)
1488 {
1489 elog(WARNING, "HMAC context reference leak: context %p still referenced",
1490 DatumGetPointer(handle));
1491 }
1492