Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/build-gpdb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ jobs:
},
{"test":"ic-mirrorless",
"make_configs":["src/test/isolation2:installcheck-mirrorless"]
},
{"test":"ic-modules",
"make_configs":["src/test/modules:installcheck"]
}
]
}'
Expand Down
121 changes: 113 additions & 8 deletions src/backend/storage/ipc/dsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ typedef struct dsm_control_item
{
dsm_handle handle;
uint32 refcnt; /* 2+ = active, 1 = moribund, 0 = gone */
bool pinned;
} dsm_control_item;

/* Layout of the dynamic shared memory control segment. */
Expand Down Expand Up @@ -452,6 +453,13 @@ dsm_set_control_handle(dsm_handle h)

/*
* Create a new dynamic shared memory segment.
*
* If there is a non-NULL CurrentResourceOwner, the new segment is associated
* with it and must be detached before the resource owner releases, or a
* warning will be logged. If CurrentResourceOwner is NULL, the segment
* remains attached until explicitely detached or the session ends.
* Creating with a NULL CurrentResourceOwner is equivalent to creating
* with a non-NULL CurrentResourceOwner and then calling dsm_pin_mapping.
*/
dsm_segment *
dsm_create(Size size)
Expand Down Expand Up @@ -490,6 +498,7 @@ dsm_create(Size size)
dsm_control->item[i].handle = seg->handle;
/* refcnt of 1 triggers destruction, so start at 2 */
dsm_control->item[i].refcnt = 2;
dsm_control->item[i].pinned = false;
seg->control_slot = i;
LWLockRelease(DynamicSharedMemoryControlLock);
return seg;
Expand All @@ -515,6 +524,7 @@ dsm_create(Size size)
dsm_control->item[nitems].handle = seg->handle;
/* refcnt of 1 triggers destruction, so start at 2 */
dsm_control->item[nitems].refcnt = 2;
dsm_control->item[nitems].pinned = false;
seg->control_slot = nitems;
dsm_control->nitems++;
LWLockRelease(DynamicSharedMemoryControlLock);
Expand All @@ -532,6 +542,11 @@ dsm_create(Size size)
* This can happen if we're asked to attach the segment, but then everyone
* else detaches it (causing it to be destroyed) before we get around to
* attaching it.
*
* If there is a non-NULL CurrentResourceOwner, the attached segment is
* associated with it and must be detached before the resource owner releases,
* or a warning will be logged. Otherwise the segment remains attached until
* explicitely detached or the session ends. See the note atop dsm_create().
*/
dsm_segment *
dsm_attach(dsm_handle h)
Expand Down Expand Up @@ -753,6 +768,9 @@ dsm_detach(dsm_segment *seg)
/* If new reference count is 1, try to destroy the segment. */
if (refcnt == 1)
{
/* A pinned segment should never reach 1. */
Assert(!dsm_control->item[control_slot].pinned);

/*
* If we fail to destroy the segment here, or are killed before we
* finish doing so, the reference count will remain at 1, which
Expand Down Expand Up @@ -805,11 +823,11 @@ dsm_pin_mapping(dsm_segment *seg)
}

/*
* Keep a dynamic shared memory segment until postmaster shutdown.
* Keep a dynamic shared memory segment until postmaster shutdown, or until
* dsm_unpin_segment is called.
*
* This function should not be called more than once per segment;
* on Windows, doing so will create unnecessary handles which will
* consume system resources to no benefit.
* This function should not be called more than once per segment, unless the
* segment is explicitly unpinned with dsm_unpin_segment in between calls.
*
* Note that this function does not arrange for the current process to
* keep the segment mapped indefinitely; if that behavior is desired,
Expand All @@ -822,13 +840,98 @@ dsm_pin_segment(dsm_segment *seg)
/*
* Bump reference count for this segment in shared memory. This will
* ensure that even if there is no session which is attached to this
* segment, it will remain until postmaster shutdown.
* segment, it will remain until postmaster shutdown or an explicit call
* to unpin.
*/
LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
if (dsm_control->item[seg->control_slot].pinned)
elog(ERROR, "cannot pin a segment that is already pinned");
dsm_impl_pin_segment(seg->handle, seg->impl_private);
dsm_control->item[seg->control_slot].pinned = true;
dsm_control->item[seg->control_slot].refcnt++;
LWLockRelease(DynamicSharedMemoryControlLock);
}

