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