Age Owner Branch data TLA Line data Source code
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 on how to use it.
10 : : *
11 : : * The implementation consists of a small fixed-size array and a hash table.
12 : : * New entries are inserted to the fixed-size array, and when the array
13 : : * fills up, all the entries are moved to the hash table. This way, the
14 : : * array always contains a few most recently remembered references. To find
15 : : * a particular reference, you need to search both the array and the hash
16 : : * table.
17 : : *
18 : : * The most frequent usage is that a resource is remembered, and forgotten
19 : : * shortly thereafter. For example, pin a buffer, read one tuple from it,
20 : : * release the pin. Linearly scanning the small array handles that case
21 : : * efficiently. However, some resources are held for a longer time, and
22 : : * sometimes a lot of resources need to be held simultaneously. The hash
23 : : * table handles those cases.
24 : : *
25 : : * When it's time to release the resources, we sort them according to the
26 : : * release-priority of each resource, and release them in that order.
27 : : *
28 : : * Local lock references are special, they are not stored in the array or
29 : : * the hash table. Instead, each resource owner has a separate small cache
30 : : * of locks it owns. The lock manager has the same information in its local
31 : : * lock hash table, and we fall back on that if the cache overflows, but
32 : : * traversing the hash table is slower when there are a lot of locks
33 : : * belonging to other resource owners. This is to speed up bulk releasing
34 : : * or reassigning locks from a resource owner to its parent.
35 : : *
36 : : *
37 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
38 : : * Portions Copyright (c) 1994, Regents of the University of California
39 : : *
40 : : *
41 : : * IDENTIFICATION
42 : : * src/backend/utils/resowner/resowner.c
43 : : *
44 : : *-------------------------------------------------------------------------
45 : : */
46 : : #include "postgres.h"
47 : :
48 : : #include "common/hashfn.h"
49 : : #include "common/int.h"
50 : : #include "lib/ilist.h"
51 : : #include "storage/aio.h"
52 : : #include "storage/ipc.h"
53 : : #include "storage/predicate.h"
54 : : #include "storage/proc.h"
55 : : #include "utils/memutils.h"
56 : : #include "utils/resowner.h"
57 : :
58 : : /*
59 : : * ResourceElem represents a reference associated with a resource owner.
60 : : *
61 : : * All objects managed by this code are required to fit into a Datum,
62 : : * which is fine since they are generally pointers or integers.
63 : : */
64 : : typedef struct ResourceElem
65 : : {
66 : : Datum item;
67 : : const ResourceOwnerDesc *kind; /* NULL indicates a free hash table slot */
68 : : } ResourceElem;
69 : :
70 : : /*
71 : : * Size of the fixed-size array to hold most-recently remembered resources.
72 : : */
73 : : #define RESOWNER_ARRAY_SIZE 32
74 : :
75 : : /*
76 : : * Initially allocated size of a ResourceOwner's hash table. Must be power of
77 : : * two because we use (capacity - 1) as mask for hashing.
78 : : */
79 : : #define RESOWNER_HASH_INIT_SIZE 64
80 : :
81 : : /*
82 : : * How many items may be stored in a hash table of given capacity. When this
83 : : * number is reached, we must resize.
84 : : *
85 : : * The hash table must always have enough free space that we can copy the
86 : : * entries from the array to it, in ResourceOwnerSort. We also insist that
87 : : * the initial size is large enough that we don't hit the max size immediately
88 : : * when it's created. Aside from those limitations, 0.75 is a reasonable fill
89 : : * factor.
90 : : */
91 : : #define RESOWNER_HASH_MAX_ITEMS(capacity) \
92 : : Min(capacity - RESOWNER_ARRAY_SIZE, (capacity)/4 * 3)
93 : :
94 : : StaticAssertDecl(RESOWNER_HASH_MAX_ITEMS(RESOWNER_HASH_INIT_SIZE) >= RESOWNER_ARRAY_SIZE,
95 : : "initial hash size too small compared to array size");
96 : :
97 : : /*
98 : : * MAX_RESOWNER_LOCKS is the size of the per-resource owner locks cache. It's
99 : : * chosen based on some testing with pg_dump with a large schema. When the
100 : : * tests were done (on 9.2), resource owners in a pg_dump run contained up
101 : : * to 9 locks, regardless of the schema size, except for the top resource
102 : : * owner which contained much more (overflowing the cache). 15 seems like a
103 : : * nice round number that's somewhat higher than what pg_dump needs. Note that
104 : : * making this number larger is not free - the bigger the cache, the slower
105 : : * it is to release locks (in retail), when a resource owner holds many locks.
106 : : */
107 : : #define MAX_RESOWNER_LOCKS 15
108 : :
109 : : /*
110 : : * ResourceOwner objects look like this
111 : : */
112 : : struct ResourceOwnerData
113 : : {
114 : : ResourceOwner parent; /* NULL if no parent (toplevel owner) */
115 : : ResourceOwner firstchild; /* head of linked list of children */
116 : : ResourceOwner nextchild; /* next child of same parent */
117 : : const char *name; /* name (just for debugging) */
118 : :
119 : : /*
120 : : * When ResourceOwnerRelease is called, we sort the 'hash' and 'arr' by
121 : : * the release priority. After that, no new resources can be remembered
122 : : * or forgotten in retail. We have separate flags because
123 : : * ResourceOwnerReleaseAllOfKind() temporarily sets 'releasing' without
124 : : * sorting the arrays.
125 : : */
126 : : bool releasing;
127 : : bool sorted; /* are 'hash' and 'arr' sorted by priority? */
128 : :
129 : : /*
130 : : * Number of items in the locks cache, array, and hash table respectively.
131 : : * (These are packed together to avoid padding in the struct.)
132 : : */
133 : : uint8 nlocks; /* number of owned locks */
134 : : uint8 narr; /* how many items are stored in the array */
135 : : uint32 nhash; /* how many items are stored in the hash */
136 : :
137 : : /*
138 : : * The fixed-size array for recent resources.
139 : : *
140 : : * If 'sorted' is set, the contents are sorted by release priority.
141 : : */
142 : : ResourceElem arr[RESOWNER_ARRAY_SIZE];
143 : :
144 : : /*
145 : : * The hash table. Uses open-addressing. 'nhash' is the number of items
146 : : * present; if it would exceed 'grow_at', we enlarge it and re-hash.
147 : : * 'grow_at' should be rather less than 'capacity' so that we don't waste
148 : : * too much time searching for empty slots.
149 : : *
150 : : * If 'sorted' is set, the contents are no longer hashed, but sorted by
151 : : * release priority. The first 'nhash' elements are occupied, the rest
152 : : * are empty.
153 : : */
154 : : ResourceElem *hash;
155 : : uint32 capacity; /* allocated length of hash[] */
156 : : uint32 grow_at; /* grow hash when reach this */
157 : :
158 : : /* The local locks cache. */
159 : : LOCALLOCK *locks[MAX_RESOWNER_LOCKS]; /* list of owned locks */
160 : :
161 : : /*
162 : : * AIO handles need be registered in critical sections and therefore
163 : : * cannot use the normal ResourceElem mechanism.
164 : : */
165 : : dlist_head aio_handles;
166 : : };
167 : :
168 : :
169 : : /*****************************************************************************
170 : : * GLOBAL MEMORY *
171 : : *****************************************************************************/
172 : :
173 : : ResourceOwner CurrentResourceOwner = NULL;
174 : : ResourceOwner CurTransactionResourceOwner = NULL;
175 : : ResourceOwner TopTransactionResourceOwner = NULL;
176 : : ResourceOwner AuxProcessResourceOwner = NULL;
177 : :
178 : : /* #define RESOWNER_STATS */
179 : :
180 : : #ifdef RESOWNER_STATS
181 : : static int narray_lookups = 0;
182 : : static int nhash_lookups = 0;
183 : : #endif
184 : :
185 : : /*
186 : : * List of add-on callbacks for resource releasing
187 : : */
188 : : typedef struct ResourceReleaseCallbackItem
189 : : {
190 : : struct ResourceReleaseCallbackItem *next;
191 : : ResourceReleaseCallback callback;
192 : : void *arg;
193 : : } ResourceReleaseCallbackItem;
194 : :
195 : : static ResourceReleaseCallbackItem *ResourceRelease_callbacks = NULL;
196 : :
197 : :
198 : : /* Internal routines */
199 : : static inline uint32 hash_resource_elem(Datum value, const ResourceOwnerDesc *kind);
200 : : static void ResourceOwnerAddToHash(ResourceOwner owner, Datum value,
201 : : const ResourceOwnerDesc *kind);
202 : : static int resource_priority_cmp(const void *a, const void *b);
203 : : static void ResourceOwnerSort(ResourceOwner owner);
204 : : static void ResourceOwnerReleaseAll(ResourceOwner owner,
205 : : ResourceReleasePhase phase,
206 : : bool printLeakWarnings);
207 : : static void ResourceOwnerReleaseInternal(ResourceOwner owner,
208 : : ResourceReleasePhase phase,
209 : : bool isCommit,
210 : : bool isTopLevel);
211 : : static void ReleaseAuxProcessResourcesCallback(int code, Datum arg);
212 : :
213 : :
214 : : /*****************************************************************************
215 : : * INTERNAL ROUTINES *
216 : : *****************************************************************************/
217 : :
218 : : /*
219 : : * Hash function for value+kind combination.
220 : : */
221 : : static inline uint32
965 heikki.linnakangas@i 222 :CBC 2159230 : hash_resource_elem(Datum value, const ResourceOwnerDesc *kind)
223 : : {
224 : : /*
225 : : * Most resource kinds store a pointer in 'value', and pointers are unique
226 : : * all on their own. But some resources store plain integers (Files and
227 : : * Buffers as of this writing), so we want to incorporate the 'kind' in
228 : : * the hash too, otherwise those resources will collide a lot. But
229 : : * because there are only a few resource kinds like that - and only a few
230 : : * resource kinds to begin with - we don't need to work too hard to mix
231 : : * 'kind' into the hash. Just add it with hash_combine(), it perturbs the
232 : : * result enough for our purposes.
233 : : */
321 tgl@sss.pgh.pa.us 234 :GNC 2159230 : return hash_combine64(murmurhash64((uint64) value),
235 : : (uint64) (uintptr_t) kind);
236 : : }
237 : :
238 : : /*
239 : : * Adds 'value' of given 'kind' to the ResourceOwner's hash table
240 : : */
241 : : static void
965 heikki.linnakangas@i 242 :CBC 1606220 : ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
243 : : {
244 : 1606220 : uint32 mask = owner->capacity - 1;
245 : : uint32 idx;
246 : :
247 [ - + ]: 1606220 : Assert(kind != NULL);
248 : :
249 : : /* Insert into first free slot at or after hash location. */
250 : 1606220 : idx = hash_resource_elem(value, kind) & mask;
251 : : for (;;)
252 : : {
253 [ + + ]: 221347301 : if (owner->hash[idx].kind == NULL)
254 : 1606220 : break; /* found a free slot */
255 : 219741081 : idx = (idx + 1) & mask;
256 : : }
257 : 1606220 : owner->hash[idx].item = value;
258 : 1606220 : owner->hash[idx].kind = kind;
259 : 1606220 : owner->nhash++;
3808 tgl@sss.pgh.pa.us 260 : 1606220 : }
261 : :
262 : : /*
263 : : * Comparison function to sort by release phase and priority
264 : : */
265 : : static int
965 heikki.linnakangas@i 266 : 618918 : resource_priority_cmp(const void *a, const void *b)
267 : : {
268 : 618918 : const ResourceElem *ra = (const ResourceElem *) a;
269 : 618918 : const ResourceElem *rb = (const ResourceElem *) b;
270 : :
271 : : /* Note: reverse order */
272 [ + + ]: 618918 : if (ra->kind->release_phase == rb->kind->release_phase)
865 nathan@postgresql.or 273 : 479642 : return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
965 heikki.linnakangas@i 274 [ + + ]: 139276 : else if (ra->kind->release_phase > rb->kind->release_phase)
275 : 119970 : return -1;
276 : : else
277 : 19306 : return 1;
278 : : }
279 : :
280 : : /*
281 : : * Sort resources in reverse release priority.
282 : : *
283 : : * If the hash table is in use, all the elements from the fixed-size array are
284 : : * moved to the hash table, and then the hash table is sorted. If there is no
285 : : * hash table, then the fixed-size array is sorted directly. In either case,
286 : : * the result is one sorted array that contains all the resources.
287 : : */
288 : : static void
289 : 928810 : ResourceOwnerSort(ResourceOwner owner)
290 : : {
291 : : ResourceElem *items;
292 : : uint32 nitems;
293 : :
294 [ + + ]: 928810 : if (owner->nhash == 0)
295 : : {
296 : 928624 : items = owner->arr;
297 : 928624 : nitems = owner->narr;
298 : : }
299 : : else
300 : : {
301 : : /*
302 : : * Compact the hash table, so that all the elements are in the
303 : : * beginning of the 'hash' array, with no empty elements.
304 : : */
305 : 186 : uint32 dst = 0;
306 : :
307 [ + + ]: 568890 : for (int idx = 0; idx < owner->capacity; idx++)
308 : : {
309 [ + + ]: 568704 : if (owner->hash[idx].kind != NULL)
310 : : {
311 [ + + ]: 218254 : if (dst != idx)
312 : 216105 : owner->hash[dst] = owner->hash[idx];
313 : 218254 : dst++;
314 : : }
315 : : }
316 : :
317 : : /*
318 : : * Move all entries from the fixed-size array to 'hash'.
319 : : *
320 : : * RESOWNER_HASH_MAX_ITEMS is defined so that there is always enough
321 : : * free space to move all the elements from the fixed-size array to
322 : : * the hash.
323 : : */
324 [ - + ]: 186 : Assert(dst + owner->narr <= owner->capacity);
325 [ + + ]: 966 : for (int idx = 0; idx < owner->narr; idx++)
326 : : {
327 : 780 : owner->hash[dst] = owner->arr[idx];
328 : 780 : dst++;
329 : : }
330 [ - + ]: 186 : Assert(dst == owner->nhash + owner->narr);
331 : 186 : owner->narr = 0;
332 : 186 : owner->nhash = dst;
333 : :
334 : 186 : items = owner->hash;
335 : 186 : nitems = owner->nhash;
336 : : }
337 : :
338 : 928810 : qsort(items, nitems, sizeof(ResourceElem), resource_priority_cmp);
3808 tgl@sss.pgh.pa.us 339 : 928810 : }
340 : :
341 : : /*
342 : : * Call the ReleaseResource callback on entries with given 'phase'.
343 : : */
344 : : static void
965 heikki.linnakangas@i 345 : 1857622 : ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase,
346 : : bool printLeakWarnings)
347 : : {
348 : : ResourceElem *items;
349 : : uint32 nitems;
350 : : bool using_arr;
351 : :
352 : : /*
353 : : * ResourceOwnerSort must've been called already. All the resources are
354 : : * either in the array or the hash.
355 : : */
356 [ - + ]: 1857622 : Assert(owner->releasing);
357 [ - + ]: 1857622 : Assert(owner->sorted);
964 358 [ + + ]: 1857622 : if (owner->nhash == 0)
359 : : {
965 360 : 1857251 : items = owner->arr;
361 : 1857251 : nitems = owner->narr;
8 tgl@sss.pgh.pa.us 362 : 1857251 : using_arr = true;
363 : : }
364 : : else
365 : : {
965 heikki.linnakangas@i 366 [ - + ]: 371 : Assert(owner->narr == 0);
367 : 371 : items = owner->hash;
368 : 371 : nitems = owner->nhash;
8 tgl@sss.pgh.pa.us 369 : 371 : using_arr = false;
370 : : }
371 : :
372 : : /*
373 : : * The resources are sorted in reverse priority order. Release them
374 : : * starting from the end, until we hit the end of the phase that we are
375 : : * releasing now. We will continue from there when called again for the
376 : : * next phase.
377 : : */
965 heikki.linnakangas@i 378 [ + + ]: 2171379 : while (nitems > 0)
379 : : {
380 : 336981 : uint32 idx = nitems - 1;
381 : 336981 : Datum value = items[idx].item;
382 : 336981 : const ResourceOwnerDesc *kind = items[idx].kind;
383 : :
384 [ + + ]: 336981 : if (kind->release_phase > phase)
385 : 23224 : break;
386 [ - + ]: 313757 : Assert(kind->release_phase == phase);
387 : :
388 [ + + ]: 313757 : if (printLeakWarnings)
389 : : {
390 : : char *res_str;
391 : :
392 : 12 : res_str = kind->DebugPrint ?
393 : 6 : kind->DebugPrint(value)
394 [ + - ]: 6 : : psprintf("%s %p", kind->name, DatumGetPointer(value));
395 [ + - ]: 6 : elog(WARNING, "resource was not closed: %s", res_str);
396 : 6 : pfree(res_str);
397 : : }
398 : :
399 : : /*
400 : : * Update stored count to forget the item before calling its
401 : : * ReleaseResource method. This avoids double-free crashes in case an
402 : : * error gets thrown within ReleaseResource.
403 : : */
404 : 313757 : nitems--;
8 tgl@sss.pgh.pa.us 405 [ + + ]: 313757 : if (using_arr)
406 : 94723 : owner->narr = nitems;
407 : : else
408 : 219034 : owner->nhash = nitems;
409 : :
410 : 313757 : kind->ReleaseResource(value);
411 : : }
3808 412 : 1857622 : }
413 : :
414 : :
415 : : /*****************************************************************************
416 : : * EXPORTED ROUTINES *
417 : : *****************************************************************************/
418 : :
419 : :
420 : : /*
421 : : * ResourceOwnerCreate
422 : : * Create an empty ResourceOwner.
423 : : *
424 : : * All ResourceOwner objects are kept in TopMemoryContext, since they should
425 : : * only be freed explicitly.
426 : : */
427 : : ResourceOwner
8018 428 : 929284 : ResourceOwnerCreate(ResourceOwner parent, const char *name)
429 : : {
430 : : ResourceOwner owner;
431 : :
432 : 929284 : owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
433 : : sizeof(struct ResourceOwnerData));
434 : 929284 : owner->name = name;
435 : :
436 [ + + ]: 929284 : if (parent)
437 : : {
438 : 494786 : owner->parent = parent;
439 : 494786 : owner->nextchild = parent->firstchild;
440 : 494786 : parent->firstchild = owner;
441 : : }
442 : :
470 andres@anarazel.de 443 : 929284 : dlist_init(&owner->aio_handles);
444 : :
8018 tgl@sss.pgh.pa.us 445 : 929284 : return owner;
446 : : }
447 : :
448 : : /*
449 : : * Make sure there is room for at least one more resource in an array.
450 : : *
451 : : * This is separate from actually inserting a resource because if we run out
452 : : * of memory, it's critical to do so *before* acquiring the resource.
453 : : *
454 : : * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between
455 : : * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that
456 : : * you reserved the space for!
457 : : */
458 : : void
965 heikki.linnakangas@i 459 : 240780244 : ResourceOwnerEnlarge(ResourceOwner owner)
460 : : {
461 : : /*
462 : : * Mustn't try to remember more resources after we have already started
463 : : * releasing
464 : : */
465 [ + + ]: 240780244 : if (owner->releasing)
466 [ + - ]: 1 : elog(ERROR, "ResourceOwnerEnlarge called after release started");
467 : :
468 [ + + ]: 240780243 : if (owner->narr < RESOWNER_ARRAY_SIZE)
469 : 240755793 : return; /* no work needed */
470 : :
471 : : /*
472 : : * Is there space in the hash? If not, enlarge it.
473 : : */
474 [ + + ]: 24450 : if (owner->narr + owner->nhash >= owner->grow_at)
475 : : {
476 : : uint32 i,
477 : : oldcap,
478 : : newcap;
479 : : ResourceElem *oldhash;
480 : : ResourceElem *newhash;
481 : :
482 : 8876 : oldhash = owner->hash;
483 : 8876 : oldcap = owner->capacity;
484 : :
485 : : /* Double the capacity (it must stay a power of 2!) */
486 [ + + ]: 8876 : newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE;
487 : 8876 : newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext,
488 : : newcap * sizeof(ResourceElem));
489 : :
490 : : /*
491 : : * We assume we can't fail below this point, so OK to scribble on the
492 : : * owner
493 : : */
494 : 8876 : owner->hash = newhash;
495 : 8876 : owner->capacity = newcap;
496 : 8876 : owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap);
497 : 8876 : owner->nhash = 0;
498 : :
499 [ + + ]: 8876 : if (oldhash != NULL)
500 : : {
501 : : /*
502 : : * Transfer any pre-existing entries into the new hash table; they
503 : : * don't necessarily go where they were before, so this simple
504 : : * logic is the best way.
505 : : */
506 [ + + ]: 1219993 : for (i = 0; i < oldcap; i++)
507 : : {
508 [ + + ]: 1217088 : if (oldhash[i].kind != NULL)
509 : 823820 : ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
510 : : }
511 : :
512 : : /* And release old hash table. */
513 : 2905 : pfree(oldhash);
514 : : }
515 : : }
516 : :
517 : : /* Move items from the array to the hash */
518 [ + + ]: 806850 : for (int i = 0; i < owner->narr; i++)
519 : 782400 : ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
520 : 24450 : owner->narr = 0;
521 : :
522 [ - + ]: 24450 : Assert(owner->nhash <= owner->grow_at);
523 : : }
524 : :
525 : : /*
526 : : * Remember that an object is owned by a ResourceOwner
527 : : *
528 : : * Caller must have previously done ResourceOwnerEnlarge()
529 : : */
530 : : void
531 : 235952217 : ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
532 : : {
533 : : uint32 idx;
534 : :
535 : : /* sanity check the ResourceOwnerDesc */
536 [ - + ]: 235952217 : Assert(kind->release_phase != 0);
537 [ - + ]: 235952217 : Assert(kind->release_priority != 0);
538 : :
539 : : /*
540 : : * Mustn't try to remember more resources after we have already started
541 : : * releasing. We already checked this in ResourceOwnerEnlarge.
542 : : */
543 [ - + ]: 235952217 : Assert(!owner->releasing);
544 [ - + ]: 235952217 : Assert(!owner->sorted);
545 : :
546 [ - + ]: 235952217 : if (owner->narr >= RESOWNER_ARRAY_SIZE)
547 : : {
548 : : /* forgot to call ResourceOwnerEnlarge? */
965 heikki.linnakangas@i 549 [ # # ]:UBC 0 : elog(ERROR, "ResourceOwnerRemember called but array was full");
550 : : }
551 : :
552 : : /* Append to the array. */
965 heikki.linnakangas@i 553 :CBC 235952217 : idx = owner->narr;
554 : 235952217 : owner->arr[idx].item = value;
555 : 235952217 : owner->arr[idx].kind = kind;
556 : 235952217 : owner->narr++;
557 : 235952217 : }
558 : :
559 : : /*
560 : : * Forget that an object is owned by a ResourceOwner
561 : : *
562 : : * Note: If same resource ID is associated with the ResourceOwner more than
563 : : * once, one instance is removed.
564 : : *
565 : : * Note: Forgetting a resource does not guarantee that there is room to
566 : : * remember a new resource. One exception is when you forget the most
567 : : * recently remembered resource; that does make room for a new remember call.
568 : : * Some code callers rely on that exception.
569 : : */
570 : : void
571 : 235590841 : ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
572 : : {
573 : : /*
574 : : * Mustn't call this after we have already started releasing resources.
575 : : * (Release callback functions are not allowed to release additional
576 : : * resources.)
577 : : */
578 [ + + ]: 235590841 : if (owner->releasing)
579 [ + - ]: 1 : elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
580 [ - + ]: 235590840 : Assert(!owner->sorted);
581 : :
582 : : /* Search through all items in the array first. */
583 [ + + ]: 309422857 : for (int i = owner->narr - 1; i >= 0; i--)
584 : : {
585 [ + + ]: 308869847 : if (owner->arr[i].item == value &&
586 [ + + ]: 235038381 : owner->arr[i].kind == kind)
587 : : {
588 : 235037830 : owner->arr[i] = owner->arr[owner->narr - 1];
589 : 235037830 : owner->narr--;
590 : :
591 : : #ifdef RESOWNER_STATS
592 : : narray_lookups++;
593 : : #endif
594 : 235037830 : return;
595 : : }
596 : : }
597 : :
598 : : /* Search hash */
599 [ + - ]: 553010 : if (owner->nhash > 0)
600 : : {
601 : 553010 : uint32 mask = owner->capacity - 1;
602 : : uint32 idx;
603 : :
604 : 553010 : idx = hash_resource_elem(value, kind) & mask;
605 [ + - ]: 116778948 : for (uint32 i = 0; i < owner->capacity; i++)
606 : : {
607 [ + + ]: 116778948 : if (owner->hash[idx].item == value &&
608 [ + + ]: 555386 : owner->hash[idx].kind == kind)
609 : : {
610 : 553010 : owner->hash[idx].item = (Datum) 0;
611 : 553010 : owner->hash[idx].kind = NULL;
612 : 553010 : owner->nhash--;
613 : :
614 : : #ifdef RESOWNER_STATS
615 : : nhash_lookups++;
616 : : #endif
617 : 553010 : return;
618 : : }
619 : 116225938 : idx = (idx + 1) & mask;
620 : : }
621 : : }
622 : :
623 : : /*
624 : : * Use %p to print the reference, since most objects tracked by a resource
625 : : * owner are pointers. It's a bit misleading if it's not a pointer, but
626 : : * this is a programmer error, anyway.
627 : : */
965 heikki.linnakangas@i 628 [ # # ]:UBC 0 : elog(ERROR, "%s %p is not owned by resource owner %s",
629 : : kind->name, DatumGetPointer(value), owner->name);
630 : : }
631 : :
632 : : /*
633 : : * ResourceOwnerRelease
634 : : * Release all resources owned by a ResourceOwner and its descendants,
635 : : * but don't delete the owner objects themselves.
636 : : *
637 : : * Note that this executes just one phase of release, and so typically
638 : : * must be called three times. We do it this way because (a) we want to
639 : : * do all the recursion separately for each phase, thereby preserving
640 : : * the needed order of operations; and (b) xact.c may have other operations
641 : : * to do between the phases.
642 : : *
643 : : * phase: release phase to execute
644 : : * isCommit: true for successful completion of a query or transaction,
645 : : * false for unsuccessful
646 : : * isTopLevel: true if completing a main transaction, else false
647 : : *
648 : : * isCommit is passed because some modules may expect that their resources
649 : : * were all released already if the transaction or portal finished normally.
650 : : * If so it is reasonable to give a warning (NOT an error) should any
651 : : * unreleased resources be present. When isCommit is false, such warnings
652 : : * are generally inappropriate.
653 : : *
654 : : * isTopLevel is passed when we are releasing TopTransactionResourceOwner
655 : : * at completion of a main transaction. This generally means that *all*
656 : : * resources will be released, and so we can optimize things a bit.
657 : : *
658 : : * NOTE: After starting the release process, by calling this function, no new
659 : : * resources can be remembered in the resource owner. You also cannot call
660 : : * ResourceOwnerForget on any previously remembered resources to release
661 : : * resources "in retail" after that, you must let the bulk release take care
662 : : * of them.
663 : : */
664 : : void
8018 tgl@sss.pgh.pa.us 665 :CBC 2645315 : ResourceOwnerRelease(ResourceOwner owner,
666 : : ResourceReleasePhase phase,
667 : : bool isCommit,
668 : : bool isTopLevel)
669 : : {
670 : : /* There's not currently any setup needed before recursing */
3184 671 : 2645315 : ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
672 : :
673 : : #ifdef RESOWNER_STATS
674 : : if (isTopLevel)
675 : : {
676 : : elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
677 : : narray_lookups, nhash_lookups);
678 : : narray_lookups = 0;
679 : : nhash_lookups = 0;
680 : : }
681 : : #endif
8004 682 : 2645315 : }
683 : :
684 : : static void
685 : 2786432 : ResourceOwnerReleaseInternal(ResourceOwner owner,
686 : : ResourceReleasePhase phase,
687 : : bool isCommit,
688 : : bool isTopLevel)
689 : : {
690 : : ResourceOwner child;
691 : : ResourceOwner save;
692 : : ResourceReleaseCallbackItem *item;
693 : : ResourceReleaseCallbackItem *next;
694 : :
695 : : /* Recurse to handle descendants */
8018 696 [ + + ]: 2927549 : for (child = owner->firstchild; child != NULL; child = child->nextchild)
8004 697 : 141117 : ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
698 : :
699 : : /*
700 : : * To release the resources in the right order, sort them by phase and
701 : : * priority.
702 : : *
703 : : * The ReleaseResource callback functions are not allowed to remember or
704 : : * forget any other resources after this. Otherwise we lose track of where
705 : : * we are in processing the hash/array.
706 : : */
965 heikki.linnakangas@i 707 [ + + ]: 2786432 : if (!owner->releasing)
708 : : {
709 [ - + ]: 928810 : Assert(phase == RESOURCE_RELEASE_BEFORE_LOCKS);
710 [ - + ]: 928810 : Assert(!owner->sorted);
711 : 928810 : owner->releasing = true;
712 : : }
713 : : else
714 : : {
715 : : /*
716 : : * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
717 : : * the first call to ResourceOwnerRelease. But if an error happens
718 : : * between the release phases, we might get called again for the same
719 : : * ResourceOwner from AbortTransaction.
720 : : */
721 : : }
722 [ + + ]: 2786432 : if (!owner->sorted)
723 : : {
724 : 928810 : ResourceOwnerSort(owner);
725 : 928810 : owner->sorted = true;
726 : : }
727 : :
728 : : /*
729 : : * Make CurrentResourceOwner point to me, so that the release callback
730 : : * functions know which resource owner is been released.
731 : : */
732 : 2786432 : save = CurrentResourceOwner;
733 : 2786432 : CurrentResourceOwner = owner;
734 : :
735 [ + + ]: 2786432 : if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
736 : : {
737 : : /*
738 : : * Release all resources that need to be released before the locks.
739 : : *
740 : : * During a commit, there shouldn't be any remaining resources ---
741 : : * that would indicate failure to clean up the executor correctly ---
742 : : * so issue warnings. In the abort case, just clean up quietly.
743 : : */
744 : 928812 : ResourceOwnerReleaseAll(owner, phase, isCommit);
745 : :
470 andres@anarazel.de 746 [ + + ]: 928860 : while (!dlist_is_empty(&owner->aio_handles))
747 : : {
748 : 48 : dlist_node *node = dlist_head_node(&owner->aio_handles);
749 : :
750 : 48 : pgaio_io_release_resowner(node, !isCommit);
751 : : }
752 : : }
8018 tgl@sss.pgh.pa.us 753 [ + + ]: 1857620 : else if (phase == RESOURCE_RELEASE_LOCKS)
754 : : {
755 [ + + ]: 928810 : if (isTopLevel)
756 : : {
757 : : /*
758 : : * For a top-level xact we are going to release all locks (or at
759 : : * least all non-session locks), so just do a single lmgr call at
760 : : * the top of the recursion.
761 : : */
762 [ + + ]: 480174 : if (owner == TopTransactionResourceOwner)
763 : : {
764 : 428004 : ProcReleaseLocks(isCommit);
2664 tmunro@postgresql.or 765 : 428004 : ReleasePredicateLocks(isCommit, false);
766 : : }
767 : : }
768 : : else
769 : : {
770 : : /*
771 : : * Release locks retail. Note that if we are committing a
772 : : * subtransaction, we do NOT release its locks yet, but transfer
773 : : * them to the parent.
774 : : */
775 : : LOCALLOCK **locks;
776 : : int nlocks;
777 : :
7979 tgl@sss.pgh.pa.us 778 [ - + ]: 448636 : Assert(owner->parent != NULL);
779 : :
780 : : /*
781 : : * Pass the list of locks owned by this resource owner to the lock
782 : : * manager, unless it has overflowed.
783 : : */
5122 heikki.linnakangas@i 784 [ + + ]: 448636 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
785 : : {
786 : 7962 : locks = NULL;
787 : 7962 : nlocks = 0;
788 : : }
789 : : else
790 : : {
791 : 440674 : locks = owner->locks;
792 : 440674 : nlocks = owner->nlocks;
793 : : }
794 : :
7977 tgl@sss.pgh.pa.us 795 [ + + ]: 448636 : if (isCommit)
5122 heikki.linnakangas@i 796 : 442309 : LockReassignCurrentOwner(locks, nlocks);
797 : : else
798 : 6327 : LockReleaseCurrentOwner(locks, nlocks);
799 : : }
800 : : }
8018 tgl@sss.pgh.pa.us 801 [ + - ]: 928810 : else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
802 : : {
803 : : /*
804 : : * Release all resources that need to be released after the locks.
805 : : */
965 heikki.linnakangas@i 806 : 928810 : ResourceOwnerReleaseAll(owner, phase, isCommit);
807 : : }
808 : :
809 : : /* Let add-on modules get a chance too */
810 [ - + ]: 2786432 : for (item = ResourceRelease_callbacks; item; item = next)
811 : : {
812 : : /* allow callbacks to unregister themselves when called */
965 heikki.linnakangas@i 813 :UBC 0 : next = item->next;
814 : 0 : item->callback(phase, isCommit, isTopLevel, item->arg);
815 : : }
816 : :
965 heikki.linnakangas@i 817 :CBC 2786432 : CurrentResourceOwner = save;
818 : 2786432 : }
819 : :
820 : : /*
821 : : * ResourceOwnerReleaseAllOfKind
822 : : * Release all resources of a certain type held by this owner.
823 : : */
824 : : void
825 : 10243 : ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
826 : : {
827 : : /* Mustn't call this after we have already started releasing resources. */
828 [ - + ]: 10243 : if (owner->releasing)
965 heikki.linnakangas@i 829 [ # # ]:UBC 0 : elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
965 heikki.linnakangas@i 830 [ - + ]:CBC 10243 : Assert(!owner->sorted);
831 : :
832 : : /*
833 : : * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
834 : : * while we're scanning the owner. Enlarging the hash would cause us to
835 : : * lose track of the point we're scanning.
836 : : */
837 : 10243 : owner->releasing = true;
838 : :
839 : : /* Array first */
840 [ + + ]: 46727 : for (int i = 0; i < owner->narr; i++)
841 : : {
842 [ + - ]: 36484 : if (owner->arr[i].kind == kind)
843 : : {
844 : 36484 : Datum value = owner->arr[i].item;
845 : :
846 : 36484 : owner->arr[i] = owner->arr[owner->narr - 1];
847 : 36484 : owner->narr--;
848 : 36484 : i--;
849 : :
850 : 36484 : kind->ReleaseResource(value);
851 : : }
852 : : }
853 : :
854 : : /* Then hash */
855 [ + + ]: 32515 : for (int i = 0; i < owner->capacity; i++)
856 : : {
857 [ + + ]: 22272 : if (owner->hash[i].kind == kind)
858 : : {
859 : 11136 : Datum value = owner->hash[i].item;
860 : :
861 : 11136 : owner->hash[i].item = (Datum) 0;
862 : 11136 : owner->hash[i].kind = NULL;
863 : 11136 : owner->nhash--;
864 : :
865 : 11136 : kind->ReleaseResource(value);
866 : : }
867 : : }
868 : 10243 : owner->releasing = false;
2287 tgl@sss.pgh.pa.us 869 : 10243 : }
870 : :
871 : : /*
872 : : * ResourceOwnerDelete
873 : : * Delete an owner object and its descendants.
874 : : *
875 : : * The caller must have already released all resources in the object tree.
876 : : */
877 : : void
8018 878 : 923623 : ResourceOwnerDelete(ResourceOwner owner)
879 : : {
880 : : /* We had better not be deleting CurrentResourceOwner ... */
881 [ - + ]: 923623 : Assert(owner != CurrentResourceOwner);
882 : :
883 : : /* And it better not own any resources, either */
965 heikki.linnakangas@i 884 [ - + ]: 923623 : Assert(owner->narr == 0);
885 [ - + ]: 923623 : Assert(owner->nhash == 0);
5122 886 [ + + - + ]: 923623 : Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
887 : :
888 : : /*
889 : : * Delete children. The recursive call will delink the child from me, so
890 : : * just iterate as long as there is a child.
891 : : */
8018 tgl@sss.pgh.pa.us 892 [ + + ]: 970666 : while (owner->firstchild != NULL)
893 : 47043 : ResourceOwnerDelete(owner->firstchild);
894 : :
895 : : /*
896 : : * We delink the owner from its parent before deleting it, so that if
897 : : * there's an error we won't have deleted/busted owners still attached to
898 : : * the owner tree. Better a leak than a crash.
899 : : */
900 : 923623 : ResourceOwnerNewParent(owner, NULL);
901 : :
902 : : /* And free the object. */
965 heikki.linnakangas@i 903 [ + + ]: 923623 : if (owner->hash)
904 : 5971 : pfree(owner->hash);
8018 tgl@sss.pgh.pa.us 905 : 923623 : pfree(owner);
906 : 923623 : }
907 : :
908 : : /*
909 : : * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
910 : : */
911 : : ResourceOwner
7977 912 : 442309 : ResourceOwnerGetParent(ResourceOwner owner)
913 : : {
914 : 442309 : return owner->parent;
915 : : }
916 : :
917 : : /*
918 : : * Reassign a ResourceOwner to have a new parent
919 : : */
920 : : void
8018 921 : 923662 : ResourceOwnerNewParent(ResourceOwner owner,
922 : : ResourceOwner newparent)
923 : : {
924 : 923662 : ResourceOwner oldparent = owner->parent;
925 : :
926 [ + + ]: 923662 : if (oldparent)
927 : : {
928 [ + + ]: 494825 : if (owner == oldparent->firstchild)
929 : 484816 : oldparent->firstchild = owner->nextchild;
930 : : else
931 : : {
932 : : ResourceOwner child;
933 : :
934 [ + - ]: 11642 : for (child = oldparent->firstchild; child; child = child->nextchild)
935 : : {
936 [ + + ]: 11642 : if (owner == child->nextchild)
937 : : {
938 : 10009 : child->nextchild = owner->nextchild;
939 : 10009 : break;
940 : : }
941 : : }
942 : : }
943 : : }
944 : :
945 [ + + ]: 923662 : if (newparent)
946 : : {
947 [ - + ]: 39 : Assert(owner != newparent);
948 : 39 : owner->parent = newparent;
949 : 39 : owner->nextchild = newparent->firstchild;
950 : 39 : newparent->firstchild = owner;
951 : : }
952 : : else
953 : : {
954 : 923623 : owner->parent = NULL;
955 : 923623 : owner->nextchild = NULL;
956 : : }
957 : 923662 : }
958 : :
959 : : /*
960 : : * Register or deregister callback functions for resource cleanup
961 : : *
962 : : * These functions can be used by dynamically loaded modules. These used
963 : : * to be the only way for an extension to register custom resource types
964 : : * with a resource owner, but nowadays it is easier to define a new
965 : : * ResourceOwnerDesc with custom callbacks.
966 : : */
967 : : void
8018 tgl@sss.pgh.pa.us 968 :UBC 0 : RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
969 : : {
970 : : ResourceReleaseCallbackItem *item;
971 : :
972 : : item = (ResourceReleaseCallbackItem *)
973 : 0 : MemoryContextAlloc(TopMemoryContext,
974 : : sizeof(ResourceReleaseCallbackItem));
975 : 0 : item->callback = callback;
976 : 0 : item->arg = arg;
977 : 0 : item->next = ResourceRelease_callbacks;
978 : 0 : ResourceRelease_callbacks = item;
979 : 0 : }
980 : :
981 : : void
982 : 0 : UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
983 : : {
984 : : ResourceReleaseCallbackItem *item;
985 : : ResourceReleaseCallbackItem *prev;
986 : :
987 : 0 : prev = NULL;
988 [ # # ]: 0 : for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
989 : : {
990 [ # # # # ]: 0 : if (item->callback == callback && item->arg == arg)
991 : : {
992 [ # # ]: 0 : if (prev)
993 : 0 : prev->next = item->next;
994 : : else
995 : 0 : ResourceRelease_callbacks = item->next;
996 : 0 : pfree(item);
997 : 0 : break;
998 : : }
999 : : }
1000 : 0 : }
1001 : :
1002 : : /*
1003 : : * Establish an AuxProcessResourceOwner for the current process.
1004 : : */
1005 : : void
2904 tgl@sss.pgh.pa.us 1006 :CBC 5661 : CreateAuxProcessResourceOwner(void)
1007 : : {
1008 [ - + ]: 5661 : Assert(AuxProcessResourceOwner == NULL);
1009 [ - + ]: 5661 : Assert(CurrentResourceOwner == NULL);
1010 : 5661 : AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
1011 : 5661 : CurrentResourceOwner = AuxProcessResourceOwner;
1012 : :
1013 : : /*
1014 : : * Register a shmem-exit callback for cleanup of aux-process resource
1015 : : * owner. (This needs to run after, e.g., ShutdownXLOG.)
1016 : : */
1017 : 5661 : on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
1018 : 5661 : }
1019 : :
1020 : : /*
1021 : : * Convenience routine to release all resources tracked in
1022 : : * AuxProcessResourceOwner (but that resowner is not destroyed here).
1023 : : * Warn about leaked resources if isCommit is true.
1024 : : */
1025 : : void
1026 : 6024 : ReleaseAuxProcessResources(bool isCommit)
1027 : : {
1028 : : /*
1029 : : * At this writing, the only thing that could actually get released is
1030 : : * buffer pins; but we may as well do the full release protocol.
1031 : : */
1032 : 6024 : ResourceOwnerRelease(AuxProcessResourceOwner,
1033 : : RESOURCE_RELEASE_BEFORE_LOCKS,
1034 : : isCommit, true);
1035 : 6024 : ResourceOwnerRelease(AuxProcessResourceOwner,
1036 : : RESOURCE_RELEASE_LOCKS,
1037 : : isCommit, true);
1038 : 6024 : ResourceOwnerRelease(AuxProcessResourceOwner,
1039 : : RESOURCE_RELEASE_AFTER_LOCKS,
1040 : : isCommit, true);
1041 : : /* allow it to be reused */
965 heikki.linnakangas@i 1042 : 6024 : AuxProcessResourceOwner->releasing = false;
1043 : 6024 : AuxProcessResourceOwner->sorted = false;
2904 tgl@sss.pgh.pa.us 1044 : 6024 : }
1045 : :
1046 : : /*
1047 : : * Shmem-exit callback for the same.
1048 : : * Warn about leaked resources if process exit code is zero (ie normal).
1049 : : */
1050 : : static void
1051 : 5661 : ReleaseAuxProcessResourcesCallback(int code, Datum arg)
1052 : : {
1053 : 5661 : bool isCommit = (code == 0);
1054 : :
1055 : 5661 : ReleaseAuxProcessResources(isCommit);
1056 : 5661 : }
1057 : :
1058 : : /*
1059 : : * Remember that a Local Lock is owned by a ResourceOwner
1060 : : *
1061 : : * This is different from the generic ResourceOwnerRemember in that the list of
1062 : : * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
1063 : : * and when it overflows, we stop tracking locks. The point of only remembering
1064 : : * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
1065 : : * ResourceOwnerForgetLock doesn't need to scan through a large array to find
1066 : : * the entry.
1067 : : */
1068 : : void
4780 bruce@momjian.us 1069 : 24106660 : ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
1070 : : {
3808 tgl@sss.pgh.pa.us 1071 [ - + ]: 24106660 : Assert(locallock != NULL);
1072 : :
5122 heikki.linnakangas@i 1073 [ + + ]: 24106660 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
4780 bruce@momjian.us 1074 : 3326323 : return; /* we have already overflowed */
1075 : :
5122 heikki.linnakangas@i 1076 [ + + ]: 20780337 : if (owner->nlocks < MAX_RESOWNER_LOCKS)
1077 : 20762826 : owner->locks[owner->nlocks] = locallock;
1078 : : else
1079 : : {
1080 : : /* overflowed */
1081 : : }
1082 : 20780337 : owner->nlocks++;
1083 : : }
1084 : :
1085 : : /*
1086 : : * Forget that a Local Lock is owned by a ResourceOwner
1087 : : */
1088 : : void
1089 : 24106660 : ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1090 : : {
1091 : : int i;
1092 : :
1093 [ + + ]: 24106660 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
4780 bruce@momjian.us 1094 : 3606499 : return; /* we have overflowed */
1095 : :
5122 heikki.linnakangas@i 1096 [ - + ]: 20500161 : Assert(owner->nlocks > 0);
1097 [ + - ]: 23605593 : for (i = owner->nlocks - 1; i >= 0; i--)
1098 : : {
1099 [ + + ]: 23605593 : if (locallock == owner->locks[i])
1100 : : {
1101 : 20500161 : owner->locks[i] = owner->locks[owner->nlocks - 1];
1102 : 20500161 : owner->nlocks--;
1103 : 20500161 : return;
1104 : : }
1105 : : }
5122 heikki.linnakangas@i 1106 [ # # ]:UBC 0 : elog(ERROR, "lock reference %p is not owned by resource owner %s",
1107 : : locallock, owner->name);
1108 : : }
1109 : :
1110 : : void
470 andres@anarazel.de 1111 :CBC 1491102 : ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1112 : : {
1113 : 1491102 : dlist_push_tail(&owner->aio_handles, ioh_node);
1114 : 1491102 : }
1115 : :
1116 : : void
1117 : 1491102 : ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1118 : : {
1119 : 1491102 : dlist_delete_from(&owner->aio_handles, ioh_node);
1120 : 1491102 : }
|