dsm_impl_pin_segment(seg->handle, seg->impl_private);
/*
* Unpin a dynamic shared memory segment that was previously pinned with
* dsm_pin_segment. This function should not be called unless dsm_pin_segment
* was previously called for this segment.
*
* The argument is a dsm_handle rather than a dsm_segment in case you want
* to unpin a segment to which you haven't attached. This turns out to be
* useful if, for example, a reference to one shared memory segment is stored
* within another shared memory segment. You might want to unpin the
* referenced segment before destroying the referencing segment.
*/
void
dsm_unpin_segment(dsm_handle handle)
{
uint32 control_slot = INVALID_CONTROL_SLOT;
bool destroy = false;
uint32 i;

/* Find the control slot for the given handle. */
LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
for (i = 0; i < dsm_control->nitems; ++i)
{
/* Skip unused slots and segments that are concurrently going away. */
if (dsm_control->item[i].refcnt <= 1)
continue;

/* If we've found our handle, we can stop searching. */
if (dsm_control->item[i].handle == handle)
{
control_slot = i;
break;
}
}

/*
* We should definitely have found the slot, and it should not already be
* in the process of going away, because this function should only be
* called on a segment which is pinned.
*/
if (control_slot == INVALID_CONTROL_SLOT)
elog(ERROR, "cannot unpin unknown segment handle");
if (!dsm_control->item[control_slot].pinned)
elog(ERROR, "cannot unpin a segment that is not pinned");
Assert(dsm_control->item[control_slot].refcnt > 1);

/* Note that 1 means no references (0 means unused slot). */
if (--dsm_control->item[control_slot].refcnt == 1)
destroy = true;
dsm_control->item[control_slot].pinned = false;

/* Now we can release the lock. */
LWLockRelease(DynamicSharedMemoryControlLock);

/* Clean up resources if that was the last reference. */
if (destroy)
{
void *junk_impl_private = NULL;
void *junk_mapped_address = NULL;
Size junk_mapped_size = 0;

/*
* For an explanation of how error handling works in this case, see
* comments in dsm_detach. Note that if we reach this point, the
* current process certainly does not have the segment mapped, because
* if it did, the reference count would have still been greater than 1
* even after releasing the reference count held by the pin. The fact
* that there can't be a dsm_segment for this handle makes it OK to
* pass the mapped size, mapped address, and private data as NULL
* here.
*/
if (dsm_impl_op(DSM_OP_DESTROY, handle, 0, &junk_impl_private,
&junk_mapped_address, &junk_mapped_size, WARNING))
{
LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE);
Assert(dsm_control->item[control_slot].handle == handle);
Assert(dsm_control->item[control_slot].refcnt == 1);
dsm_control->item[control_slot].refcnt = 0;
LWLockRelease(DynamicSharedMemoryControlLock);
}
}
}

/*
Expand Down Expand Up @@ -964,7 +1067,8 @@ dsm_create_descriptor(void)
{
dsm_segment *seg;

ResourceOwnerEnlargeDSMs(CurrentResourceOwner);
if (CurrentResourceOwner)
ResourceOwnerEnlargeDSMs(CurrentResourceOwner);

seg = MemoryContextAlloc(TopMemoryContext, sizeof(dsm_segment));
dlist_push_head(&dsm_segment_list, &seg->node);
Expand All @@ -976,7 +1080,8 @@ dsm_create_descriptor(void)
seg->mapped_size = 0;

seg->resowner = CurrentResourceOwner;
ResourceOwnerRememberDSM(CurrentResourceOwner, seg);
if (CurrentResourceOwner)
ResourceOwnerRememberDSM(CurrentResourceOwner, seg);

slist_init(&seg->on_detach);

Expand Down
4 changes: 2 additions & 2 deletions src/backend/storage/ipc/dsm_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1055,8 +1055,8 @@ dsm_impl_mmap(dsm_op op, dsm_handle handle, Size request_size,
#endif

/*
* Implementation-specific actions that must be performed when a segment
* is to be preserved until postmaster shutdown.
* Implementation-specific actions that must be performed when a segment is to
* be preserved even when no backend has it attached.
*
* Except on Windows, we don't need to do anything at all. But since Windows
* cleans up segments automatically when no references remain, we duplicate
Expand Down
2 changes: 1 addition & 1 deletion src/backend/utils/mmgr/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ subdir = src/backend/utils/mmgr
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global

OBJS = aset.o mcxt.o memaccounting.o mpool.o portalmem.o memprot.o vmem_tracker.o redzone_handler.o runaway_cleaner.o idle_tracker.o event_version.o
OBJS = aset.o dsa.o freepage.o mcxt.o memaccounting.o mpool.o portalmem.o memprot.o vmem_tracker.o redzone_handler.o runaway_cleaner.o idle_tracker.o event_version.o

# In PostgreSQL, this is under src/common. It has been backported, but because
# we haven't merged the changes that introduced the src/common directory, it
Expand Down
Loading
Loading