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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
739 | ResourceOwnerGetParent(ResourceOwner owner) |
740 | { |
741 | return owner->parent; |
742 | } |
743 | |
744 | /* |
745 | * Reassign a ResourceOwner to have a new parent |
746 | */ |
747 | void |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | |