@@ -82,6 +82,8 @@ typedef struct dsm_control_item
8282{
8383 dsm_handle handle ;
8484 uint32 refcnt ; /* 2+ = active, 1 = moribund, 0 = gone */
85+ void * impl_private_pm_handle ; /* only needed on Windows */
86+ bool pinned ;
8587} dsm_control_item ;
8688
8789/* Layout of the dynamic shared memory control segment. */
@@ -491,6 +493,8 @@ dsm_create(Size size, int flags)
491493 dsm_control -> item [i ].handle = seg -> handle ;
492494 /* refcnt of 1 triggers destruction, so start at 2 */
493495 dsm_control -> item [i ].refcnt = 2 ;
496+ dsm_control -> item [i ].impl_private_pm_handle = NULL ;
497+ dsm_control -> item [i ].pinned = false;
494498 seg -> control_slot = i ;
495499 LWLockRelease (DynamicSharedMemoryControlLock );
496500 return seg ;
@@ -520,6 +524,8 @@ dsm_create(Size size, int flags)
520524 dsm_control -> item [nitems ].handle = seg -> handle ;
521525 /* refcnt of 1 triggers destruction, so start at 2 */
522526 dsm_control -> item [nitems ].refcnt = 2 ;
527+ dsm_control -> item [nitems ].impl_private_pm_handle = NULL ;
528+ dsm_control -> item [nitems ].pinned = false;
523529 seg -> control_slot = nitems ;
524530 dsm_control -> nitems ++ ;
525531 LWLockRelease (DynamicSharedMemoryControlLock );
@@ -760,6 +766,9 @@ dsm_detach(dsm_segment *seg)
760766 /* If new reference count is 1, try to destroy the segment. */
761767 if (refcnt == 1 )
762768 {
769+ /* A pinned segment should never reach 1. */
770+ Assert (!dsm_control -> item [control_slot ].pinned );
771+
763772 /*
764773 * If we fail to destroy the segment here, or are killed before we
765774 * finish doing so, the reference count will remain at 1, which
@@ -830,11 +839,11 @@ dsm_unpin_mapping(dsm_segment *seg)
830839}
831840
832841/*
833- * Keep a dynamic shared memory segment until postmaster shutdown.
842+ * Keep a dynamic shared memory segment until postmaster shutdown, or until
843+ * dsm_unpin_segment is called.
834844 *
835- * This function should not be called more than once per segment;
836- * on Windows, doing so will create unnecessary handles which will
837- * consume system resources to no benefit.
845+ * This function should not be called more than once per segment, unless the
846+ * segment is explicitly unpinned with dsm_unpin_segment in between calls.
838847 *
839848 * Note that this function does not arrange for the current process to
840849 * keep the segment mapped indefinitely; if that behavior is desired,
@@ -844,16 +853,112 @@ dsm_unpin_mapping(dsm_segment *seg)
844853void
845854dsm_pin_segment (dsm_segment * seg )
846855{
856+ void * handle ;
857+
847858 /*
848859 * Bump reference count for this segment in shared memory. This will
849860 * ensure that even if there is no session which is attached to this
850- * segment, it will remain until postmaster shutdown.
861+ * segment, it will remain until postmaster shutdown or an explicit call
862+ * to unpin.
851863 */
852864 LWLockAcquire (DynamicSharedMemoryControlLock , LW_EXCLUSIVE );
865+ if (dsm_control -> item [seg -> control_slot ].pinned )
866+ elog (ERROR , "cannot pin a segment that is already pinned" );
867+ dsm_impl_pin_segment (seg -> handle , seg -> impl_private , & handle );
868+ dsm_control -> item [seg -> control_slot ].pinned = true;
853869 dsm_control -> item [seg -> control_slot ].refcnt ++ ;
870+ dsm_control -> item [seg -> control_slot ].impl_private_pm_handle = handle ;
854871 LWLockRelease (DynamicSharedMemoryControlLock );
872+ }
873+
874+ /*
875+ * Unpin a dynamic shared memory segment that was previously pinned with
876+ * dsm_pin_segment. This function should not be called unless dsm_pin_segment
877+ * was previously called for this segment.
878+ *
879+ * The argument is a dsm_handle rather than a dsm_segment in case you want
880+ * to unpin a segment to which you haven't attached. This turns out to be
881+ * useful if, for example, a reference to one shared memory segment is stored
882+ * within another shared memory segment. You might want to unpin the
883+ * referenced segment before destroying the referencing segment.
884+ */
885+ void
886+ dsm_unpin_segment (dsm_handle handle )
887+ {
888+ uint32 control_slot = INVALID_CONTROL_SLOT ;
889+ bool destroy = false;
890+ uint32 i ;
855891
856- dsm_impl_pin_segment (seg -> handle , seg -> impl_private );
892+ /* Find the control slot for the given handle. */
893+ LWLockAcquire (DynamicSharedMemoryControlLock , LW_EXCLUSIVE );
894+ for (i = 0 ; i < dsm_control -> nitems ; ++ i )
895+ {
896+ /* Skip unused slots. */
897+ if (dsm_control -> item [i ].refcnt == 0 )
898+ continue ;
899+
900+ /* If we've found our handle, we can stop searching. */
901+ if (dsm_control -> item [i ].handle == handle )
902+ {
903+ control_slot = i ;
904+ break ;
905+ }
906+ }
907+
908+ /*
909+ * We should definitely have found the slot, and it should not already be
910+ * in the process of going away, because this function should only be
911+ * called on a segment which is pinned.
912+ */
913+ if (control_slot == INVALID_CONTROL_SLOT )
914+ elog (ERROR , "cannot unpin unknown segment handle" );
915+ if (!dsm_control -> item [control_slot ].pinned )
916+ elog (ERROR , "cannot unpin a segment that is not pinned" );
917+ Assert (dsm_control -> item [control_slot ].refcnt > 1 );
918+
919+ /*
920+ * Allow implementation-specific code to run. We have to do this before
921+ * releasing the lock, because impl_private_pm_handle may get modified by
922+ * dsm_impl_unpin_segment.
923+ */
924+ dsm_impl_unpin_segment (handle ,
925+ & dsm_control -> item [control_slot ].impl_private_pm_handle );
926+
927+ /* Note that 1 means no references (0 means unused slot). */
928+ if (-- dsm_control -> item [control_slot ].refcnt == 1 )
929+ destroy = true;
930+ dsm_control -> item [control_slot ].pinned = false;
931+
932+ /* Now we can release the lock. */
933+ LWLockRelease (DynamicSharedMemoryControlLock );
934+
935+ /* Clean up resources if that was the last reference. */
936+ if (destroy )
937+ {
938+ void * junk_impl_private = NULL ;
939+ void * junk_mapped_address = NULL ;
940+ Size junk_mapped_size = 0 ;
941+
942+ /*
943+ * For an explanation of how error handling works in this case, see
944+ * comments in dsm_detach. Note that if we reach this point, the
945+ * current process certainly does not have the segment mapped, because
946+ * if it did, the reference count would have still been greater than 1
947+ * even after releasing the reference count held by the pin. The fact
948+ * that there can't be a dsm_segment for this handle makes it OK to
949+ * pass the mapped size, mapped address, and private data as NULL
950+ * here.
951+ */
952+ if (dsm_impl_op (DSM_OP_DESTROY , handle , 0 , & junk_impl_private ,
953+ & junk_mapped_address , & junk_mapped_size , WARNING ))
954+ {
955+ LWLockAcquire (DynamicSharedMemoryControlLock , LW_EXCLUSIVE );
956+ Assert (dsm_control -> item [control_slot ].handle == handle );
957+ Assert (dsm_control -> item [control_slot ].refcnt == 1 );
958+ dsm_control -> item [control_slot ].refcnt = 0 ;
959+ LWLockRelease (DynamicSharedMemoryControlLock );
960+ }
961+ }
857962}
858963
859964/*
0 commit comments