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