Coverage Report

Created: 2026-09-28 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/resowner/resowner.c
Line
Count
Source
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
0
#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
0
#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
0
  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
0
#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
222
hash_resource_elem(Datum value, const ResourceOwnerDesc *kind)
223
0
{
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
   */
234
0
  return hash_combine64(murmurhash64((uint64) value),
235
0
              (uint64) (uintptr_t) kind);
236
0
}
237
238
/*
239
 * Adds 'value' of given 'kind' to the ResourceOwner's hash table
240
 */
241
static void
242
ResourceOwnerAddToHash(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
243
0
{
244
0
  uint32    mask = owner->capacity - 1;
245
0
  uint32    idx;
246
247
0
  Assert(kind != NULL);
248
249
  /* Insert into first free slot at or after hash location. */
250
0
  idx = hash_resource_elem(value, kind) & mask;
251
0
  for (;;)
252
0
  {
253
0
    if (owner->hash[idx].kind == NULL)
254
0
      break;       /* found a free slot */
255
0
    idx = (idx + 1) & mask;
256
0
  }
257
0
  owner->hash[idx].item = value;
258
0
  owner->hash[idx].kind = kind;
259
0
  owner->nhash++;
260
0
}
261
262
/*
263
 * Comparison function to sort by release phase and priority
264
 */
265
static int
266
resource_priority_cmp(const void *a, const void *b)
267
0
{
268
0
  const ResourceElem *ra = (const ResourceElem *) a;
269
0
  const ResourceElem *rb = (const ResourceElem *) b;
270
271
  /* Note: reverse order */
272
0
  if (ra->kind->release_phase == rb->kind->release_phase)
273
0
    return pg_cmp_u32(rb->kind->release_priority, ra->kind->release_priority);
274
0
  else if (ra->kind->release_phase > rb->kind->release_phase)
275
0
    return -1;
276
0
  else
277
0
    return 1;
278
0
}
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
ResourceOwnerSort(ResourceOwner owner)
290
0
{
291
0
  ResourceElem *items;
292
0
  uint32    nitems;
293
294
0
  if (owner->nhash == 0)
295
0
  {
296
0
    items = owner->arr;
297
0
    nitems = owner->narr;
298
0
  }
299
0
  else
300
0
  {
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
0
    uint32    dst = 0;
306
307
0
    for (uint32 idx = 0; idx < owner->capacity; idx++)
308
0
    {
309
0
      if (owner->hash[idx].kind != NULL)
310
0
      {
311
0
        if (dst != idx)
312
0
          owner->hash[dst] = owner->hash[idx];
313
0
        dst++;
314
0
      }
315
0
    }
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
0
    Assert(dst + owner->narr <= owner->capacity);
325
0
    for (int idx = 0; idx < owner->narr; idx++)
326
0
    {
327
0
      owner->hash[dst] = owner->arr[idx];
328
0
      dst++;
329
0
    }
330
0
    Assert(dst == owner->nhash + owner->narr);
331
0
    owner->narr = 0;
332
0
    owner->nhash = dst;
333
334
0
    items = owner->hash;
335
0
    nitems = owner->nhash;
336
0
  }
337
338
0
  qsort(items, nitems, sizeof(ResourceElem), resource_priority_cmp);
339
0
}
340
341
/*
342
 * Call the ReleaseResource callback on entries with given 'phase'.
343
 */
344
static void
345
ResourceOwnerReleaseAll(ResourceOwner owner, ResourceReleasePhase phase,
346
            bool printLeakWarnings)
347
0
{
348
0
  ResourceElem *items;
349
0
  uint32    nitems;
350
0
  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
0
  Assert(owner->releasing);
357
0
  Assert(owner->sorted);
358
0
  if (owner->nhash == 0)
359
0
  {
360
0
    items = owner->arr;
361
0
    nitems = owner->narr;
362
0
    using_arr = true;
363
0
  }
364
0
  else
365
0
  {
366
0
    Assert(owner->narr == 0);
367
0
    items = owner->hash;
368
0
    nitems = owner->nhash;
369
0
    using_arr = false;
370
0
  }
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
   */
378
0
  while (nitems > 0)
379
0
  {
380
0
    uint32    idx = nitems - 1;
381
0
    Datum   value = items[idx].item;
382
0
    const ResourceOwnerDesc *kind = items[idx].kind;
383
384
0
    if (kind->release_phase > phase)
385
0
      break;
386
0
    Assert(kind->release_phase == phase);
387
388
0
    if (printLeakWarnings)
389
0
    {
390
0
      char     *res_str;
391
392
0
      res_str = kind->DebugPrint ?
393
0
        kind->DebugPrint(value)
394
0
        : psprintf("%s %p", kind->name, DatumGetPointer(value));
395
0
      elog(WARNING, "resource was not closed: %s", res_str);
396
0
      pfree(res_str);
397
0
    }
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
0
    nitems--;
405
0
    if (using_arr)
406
0
      owner->narr = nitems;
407
0
    else
408
0
      owner->nhash = nitems;
409
410
0
    kind->ReleaseResource(value);
411
0
  }
412
0
}
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
428
ResourceOwnerCreate(ResourceOwner parent, const char *name)
429
0
{
430
0
  ResourceOwner owner;
431
432
0
  owner = (ResourceOwner) MemoryContextAllocZero(TopMemoryContext,
433
0
                           sizeof(struct ResourceOwnerData));
434
0
  owner->name = name;
435
436
0
  if (parent)
437
0
  {
438
0
    owner->parent = parent;
439
0
    owner->nextchild = parent->firstchild;
440
0
    parent->firstchild = owner;
441
0
  }
442
443
0
  dlist_init(&owner->aio_handles);
444
445
0
  return owner;
446
0
}
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
459
ResourceOwnerEnlarge(ResourceOwner owner)
460
0
{
461
  /*
462
   * Mustn't try to remember more resources after we have already started
463
   * releasing
464
   */
465
0
  if (owner->releasing)
466
0
    elog(ERROR, "ResourceOwnerEnlarge called after release started");
467
468
0
  if (owner->narr < RESOWNER_ARRAY_SIZE)
469
0
    return;         /* no work needed */
470
471
  /*
472
   * Is there space in the hash? If not, enlarge it.
473
   */
474
0
  if (owner->narr + owner->nhash >= owner->grow_at)
475
0
  {
476
0
    uint32    i,
477
0
          oldcap,
478
0
          newcap;
479
0
    ResourceElem *oldhash;
480
0
    ResourceElem *newhash;
481
482
0
    oldhash = owner->hash;
483
0
    oldcap = owner->capacity;
484
485
    /* Double the capacity (it must stay a power of 2!) */
486
0
    newcap = (oldcap > 0) ? oldcap * 2 : RESOWNER_HASH_INIT_SIZE;
487
0
    newhash = (ResourceElem *) MemoryContextAllocZero(TopMemoryContext,
488
0
                              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
0
    owner->hash = newhash;
495
0
    owner->capacity = newcap;
496
0
    owner->grow_at = RESOWNER_HASH_MAX_ITEMS(newcap);
497
0
    owner->nhash = 0;
498
499
0
    if (oldhash != NULL)
500
0
    {
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
0
      for (i = 0; i < oldcap; i++)
507
0
      {
508
0
        if (oldhash[i].kind != NULL)
509
0
          ResourceOwnerAddToHash(owner, oldhash[i].item, oldhash[i].kind);
510
0
      }
511
512
      /* And release old hash table. */
513
0
      pfree(oldhash);
514
0
    }
515
0
  }
516
517
  /* Move items from the array to the hash */
518
0
  for (int i = 0; i < owner->narr; i++)
519
0
    ResourceOwnerAddToHash(owner, owner->arr[i].item, owner->arr[i].kind);
520
0
  owner->narr = 0;
521
522
0
  Assert(owner->nhash <= owner->grow_at);
523
0
}
524
525
/*
526
 * Remember that an object is owned by a ResourceOwner
527
 *
528
 * Caller must have previously done ResourceOwnerEnlarge()
529
 */
530
void
531
ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
532
0
{
533
0
  uint32    idx;
534
535
  /* sanity check the ResourceOwnerDesc */
536
0
  Assert(kind->release_phase != 0);
537
0
  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
0
  Assert(!owner->releasing);
544
0
  Assert(!owner->sorted);
545
546
0
  if (owner->narr >= RESOWNER_ARRAY_SIZE)
547
0
  {
548
    /* forgot to call ResourceOwnerEnlarge? */
549
0
    elog(ERROR, "ResourceOwnerRemember called but array was full");
550
0
  }
551
552
  /* Append to the array. */
553
0
  idx = owner->narr;
554
0
  owner->arr[idx].item = value;
555
0
  owner->arr[idx].kind = kind;
556
0
  owner->narr++;
557
0
}
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
ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
572
0
{
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
0
  if (owner->releasing)
579
0
    elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
580
0
  Assert(!owner->sorted);
581
582
  /* Search through all items in the array first. */
583
0
  for (int i = owner->narr - 1; i >= 0; i--)
584
0
  {
585
0
    if (owner->arr[i].item == value &&
586
0
      owner->arr[i].kind == kind)
587
0
    {
588
0
      owner->arr[i] = owner->arr[owner->narr - 1];
589
0
      owner->narr--;
590
591
#ifdef RESOWNER_STATS
592
      narray_lookups++;
593
#endif
594
0
      return;
595
0
    }
596
0
  }
597
598
  /* Search hash */
599
0
  if (owner->nhash > 0)
600
0
  {
601
0
    uint32    mask = owner->capacity - 1;
602
0
    uint32    idx;
603
604
0
    idx = hash_resource_elem(value, kind) & mask;
605
0
    for (uint32 i = 0; i < owner->capacity; i++)
606
0
    {
607
0
      if (owner->hash[idx].item == value &&
608
0
        owner->hash[idx].kind == kind)
609
0
      {
610
0
        owner->hash[idx].item = (Datum) 0;
611
0
        owner->hash[idx].kind = NULL;
612
0
        owner->nhash--;
613
614
#ifdef RESOWNER_STATS
615
        nhash_lookups++;
616
#endif
617
0
        return;
618
0
      }
619
0
      idx = (idx + 1) & mask;
620
0
    }
621
0
  }
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
   */
628
0
  elog(ERROR, "%s %p is not owned by resource owner %s",
629
0
     kind->name, DatumGetPointer(value), owner->name);
630
0
}
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
665
ResourceOwnerRelease(ResourceOwner owner,
666
           ResourceReleasePhase phase,
667
           bool isCommit,
668
           bool isTopLevel)
669
0
{
670
  /* There's not currently any setup needed before recursing */
671
0
  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
682
0
}
683
684
static void
685
ResourceOwnerReleaseInternal(ResourceOwner owner,
686
               ResourceReleasePhase phase,
687
               bool isCommit,
688
               bool isTopLevel)
689
0
{
690
0
  ResourceOwner child;
691
0
  ResourceOwner save;
692
0
  ResourceReleaseCallbackItem *item;
693
0
  ResourceReleaseCallbackItem *next;
694
695
  /* Recurse to handle descendants */
696
0
  for (child = owner->firstchild; child != NULL; child = child->nextchild)
697
0
    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
   */
707
0
  if (!owner->releasing)
708
0
  {
709
0
    Assert(phase == RESOURCE_RELEASE_BEFORE_LOCKS);
710
0
    Assert(!owner->sorted);
711
0
    owner->releasing = true;
712
0
  }
713
0
  else
714
0
  {
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
0
  }
722
0
  if (!owner->sorted)
723
0
  {
724
0
    ResourceOwnerSort(owner);
725
0
    owner->sorted = true;
726
0
  }
727
728
  /*
729
   * Make CurrentResourceOwner point to me, so that the release callback
730
   * functions know which resource owner is been released.
731
   */
732
0
  save = CurrentResourceOwner;
733
0
  CurrentResourceOwner = owner;
734
735
0
  if (phase == RESOURCE_RELEASE_BEFORE_LOCKS)
736
0
  {
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
0
    ResourceOwnerReleaseAll(owner, phase, isCommit);
745
746
0
    while (!dlist_is_empty(&owner->aio_handles))
747
0
    {
748
0
      dlist_node *node = dlist_head_node(&owner->aio_handles);
749
750
0
      pgaio_io_release_resowner(node, !isCommit);
751
0
    }
752
0
  }
753
0
  else if (phase == RESOURCE_RELEASE_LOCKS)
754
0
  {
755
0
    if (isTopLevel)
756
0
    {
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
0
      if (owner == TopTransactionResourceOwner)
763
0
      {
764
0
        ProcReleaseLocks(isCommit);
765
0
        ReleasePredicateLocks(isCommit, false);
766
0
      }
767
0
    }
768
0
    else
769
0
    {
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
0
      LOCALLOCK **locks;
776
0
      int     nlocks;
777
778
0
      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
       */
784
0
      if (owner->nlocks > MAX_RESOWNER_LOCKS)
785
0
      {
786
0
        locks = NULL;
787
0
        nlocks = 0;
788
0
      }
789
0
      else
790
0
      {
791
0
        locks = owner->locks;
792
0
        nlocks = owner->nlocks;
793
0
      }
794
795
0
      if (isCommit)
796
0
        LockReassignCurrentOwner(locks, nlocks);
797
0
      else
798
0
        LockReleaseCurrentOwner(locks, nlocks);
799
0
    }
800
0
  }
801
0
  else if (phase == RESOURCE_RELEASE_AFTER_LOCKS)
802
0
  {
803
    /*
804
     * Release all resources that need to be released after the locks.
805
     */
806
0
    ResourceOwnerReleaseAll(owner, phase, isCommit);
807
0
  }
808
809
  /* Let add-on modules get a chance too */
810
0
  for (item = ResourceRelease_callbacks; item; item = next)
811
0
  {
812
    /* allow callbacks to unregister themselves when called */
813
0
    next = item->next;
814
0
    item->callback(phase, isCommit, isTopLevel, item->arg);
815
0
  }
816
817
0
  CurrentResourceOwner = save;
818
0
}
819
820
/*
821
 * ResourceOwnerReleaseAllOfKind
822
 *    Release all resources of a certain type held by this owner.
823
 */
824
void
825
ResourceOwnerReleaseAllOfKind(ResourceOwner owner, const ResourceOwnerDesc *kind)
826
0
{
827
  /* Mustn't call this after we have already started releasing resources. */
828
0
  if (owner->releasing)
829
0
    elog(ERROR, "ResourceOwnerForget called for %s after release started", kind->name);
830
0
  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
0
  owner->releasing = true;
838
839
  /* Array first */
840
0
  for (int i = 0; i < owner->narr; i++)
841
0
  {
842
0
    if (owner->arr[i].kind == kind)
843
0
    {
844
0
      Datum   value = owner->arr[i].item;
845
846
0
      owner->arr[i] = owner->arr[owner->narr - 1];
847
0
      owner->narr--;
848
0
      i--;
849
850
0
      kind->ReleaseResource(value);
851
0
    }
852
0
  }
853
854
  /* Then hash */
855
0
  for (uint32 i = 0; i < owner->capacity; i++)
856
0
  {
857
0
    if (owner->hash[i].kind == kind)
858
0
    {
859
0
      Datum   value = owner->hash[i].item;
860
861
0
      owner->hash[i].item = (Datum) 0;
862
0
      owner->hash[i].kind = NULL;
863
0
      owner->nhash--;
864
865
0
      kind->ReleaseResource(value);
866
0
    }
867
0
  }
868
0
  owner->releasing = false;
869
0
}
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
878
ResourceOwnerDelete(ResourceOwner owner)
879
0
{
880
  /* We had better not be deleting CurrentResourceOwner ... */
881
0
  Assert(owner != CurrentResourceOwner);
882
883
  /* And it better not own any resources, either */
884
0
  Assert(owner->narr == 0);
885
0
  Assert(owner->nhash == 0);
886
0
  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
   */
892
0
  while (owner->firstchild != NULL)
893
0
    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
0
  ResourceOwnerNewParent(owner, NULL);
901
902
  /* And free the object. */
903
0
  if (owner->hash)
904
0
    pfree(owner->hash);
905
0
  pfree(owner);
906
0
}
907
908
/*
909
 * Fetch parent of a ResourceOwner (returns NULL if top-level owner)
910
 */
911
ResourceOwner
912
ResourceOwnerGetParent(ResourceOwner owner)
913
0
{
914
0
  return owner->parent;
915
0
}
916
917
/*
918
 * Reassign a ResourceOwner to have a new parent
919
 */
920
void
921
ResourceOwnerNewParent(ResourceOwner owner,
922
             ResourceOwner newparent)
923
0
{
924
0
  ResourceOwner oldparent = owner->parent;
925
926
0
  if (oldparent)
927
0
  {
928
0
    if (owner == oldparent->firstchild)
929
0
      oldparent->firstchild = owner->nextchild;
930
0
    else
931
0
    {
932
0
      ResourceOwner child;
933
934
0
      for (child = oldparent->firstchild; child; child = child->nextchild)
935
0
      {
936
0
        if (owner == child->nextchild)
937
0
        {
938
0
          child->nextchild = owner->nextchild;
939
0
          break;
940
0
        }
941
0
      }
942
0
    }
943
0
  }
944
945
0
  if (newparent)
946
0
  {
947
0
    Assert(owner != newparent);
948
0
    owner->parent = newparent;
949
0
    owner->nextchild = newparent->firstchild;
950
0
    newparent->firstchild = owner;
951
0
  }
952
0
  else
953
0
  {
954
0
    owner->parent = NULL;
955
0
    owner->nextchild = NULL;
956
0
  }
957
0
}
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
968
RegisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
969
0
{
970
0
  ResourceReleaseCallbackItem *item;
971
972
0
  item = (ResourceReleaseCallbackItem *)
973
0
    MemoryContextAlloc(TopMemoryContext,
974
0
               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
UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
983
0
{
984
0
  ResourceReleaseCallbackItem *item;
985
0
  ResourceReleaseCallbackItem *prev;
986
987
0
  prev = NULL;
988
0
  for (item = ResourceRelease_callbacks; item; prev = item, item = item->next)
989
0
  {
990
0
    if (item->callback == callback && item->arg == arg)
991
0
    {
992
0
      if (prev)
993
0
        prev->next = item->next;
994
0
      else
995
0
        ResourceRelease_callbacks = item->next;
996
0
      pfree(item);
997
0
      break;
998
0
    }
999
0
  }
1000
0
}
1001
1002
/*
1003
 * Establish an AuxProcessResourceOwner for the current process.
1004
 */
1005
void
1006
CreateAuxProcessResourceOwner(void)
1007
0
{
1008
0
  Assert(AuxProcessResourceOwner == NULL);
1009
0
  Assert(CurrentResourceOwner == NULL);
1010
0
  AuxProcessResourceOwner = ResourceOwnerCreate(NULL, "AuxiliaryProcess");
1011
0
  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
0
  on_shmem_exit(ReleaseAuxProcessResourcesCallback, 0);
1018
0
}
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
ReleaseAuxProcessResources(bool isCommit)
1027
0
{
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
0
  ResourceOwnerRelease(AuxProcessResourceOwner,
1033
0
             RESOURCE_RELEASE_BEFORE_LOCKS,
1034
0
             isCommit, true);
1035
0
  ResourceOwnerRelease(AuxProcessResourceOwner,
1036
0
             RESOURCE_RELEASE_LOCKS,
1037
0
             isCommit, true);
1038
0
  ResourceOwnerRelease(AuxProcessResourceOwner,
1039
0
             RESOURCE_RELEASE_AFTER_LOCKS,
1040
0
             isCommit, true);
1041
  /* allow it to be reused */
1042
0
  AuxProcessResourceOwner->releasing = false;
1043
0
  AuxProcessResourceOwner->sorted = false;
1044
0
}
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
ReleaseAuxProcessResourcesCallback(int code, Datum arg)
1052
0
{
1053
0
  bool    isCommit = (code == 0);
1054
1055
0
  ReleaseAuxProcessResources(isCommit);
1056
0
}
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
1069
ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
1070
0
{
1071
0
  Assert(locallock != NULL);
1072
1073
0
  if (owner->nlocks > MAX_RESOWNER_LOCKS)
1074
0
    return;         /* we have already overflowed */
1075
1076
0
  if (owner->nlocks < MAX_RESOWNER_LOCKS)
1077
0
    owner->locks[owner->nlocks] = locallock;
1078
0
  else
1079
0
  {
1080
    /* overflowed */
1081
0
  }
1082
0
  owner->nlocks++;
1083
0
}
1084
1085
/*
1086
 * Forget that a Local Lock is owned by a ResourceOwner
1087
 */
1088
void
1089
ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
1090
0
{
1091
0
  int     i;
1092
1093
0
  if (owner->nlocks > MAX_RESOWNER_LOCKS)
1094
0
    return;         /* we have overflowed */
1095
1096
0
  Assert(owner->nlocks > 0);
1097
0
  for (i = owner->nlocks - 1; i >= 0; i--)
1098
0
  {
1099
0
    if (locallock == owner->locks[i])
1100
0
    {
1101
0
      owner->locks[i] = owner->locks[owner->nlocks - 1];
1102
0
      owner->nlocks--;
1103
0
      return;
1104
0
    }
1105
0
  }
1106
0
  elog(ERROR, "lock reference %p is not owned by resource owner %s",
1107
0
     locallock, owner->name);
1108
0
}
1109
1110
void
1111
ResourceOwnerRememberAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1112
0
{
1113
0
  dlist_push_tail(&owner->aio_handles, ioh_node);
1114
0
}
1115
1116
void
1117
ResourceOwnerForgetAioHandle(ResourceOwner owner, struct dlist_node *ioh_node)
1118
0
{
1119
0
  dlist_delete_from(&owner->aio_handles, ioh_node);
1120
0
}