static dsa_area *attach_internal(void *place, dsm_segment *segment,
dsa_handle handle);
static void check_for_freed_segments(dsa_area *area);
+static void check_for_freed_segments_locked(dsa_area *area);
/*
* Create a new shared area in a new DSM segment. Further DSM segments will
*/
LWLockAcquire(DSA_AREA_LOCK(area), LW_EXCLUSIVE);
+ check_for_freed_segments_locked(area);
fprintf(stderr, "dsa_area handle %x:\n", area->control->handle);
fprintf(stderr, " max_total_segment_size: %zu\n",
area->control->max_total_segment_size);
(DSA_SEGMENT_HEADER_MAGIC ^ area->control->handle ^ index));
}
+ /*
+ * Callers of dsa_get_address() and dsa_free() don't hold the area lock,
+ * but it's a bug in the calling code and undefined behavior if the
+ * address is not live (ie if the segment might possibly have been freed,
+ * they're trying to use a dangling pointer).
+ *
+ * For dsa.c code that holds the area lock to manipulate segment_bins
+ * lists, it would be a bug if we ever reach a freed segment here. After
+ * it's marked as freed, the only thing any backend should do with it is
+ * unmap it, and it should always have done that in
+ * check_for_freed_segments_locked() before arriving here to resolve an
+ * index to a segment_map.
+ *
+ * Either way we can assert that we aren't returning a freed segment.
+ */
+ Assert(!area->segment_maps[index].header->freed);
+
return &area->segment_maps[index];
}
int size_class = span->size_class;
dsa_segment_map *segment_map;
- segment_map =
- get_segment_by_index(area, DSA_EXTRACT_SEGMENT_NUMBER(span->start));
/* Remove it from its fullness class list. */
unlink_span(area, span);
* could deadlock.
*/
LWLockAcquire(DSA_AREA_LOCK(area), LW_EXCLUSIVE);
+ check_for_freed_segments_locked(area);
+ segment_map =
+ get_segment_by_index(area, DSA_EXTRACT_SEGMENT_NUMBER(span->start));
FreePageManagerPut(segment_map->fpm,
DSA_EXTRACT_OFFSET(span->start) / FPM_PAGE_SIZE,
span->npages);
Size bin;
Assert(LWLockHeldByMe(DSA_AREA_LOCK(area)));
+ check_for_freed_segments_locked(area);
/*
* Start searching from the first bin that *might* have enough contiguous
freed_segment_counter = area->control->freed_segment_counter;
if (unlikely(area->freed_segment_counter != freed_segment_counter))
{
- int i;
-
/* Check all currently mapped segments to find what's been freed. */
LWLockAcquire(DSA_AREA_LOCK(area), LW_EXCLUSIVE);
+ check_for_freed_segments_locked(area);
+ LWLockRelease(DSA_AREA_LOCK(area));
+ }
+}
+
+/*
+ * Workhorse for check_for_free_segments(), and also used directly in path
+ * where the area lock is already held. This should be called after acquiring
+ * the lock but before looking up any segment by index number, to make sure we
+ * unmap any stale segments that might have previously had the same index as a
+ * current segment.
+ */
+static void
+check_for_freed_segments_locked(dsa_area *area)
+{
+ Size freed_segment_counter;
+ int i;
+
+ Assert(LWLockHeldByMe(DSA_AREA_LOCK(area)));
+ freed_segment_counter = area->control->freed_segment_counter;
+ if (unlikely(area->freed_segment_counter != freed_segment_counter))
+ {
for (i = 0; i <= area->high_segment_index; ++i)
{
if (area->segment_maps[i].header != NULL &&
area->segment_maps[i].mapped_address = NULL;
}
}
- LWLockRelease(DSA_AREA_LOCK(area));
area->freed_segment_counter = freed_segment_counter;
}
}