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-2025, 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
720 heikki.linnakangas@i 222 :CBC 1966872 : 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 : : */
76 tgl@sss.pgh.pa.us 234 :GNC 1966872 : 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
720 heikki.linnakangas@i 242 :CBC 1484602 : ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
243 : : {
244 : 1484602 : uint32 mask = owner->capacity - 1;
245 : : uint32 idx;
246 : :
247 [ - + ]: 1484602 : Assert(kind != NULL);
248 : :
249 : : /* Insert into first free slot at or after hash location. */
250 : 1484602 : idx = hash_resource_elem(value, kind) & mask;
251 : : for (;;)
252 : : {
253 [ + + ]: 195772750 : if (owner->hash[idx].kind == NULL)
254 : 1484602 : break; /* found a free slot */
255 : 194288148 : idx = (idx + 1) & mask;
256 : : }
257 : 1484602 : owner->hash[idx].item = value;
258 : 1484602 : owner->hash[idx].kind = kind;
259 : 1484602 : owner->nhash++;
3563 tgl@sss.pgh.pa.us 260 : 1484602 : }
261 : :
262 : : /*
263 : : * Comparison function to sort by release phase and priority
264 : : */
265 : : static int
720 heikki.linnakangas@i 266 : 575211 : resource_priority_cmp(const void *a, const void *b)
267 : : {
268 : 575211 : const ResourceElem *ra = (const ResourceElem *) a;
269 : 575211 : const ResourceElem *rb = (const ResourceElem *) b;
270 : :
271 : : /* Note: reverse order */
272 [ + + ]: 575211 : if (ra->kind->release_phase == rb->kind->release_phase)
620 nathan@postgresql.or 273 : 447595 : return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
720 heikki.linnakangas@i 274 [ + + ]: 127616 : else if (ra->kind->release_phase > rb->kind->release_phase)
275 : 15110 : return -1;
276 : : else
277 : 112506 : 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 : 717423 : ResourceOwnerSort(ResourceOwner owner)
290 : : {
291 : : ResourceElem *items;
292 : : uint32 nitems;
293 : :
294 [ + + ]: 717423 : if (owner->nhash == 0)
295 : : {
296 : 717321 : items = owner->arr;
297 : 717321 : 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 : 102 : uint32 dst = 0;
306 : :
307 [ + + ]: 555238 : for (int idx = 0; idx < owner->capacity; idx++)
308 : : {
309 [ + + ]: 555136 : if (owner->hash[idx].kind != NULL)
310 : : {
311 [ + + ]: 212962 : if (dst != idx)
312 : 212568 : owner->hash[dst] = owner->hash[idx];
313 : 212962 : 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 [ - + ]: 102 : Assert(dst + owner->narr <= owner->capacity);
325 [ + + ]: 483 : for (int idx = 0; idx < owner->narr; idx++)
326 : : {
327 : 381 : owner->hash[dst] = owner->arr[idx];
328 : 381 : dst++;
329 : : }
330 [ - + ]: 102 : Assert(dst == owner->nhash + owner->narr);
331 : 102 : owner->narr = 0;
332 : 102 : owner->nhash = dst;
333 : :
334 : 102 : items = owner->hash;
335 : 102 : nitems = owner->nhash;
336 : : }
337 : :
338 : 717423 : qsort(items, nitems, sizeof(ResourceElem), resource_priority_cmp);
3563 tgl@sss.pgh.pa.us 339 : 717423 : }
340 : :
341 : : /*
342 : : * Call the ReleaseResource callback on entries with given 'phase'.
343 : : */
344 : : static void
720 heikki.linnakangas@i 345 : 1434848 : ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase,
346 : : bool printLeakWarnings)
347 : : {
348 : : ResourceElem *items;
349 : : uint32 nitems;
350 : :
351 : : /*
352 : : * ResourceOwnerSort must've been called already. All the resources are
353 : : * either in the array or the hash.
354 : : */
355 [ - + ]: 1434848 : Assert(owner->releasing);
356 [ - + ]: 1434848 : Assert(owner->sorted);
719 357 [ + + ]: 1434848 : if (owner->nhash == 0)
358 : : {
720 359 : 1434644 : items = owner->arr;
360 : 1434644 : nitems = owner->narr;
361 : : }
362 : : else
363 : : {
364 [ - + ]: 204 : Assert(owner->narr == 0);
365 : 204 : items = owner->hash;
366 : 204 : nitems = owner->nhash;
367 : : }
368 : :
369 : : /*
370 : : * The resources are sorted in reverse priority order. Release them
371 : : * starting from the end, until we hit the end of the phase that we are
372 : : * releasing now. We will continue from there when called again for the
373 : : * next phase.
374 : : */
375 [ + + ]: 1716666 : while (nitems > 0)
376 : : {
377 : 298944 : uint32 idx = nitems - 1;
378 : 298944 : Datum value = items[idx].item;
379 : 298944 : const ResourceOwnerDesc *kind = items[idx].kind;
380 : :
381 [ + + ]: 298944 : if (kind->release_phase > phase)
382 : 17126 : break;
383 [ - + ]: 281818 : Assert(kind->release_phase == phase);
384 : :
385 [ + + ]: 281818 : if (printLeakWarnings)
386 : : {
387 : : char *res_str;
388 : :
389 : 12 : res_str = kind->DebugPrint ?
390 : 6 : kind->DebugPrint(value)
391 [ + - ]: 6 : : psprintf("%s %p", kind->name, DatumGetPointer(value));
392 [ + - ]: 6 : elog(WARNING, "resource was not closed: %s", res_str);
393 : 6 : pfree(res_str);
394 : : }
395 : 281818 : kind->ReleaseResource(value);
396 : 281818 : nitems--;
397 : : }
719 398 [ + + ]: 1434848 : if (owner->nhash == 0)
720 399 : 1434644 : owner->narr = nitems;
400 : : else
401 : 204 : owner->nhash = nitems;
3563 tgl@sss.pgh.pa.us 402 : 1434848 : }
403 : :
404 : :
405 : : /*****************************************************************************
406 : : * EXPORTED ROUTINES *
407 : : *****************************************************************************/
408 : :
409 : :
410 : : /*
411 : : * ResourceOwnerCreate
412 : : * Create an empty ResourceOwner.
413 : : *
414 : : * All ResourceOwner objects are kept in TopMemoryContext, since they should
415 : : * only be freed explicitly.
416 : : */
417 : : ResourceOwner
7773 418 : 717832 : ResourceOwnerCreate(ResourceOwner parent, const char *name)
419 : : {
420 : : ResourceOwner owner;
421 : :
422 : 717832 : owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
423 : : sizeof(struct ResourceOwnerData));
424 : 717832 : owner->name = name;
425 : :
426 [ + + ]: 717832 : if (parent)
427 : : {
428 : 388896 : owner->parent = parent;
429 : 388896 : owner->nextchild = parent->firstchild;
430 : 388896 : parent->firstchild = owner;
431 : : }
432 : :
225 andres@anarazel.de 433 : 717832 : dlist_init(&owner->aio_handles);
434 : :
7773 tgl@sss.pgh.pa.us 435 : 717832 : return owner;
436 : : }
437 : :
438 : : /*
439 : : * Make sure there is room for at least one more resource in an array.
440 : : *
441 : : * This is separate from actually inserting a resource because if we run out
442 : : * of memory, it's critical to do so *before* acquiring the resource.
443 : : *
444 : : * NB: Make sure there are no unrelated ResourceOwnerRemember() calls between
445 : : * your ResourceOwnerEnlarge() call and the ResourceOwnerRemember() call that
446 : : * you reserved the space for!
447 : : */
448 : : void
720 heikki.linnakangas@i 449 : 172899159 : ResourceOwnerEnlarge(ResourceOwner owner)
450 : : {
451 : : /*
452 : : * Mustn't try to remember more resources after we have already started
453 : : * releasing
454 : : */
455 [ + + ]: 172899159 : if (owner->releasing)
456 [ + - ]: 1 : elog(ERROR, "ResourceOwnerEnlarge called after release started");
457 : :
458 [ + + ]: 172899158 : if (owner->narr < RESOWNER_ARRAY_SIZE)
459 : 172877169 : return; /* no work needed */
460 : :
461 : : /*
462 : : * Is there space in the hash? If not, enlarge it.
463 : : */
464 [ + + ]: 21989 : if (owner->narr + owner->nhash >= owner->grow_at)
465 : : {
466 : : uint32 i,
467 : : oldcap,
468 : : newcap;
469 : : ResourceElem *oldhash;
470 : : ResourceElem *newhash;
471 : :
472 : 7449 : oldhash = owner->hash;
473 : 7449 : oldcap = owner->capacity;
474 : :
475 : : /* Double the capacity (it must stay a power of 2!) */
476 [ + + ]: 7449 : newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE;
477 : 7449 : newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext,
478 : : newcap * sizeof(ResourceElem));
479 : :
480 : : /*
481 : : * We assume we can't fail below this point, so OK to scribble on the
482 : : * owner
483 : : */
484 : 7449 : owner->hash = newhash;
485 : 7449 : owner->capacity = newcap;
486 : 7449 : owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap);
487 : 7449 : owner->nhash = 0;
488 : :
489 [ + + ]: 7449 : if (oldhash != NULL)
490 : : {
491 : : /*
492 : : * Transfer any pre-existing entries into the new hash table; they
493 : : * don't necessarily go where they were before, so this simple
494 : : * logic is the best way.
495 : : */
496 [ + + ]: 1160759 : for (i = 0; i < oldcap; i++)
497 : : {
498 [ + + ]: 1157952 : if (oldhash[i].kind != NULL)
499 : 780954 : ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
500 : : }
501 : :
502 : : /* And release old hash table. */
503 : 2807 : pfree(oldhash);
504 : : }
505 : : }
506 : :
507 : : /* Move items from the array to the hash */
508 [ + + ]: 725637 : for (int i = 0; i < owner->narr; i++)
509 : 703648 : ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
510 : 21989 : owner->narr = 0;
511 : :
512 [ - + ]: 21989 : Assert(owner->nhash <= owner->grow_at);
513 : : }
514 : :
515 : : /*
516 : : * Remember that an object is owned by a ResourceOwner
517 : : *
518 : : * Caller must have previously done ResourceOwnerEnlarge()
519 : : */
520 : : void
521 : 168943208 : ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
522 : : {
523 : : uint32 idx;
524 : :
525 : : /* sanity check the ResourceOwnerDesc */
526 [ - + ]: 168943208 : Assert(kind->release_phase != 0);
527 [ - + ]: 168943208 : Assert(kind->release_priority != 0);
528 : :
529 : : /*
530 : : * Mustn't try to remember more resources after we have already started
531 : : * releasing. We already checked this in ResourceOwnerEnlarge.
532 : : */
533 [ - + ]: 168943208 : Assert(!owner->releasing);
534 [ - + ]: 168943208 : Assert(!owner->sorted);
535 : :
536 [ - + ]: 168943208 : if (owner->narr >= RESOWNER_ARRAY_SIZE)
537 : : {
538 : : /* forgot to call ResourceOwnerEnlarge? */
720 heikki.linnakangas@i 539 [ # # ]:UBC 0 : elog(ERROR, "ResourceOwnerRemember called but array was full");
540 : : }
541 : :
542 : : /* Append to the array. */
720 heikki.linnakangas@i 543 :CBC 168943208 : idx = owner->narr;
544 : 168943208 : owner->arr[idx].item = value;
545 : 168943208 : owner->arr[idx].kind = kind;
546 : 168943208 : owner->narr++;
547 : 168943208 : }
548 : :
549 : : /*
550 : : * Forget that an object is owned by a ResourceOwner
551 : : *
552 : : * Note: If same resource ID is associated with the ResourceOwner more than
553 : : * once, one instance is removed.
554 : : *
555 : : * Note: Forgetting a resource does not guarantee that there is room to
556 : : * remember a new resource. One exception is when you forget the most
557 : : * recently remembered resource; that does make room for a new remember call.
558 : : * Some code callers rely on that exception.
559 : : */
560 : : void
561 : 168624503 : ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
562 : : {
563 : : /*
564 : : * Mustn't call this after we have already started releasing resources.
565 : : * (Release callback functions are not allowed to release additional
566 : : * resources.)
567 : : */
568 [ + + ]: 168624503 : if (owner->releasing)
569 [ + - ]: 1 : elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
570 [ - + ]: 168624502 : Assert(!owner->sorted);
571 : :
572 : : /* Search through all items in the array first. */
573 [ + + ]: 220109067 : for (int i = owner->narr - 1; i >= 0; i--)
574 : : {
575 [ + + ]: 219626797 : if (owner->arr[i].item == value &&
576 [ + + ]: 168142681 : owner->arr[i].kind == kind)
577 : : {
578 : 168142232 : owner->arr[i] = owner->arr[owner->narr - 1];
579 : 168142232 : owner->narr--;
580 : :
581 : : #ifdef RESOWNER_STATS
582 : : narray_lookups++;
583 : : #endif
584 : 168142232 : return;
585 : : }
586 : : }
587 : :
588 : : /* Search hash */
589 [ + - ]: 482270 : if (owner->nhash > 0)
590 : : {
591 : 482270 : uint32 mask = owner->capacity - 1;
592 : : uint32 idx;
593 : :
594 : 482270 : idx = hash_resource_elem(value, kind) & mask;
595 [ + - ]: 101259770 : for (uint32 i = 0; i < owner->capacity; i++)
596 : : {
597 [ + + ]: 101259770 : if (owner->hash[idx].item == value &&
598 [ + + ]: 484364 : owner->hash[idx].kind == kind)
599 : : {
600 : 482270 : owner->hash[idx].item = (Datum) 0;
601 : 482270 : owner->hash[idx].kind = NULL;
602 : 482270 : owner->nhash--;
603 : :
604 : : #ifdef RESOWNER_STATS
605 : : nhash_lookups++;
606 : : #endif
607 : 482270 : return;
608 : : }
609 : 100777500 : idx = (idx + 1) & mask;
610 : : }
611 : : }
612 : :
613 : : /*
614 : : * Use %p to print the reference, since most objects tracked by a resource
615 : : * owner are pointers. It's a bit misleading if it's not a pointer, but
616 : : * this is a programmer error, anyway.
617 : : */
720 heikki.linnakangas@i 618 [ # # ]:UBC 0 : elog(ERROR, "%s %p is not owned by resource owner %s",
619 : : kind->name, DatumGetPointer(value), owner->name);
620 : : }
621 : :
622 : : /*
623 : : * ResourceOwnerRelease
624 : : * Release all resources owned by a ResourceOwner and its descendants,
625 : : * but don't delete the owner objects themselves.
626 : : *
627 : : * Note that this executes just one phase of release, and so typically
628 : : * must be called three times. We do it this way because (a) we want to
629 : : * do all the recursion separately for each phase, thereby preserving
630 : : * the needed order of operations; and (b) xact.c may have other operations
631 : : * to do between the phases.
632 : : *
633 : : * phase: release phase to execute
634 : : * isCommit: true for successful completion of a query or transaction,
635 : : * false for unsuccessful
636 : : * isTopLevel: true if completing a main transaction, else false
637 : : *
638 : : * isCommit is passed because some modules may expect that their resources
639 : : * were all released already if the transaction or portal finished normally.
640 : : * If so it is reasonable to give a warning (NOT an error) should any
641 : : * unreleased resources be present. When isCommit is false, such warnings
642 : : * are generally inappropriate.
643 : : *
644 : : * isTopLevel is passed when we are releasing TopTransactionResourceOwner
645 : : * at completion of a main transaction. This generally means that *all*
646 : : * resources will be released, and so we can optimize things a bit.
647 : : *
648 : : * NOTE: After starting the release process, by calling this function, no new
649 : : * resources can be remembered in the resource owner. You also cannot call
650 : : * ResourceOwnerForget on any previously remembered resources to release
651 : : * resources "in retail" after that, you must let the bulk release take care
652 : : * of them.
653 : : */
654 : : void
7773 tgl@sss.pgh.pa.us 655 :CBC 2048810 : ResourceOwnerRelease(ResourceOwner owner,
656 : : ResourceReleasePhase phase,
657 : : bool isCommit,
658 : : bool isTopLevel)
659 : : {
660 : : /* There's not currently any setup needed before recursing */
2939 661 : 2048810 : ResourceOwnerReleaseInternal(owner, phase, isCommit, isTopLevel);
662 : :
663 : : #ifdef RESOWNER_STATS
664 : : if (isTopLevel)
665 : : {
666 : : elog(LOG, "RESOWNER STATS: lookups: array %d, hash %d",
667 : : narray_lookups, nhash_lookups);
668 : : narray_lookups = 0;
669 : : nhash_lookups = 0;
670 : : }
671 : : #endif
7759 672 : 2048810 : }
673 : :
674 : : static void
675 : 2152271 : ResourceOwnerReleaseInternal(ResourceOwner owner,
676 : : ResourceReleasePhase phase,
677 : : bool isCommit,
678 : : bool isTopLevel)
679 : : {
680 : : ResourceOwner child;
681 : : ResourceOwner save;
682 : : ResourceReleaseCallbackItem *item;
683 : : ResourceReleaseCallbackItem *next;
684 : :
685 : : /* Recurse to handle descendants */
7773 686 [ + + ]: 2255732 : for (child = owner->firstchild; child != NULL; child = child->nextchild)
7759 687 : 103461 : ResourceOwnerReleaseInternal(child, phase, isCommit, isTopLevel);
688 : :
689 : : /*
690 : : * To release the resources in the right order, sort them by phase and
691 : : * priority.
692 : : *
693 : : * The ReleaseResource callback functions are not allowed to remember or
694 : : * forget any other resources after this. Otherwise we lose track of where
695 : : * we are in processing the hash/array.
696 : : */
720 heikki.linnakangas@i 697 [ + + ]: 2152271 : if (!owner->releasing)
698 : : {
699 [ - + ]: 717423 : Assert(phase == RESOURCE_RELEASE_BEFORE_LOCKS);
700 [ - + ]: 717423 : Assert(!owner->sorted);
701 : 717423 : owner->releasing = true;
702 : : }
703 : : else
704 : : {
705 : : /*
706 : : * Phase is normally > RESOURCE_RELEASE_BEFORE_LOCKS, if this is not
707 : : * the first call to ResourceOwnerRelease. But if an error happens
708 : : * between the release phases, we might get called again for the same
709 : : * ResourceOwner from AbortTransaction.
710 : : */
711 : : }
712 [ + + ]: 2152271 : if (!owner->sorted)
713 : : {
714 : 717423 : ResourceOwnerSort(owner);
715 : 717423 : owner->sorted = true;
716 : : }
717 : :
718 : : /*
719 : : * Make CurrentResourceOwner point to me, so that the release callback
720 : : * functions know which resource owner is been released.
721 : : */
722 : 2152271 : save = CurrentResourceOwner;
723 : 2152271 : CurrentResourceOwner = owner;
724 : :
725 [ + + ]: 2152271 : if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
726 : : {
727 : : /*
728 : : * Release all resources that need to be released before the locks.
729 : : *
730 : : * During a commit, there shouldn't be any remaining resources ---
731 : : * that would indicate failure to clean up the executor correctly ---
732 : : * so issue warnings. In the abort case, just clean up quietly.
733 : : */
734 : 717425 : ResourceOwnerReleaseAll(owner, phase, isCommit);
735 : :
225 andres@anarazel.de 736 [ + + ]: 717491 : while (!dlist_is_empty(&owner->aio_handles))
737 : : {
738 : 66 : dlist_node *node = dlist_head_node(&owner->aio_handles);
739 : :
740 : 66 : pgaio_io_release_resowner(node, !isCommit);
741 : : }
742 : : }
7773 tgl@sss.pgh.pa.us 743 [ + + ]: 1434846 : else if (phase == RESOURCE_RELEASE_LOCKS)
744 : : {
745 [ + + ]: 717423 : if (isTopLevel)
746 : : {
747 : : /*
748 : : * For a top-level xact we are going to release all locks (or at
749 : : * least all non-session locks), so just do a single lmgr call at
750 : : * the top of the recursion.
751 : : */
752 [ + + ]: 362357 : if (owner == TopTransactionResourceOwner)
753 : : {
754 : 322927 : ProcReleaseLocks(isCommit);
2419 tmunro@postgresql.or 755 : 322927 : ReleasePredicateLocks(isCommit, false);
756 : : }
757 : : }
758 : : else
759 : : {
760 : : /*
761 : : * Release locks retail. Note that if we are committing a
762 : : * subtransaction, we do NOT release its locks yet, but transfer
763 : : * them to the parent.
764 : : */
765 : : LOCALLOCK **locks;
766 : : int nlocks;
767 : :
7734 tgl@sss.pgh.pa.us 768 [ - + ]: 355066 : Assert(owner->parent != NULL);
769 : :
770 : : /*
771 : : * Pass the list of locks owned by this resource owner to the lock
772 : : * manager, unless it has overflowed.
773 : : */
4877 heikki.linnakangas@i 774 [ + + ]: 355066 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
775 : : {
776 : 3480 : locks = NULL;
777 : 3480 : nlocks = 0;
778 : : }
779 : : else
780 : : {
781 : 351586 : locks = owner->locks;
782 : 351586 : nlocks = owner->nlocks;
783 : : }
784 : :
7732 tgl@sss.pgh.pa.us 785 [ + + ]: 355066 : if (isCommit)
4877 heikki.linnakangas@i 786 : 349726 : LockReassignCurrentOwner(locks, nlocks);
787 : : else
788 : 5340 : LockReleaseCurrentOwner(locks, nlocks);
789 : : }
790 : : }
7773 tgl@sss.pgh.pa.us 791 [ + - ]: 717423 : else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
792 : : {
793 : : /*
794 : : * Release all resources that need to be released after the locks.
795 : : */
720 heikki.linnakangas@i 796 : 717423 : ResourceOwnerReleaseAll(owner, phase, isCommit);
797 : : }
798 : :
799 : : /* Let add-on modules get a chance too */
800 [ - + ]: 2152271 : for (item = ResourceRelease_callbacks; item; item = next)
801 : : {
802 : : /* allow callbacks to unregister themselves when called */
720 heikki.linnakangas@i 803 :UBC 0 : next = item->next;
804 : 0 : item->callback(phase, isCommit, isTopLevel, item->arg);
805 : : }
806 : :
720 heikki.linnakangas@i 807 :CBC 2152271 : CurrentResourceOwner = save;
808 : 2152271 : }
809 : :
810 : : /*
811 : : * ResourceOwnerReleaseAllOfKind
812 : : * Release all resources of a certain type held by this owner.
813 : : */
814 : : void
815 : 8377 : ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
816 : : {
817 : : /* Mustn't call this after we have already started releasing resources. */
818 [ - + ]: 8377 : if (owner->releasing)
720 heikki.linnakangas@i 819 [ # # ]:UBC 0 : elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
720 heikki.linnakangas@i 820 [ - + ]:CBC 8377 : Assert(!owner->sorted);
821 : :
822 : : /*
823 : : * Temporarily set 'releasing', to prevent calls to ResourceOwnerRemember
824 : : * while we're scanning the owner. Enlarging the hash would cause us to
825 : : * lose track of the point we're scanning.
826 : : */
827 : 8377 : owner->releasing = true;
828 : :
829 : : /* Array first */
830 [ + + ]: 36849 : for (int i = 0; i < owner->narr; i++)
831 : : {
832 [ + - ]: 28472 : if (owner->arr[i].kind == kind)
833 : : {
834 : 28472 : Datum value = owner->arr[i].item;
835 : :
836 : 28472 : owner->arr[i] = owner->arr[owner->narr - 1];
837 : 28472 : owner->narr--;
838 : 28472 : i--;
839 : :
840 : 28472 : kind->ReleaseResource(value);
841 : : }
842 : : }
843 : :
844 : : /* Then hash */
845 [ + + ]: 25209 : for (int i = 0; i < owner->capacity; i++)
846 : : {
847 [ + + ]: 16832 : if (owner->hash[i].kind == kind)
848 : : {
849 : 8416 : Datum value = owner->hash[i].item;
850 : :
851 : 8416 : owner->hash[i].item = (Datum) 0;
852 : 8416 : owner->hash[i].kind = NULL;
853 : 8416 : owner->nhash--;
854 : :
855 : 8416 : kind->ReleaseResource(value);
856 : : }
857 : : }
858 : 8377 : owner->releasing = false;
2042 tgl@sss.pgh.pa.us 859 : 8377 : }
860 : :
861 : : /*
862 : : * ResourceOwnerDelete
863 : : * Delete an owner object and its descendants.
864 : : *
865 : : * The caller must have already released all resources in the object tree.
866 : : */
867 : : void
7773 868 : 712554 : ResourceOwnerDelete(ResourceOwner owner)
869 : : {
870 : : /* We had better not be deleting CurrentResourceOwner ... */
871 [ - + ]: 712554 : Assert(owner != CurrentResourceOwner);
872 : :
873 : : /* And it better not own any resources, either */
720 heikki.linnakangas@i 874 [ - + ]: 712554 : Assert(owner->narr == 0);
875 [ - + ]: 712554 : Assert(owner->nhash == 0);
4877 876 [ + + - + ]: 712554 : Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
877 : :
878 : : /*
879 : : * Delete children. The recursive call will delink the child from me, so
880 : : * just iterate as long as there is a child.
881 : : */
7773 tgl@sss.pgh.pa.us 882 [ + + ]: 747044 : while (owner->firstchild != NULL)
883 : 34490 : ResourceOwnerDelete(owner->firstchild);
884 : :
885 : : /*
886 : : * We delink the owner from its parent before deleting it, so that if
887 : : * there's an error we won't have deleted/busted owners still attached to
888 : : * the owner tree. Better a leak than a crash.
889 : : */
890 : 712554 : ResourceOwnerNewParent(owner, NULL);
891 : :
892 : : /* And free the object. */
720 heikki.linnakangas@i 893 [ + + ]: 712554 : if (owner->hash)
894 : 4642 : pfree(owner->hash);
7773 tgl@sss.pgh.pa.us 895 : 712554 : pfree(owner);
896 : 712554 : }
897 : :
898 : : /*
899 : : * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
900 : : */
901 : : ResourceOwner
7732 902 : 349726 : ResourceOwnerGetParent(ResourceOwner owner)
903 : : {
904 : 349726 : return owner->parent;
905 : : }
906 : :
907 : : /*
908 : : * Reassign a ResourceOwner to have a new parent
909 : : */
910 : : void
7773 911 : 712591 : ResourceOwnerNewParent(ResourceOwner owner,
912 : : ResourceOwner newparent)
913 : : {
914 : 712591 : ResourceOwner oldparent = owner->parent;
915 : :
916 [ + + ]: 712591 : if (oldparent)
917 : : {
918 [ + + ]: 388933 : if (owner == oldparent->firstchild)
919 : 381013 : oldparent->firstchild = owner->nextchild;
920 : : else
921 : : {
922 : : ResourceOwner child;
923 : :
924 [ + - ]: 9160 : for (child = oldparent->firstchild; child; child = child->nextchild)
925 : : {
926 [ + + ]: 9160 : if (owner == child->nextchild)
927 : : {
928 : 7920 : child->nextchild = owner->nextchild;
929 : 7920 : break;
930 : : }
931 : : }
932 : : }
933 : : }
934 : :
935 [ + + ]: 712591 : if (newparent)
936 : : {
937 [ - + ]: 37 : Assert(owner != newparent);
938 : 37 : owner->parent = newparent;
939 : 37 : owner->nextchild = newparent->firstchild;
940 : 37 : newparent->firstchild = owner;
941 : : }
942 : : else
943 : : {
944 : 712554 : owner->parent = NULL;
945 : 712554 : owner->nextchild = NULL;
946 : : }
947 : 712591 : }
948 : :
949 : : /*
950 : : * Register or deregister callback functions for resource cleanup
951 : : *
952 : : * These functions can be used by dynamically loaded modules. These used
953 : : * to be the only way for an extension to register custom resource types
954 : : * with a resource owner, but nowadays it is easier to define a new
955 : : * ResourceOwnerDesc with custom callbacks.
956 : : */
957 : : void
7773 tgl@sss.pgh.pa.us 958 :UBC 0 : RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
959 : : {
960 : : ResourceReleaseCallbackItem *item;
961 : :
962 : : item = (ResourceReleaseCallbackItem *)
963 : 0 : MemoryContextAlloc(TopMemoryContext,
964 : : sizeof(ResourceReleaseCallbackItem));
965 : 0 : item->callback = callback;
966 : 0 : item->arg = arg;
967 : 0 : item->next = ResourceRelease_callbacks;
968 : 0 : ResourceRelease_callbacks = item;
969 : 0 : }
970 : :
971 : : void
972 : 0 : UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
973 : : {
974 : : ResourceReleaseCallbackItem *item;
975 : : ResourceReleaseCallbackItem *prev;
976 : :
977 : 0 : prev = NULL;
978 [ # # ]: 0 : for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
979 : : {
980 [ # # # # ]: 0 : if (item->callback == callback && item->arg == arg)
981 : : {
982 [ # # ]: 0 : if (prev)
983 : 0 : prev->next = item->next;
984 : : else
985 : 0 : ResourceRelease_callbacks = item->next;
986 : 0 : pfree(item);
987 : 0 : break;
988 : : }
989 : : }
990 : 0 : }
991 : :
992 : : /*
993 : : * Establish an AuxProcessResourceOwner for the current process.
994 : : */
995 : : void
2659 tgl@sss.pgh.pa.us 996 :CBC 5278 : CreateAuxProcessResourceOwner(void)
997 : : {
998 [ - + ]: 5278 : Assert(AuxProcessResourceOwner == NULL);
999 [ - + ]: 5278 : Assert(CurrentResourceOwner == NULL);
1000 : 5278 : AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
1001 : 5278 : CurrentResourceOwner = AuxProcessResourceOwner;
1002 : :
1003 : : /*
1004 : : * Register a shmem-exit callback for cleanup of aux-process resource
1005 : : * owner. (This needs to run after, e.g., ShutdownXLOG.)
1006 : : */
1007 : 5278 : on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
1008 : 5278 : }
1009 : :
1010 : : /*
1011 : : * Convenience routine to release all resources tracked in
1012 : : * AuxProcessResourceOwner (but that resowner is not destroyed here).
1013 : : * Warn about leaked resources if isCommit is true.
1014 : : */
1015 : : void
1016 : 5603 : ReleaseAuxProcessResources(bool isCommit)
1017 : : {
1018 : : /*
1019 : : * At this writing, the only thing that could actually get released is
1020 : : * buffer pins; but we may as well do the full release protocol.
1021 : : */
1022 : 5603 : ResourceOwnerRelease(AuxProcessResourceOwner,
1023 : : RESOURCE_RELEASE_BEFORE_LOCKS,
1024 : : isCommit, true);
1025 : 5603 : ResourceOwnerRelease(AuxProcessResourceOwner,
1026 : : RESOURCE_RELEASE_LOCKS,
1027 : : isCommit, true);
1028 : 5603 : ResourceOwnerRelease(AuxProcessResourceOwner,
1029 : : RESOURCE_RELEASE_AFTER_LOCKS,
1030 : : isCommit, true);
1031 : : /* allow it to be reused */
720 heikki.linnakangas@i 1032 : 5603 : AuxProcessResourceOwner->releasing = false;
1033 : 5603 : AuxProcessResourceOwner->sorted = false;
2659 tgl@sss.pgh.pa.us 1034 : 5603 : }
1035 : :
1036 : : /*
1037 : : * Shmem-exit callback for the same.
1038 : : * Warn about leaked resources if process exit code is zero (ie normal).
1039 : : */
1040 : : static void
1041 : 5278 : ReleaseAuxProcessResourcesCallback(int code, Datum arg)
1042 : : {
1043 : 5278 : bool isCommit = (code == 0);
1044 : :
1045 : 5278 : ReleaseAuxProcessResources(isCommit);
1046 : 5278 : }
1047 : :
1048 : : /*
1049 : : * Remember that a Local Lock is owned by a ResourceOwner
1050 : : *
1051 : : * This is different from the generic ResourceOwnerRemember in that the list of
1052 : : * locks is only a lossy cache. It can hold up to MAX_RESOWNER_LOCKS entries,
1053 : : * and when it overflows, we stop tracking locks. The point of only remembering
1054 : : * only up to MAX_RESOWNER_LOCKS entries is that if a lot of locks are held,
1055 : : * ResourceOwnerForgetLock doesn't need to scan through a large array to find
1056 : : * the entry.
1057 : : */
1058 : : void
4535 bruce@momjian.us 1059 : 17708236 : ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
1060 : : {
3563 tgl@sss.pgh.pa.us 1061 [ - + ]: 17708236 : Assert(locallock != NULL);
1062 : :
4877 heikki.linnakangas@i 1063 [ + + ]: 17708236 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
4535 bruce@momjian.us 1064 : 1935092 : return; /* we have already overflowed */
1065 : :
4877 heikki.linnakangas@i 1066 [ + + ]: 15773144 : if (owner->nlocks < MAX_RESOWNER_LOCKS)
1067 : 15763363 : owner->locks[owner->nlocks] = locallock;
1068 : : else
1069 : : {
1070 : : /* overflowed */
1071 : : }
1072 : 15773144 : owner->nlocks++;
1073 : : }
1074 : :
1075 : : /*
1076 : : * Forget that a Local Lock is owned by a ResourceOwner
1077 : : */
1078 : : void
1079 : 17708236 : ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1080 : : {
1081 : : int i;
1082 : :
1083 [ + + ]: 17708236 : if (owner->nlocks > MAX_RESOWNER_LOCKS)
4535 bruce@momjian.us 1084 : 2091588 : return; /* we have overflowed */
1085 : :
4877 heikki.linnakangas@i 1086 [ - + ]: 15616648 : Assert(owner->nlocks > 0);
1087 [ + - ]: 17703346 : for (i = owner->nlocks - 1; i >= 0; i--)
1088 : : {
1089 [ + + ]: 17703346 : if (locallock == owner->locks[i])
1090 : : {
1091 : 15616648 : owner->locks[i] = owner->locks[owner->nlocks - 1];
1092 : 15616648 : owner->nlocks--;
1093 : 15616648 : return;
1094 : : }
1095 : : }
4877 heikki.linnakangas@i 1096 [ # # ]:UBC 0 : elog(ERROR, "lock reference %p is not owned by resource owner %s",
1097 : : locallock, owner->name);
1098 : : }
1099 : :
1100 : : void
225 andres@anarazel.de 1101 :CBC 1272941 : ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1102 : : {
1103 : 1272941 : dlist_push_tail(&owner->aio_handles, ioh_node);
1104 : 1272941 : }
1105 : :
1106 : : void
1107 : 1272941 : ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1108 : : {
1109 : 1272941 : dlist_delete_from(&owner->aio_handles, ioh_node);
1110 : 1272941 : }
|