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