Skip to content

Commit e5271fe

Browse files
authored
[scudo] Update secondary cache time-based release logic (#107507)
Secondary cache entries are now released to the OS from least recent to most recent entries. This helps to avoid unnecessary scans of the cache since entries ready to be released (specifically, entries that are considered old relative to the configurable release interval) will always be at the tail of the list of committed entries by the LRU ordering. For this same reason, the `OldestTime` variable is no longer needed to indicate when releases are necessary so it has been removed.
1 parent 4b4dbaa commit e5271fe

File tree

1 file changed

+31
-21
lines changed

1 file changed

+31
-21
lines changed

compiler-rt/lib/scudo/standalone/secondary.h

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ class MapAllocatorCache {
247247
// The cache is initially empty
248248
LRUHead = CachedBlock::InvalidEntry;
249249
LRUTail = CachedBlock::InvalidEntry;
250+
LastUnreleasedEntry = CachedBlock::InvalidEntry;
250251

251252
// Available entries will be retrieved starting from the beginning of the
252253
// Entries array
@@ -321,9 +322,10 @@ class MapAllocatorCache {
321322
}
322323
CachedBlock PrevEntry = Quarantine[QuarantinePos];
323324
Quarantine[QuarantinePos] = Entry;
324-
if (OldestTime == 0)
325-
OldestTime = Entry.Time;
326325
Entry = PrevEntry;
326+
// Update the entry time to reflect the time that the
327+
// quarantined memory is placed in the Entries array
328+
Entry.Time = Time;
327329
}
328330

329331
// All excess entries are evicted from the cache
@@ -334,9 +336,6 @@ class MapAllocatorCache {
334336
}
335337

336338
insert(Entry);
337-
338-
if (OldestTime == 0)
339-
OldestTime = Entry.Time;
340339
} while (0);
341340

342341
for (MemMapT &EvictMemMap : EvictionMemMaps)
@@ -535,6 +534,9 @@ class MapAllocatorCache {
535534
Entries[LRUHead].Prev = static_cast<u16>(FreeIndex);
536535
}
537536

537+
if (LastUnreleasedEntry == CachedBlock::InvalidEntry)
538+
LastUnreleasedEntry = static_cast<u16>(FreeIndex);
539+
538540
Entries[FreeIndex] = Entry;
539541
Entries[FreeIndex].Next = LRUHead;
540542
Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
@@ -552,6 +554,9 @@ class MapAllocatorCache {
552554

553555
Entries[I].invalidate();
554556

557+
if (I == LastUnreleasedEntry)
558+
LastUnreleasedEntry = Entries[LastUnreleasedEntry].Prev;
559+
555560
if (I == LRUHead)
556561
LRUHead = Entries[I].Next;
557562
else
@@ -593,35 +598,37 @@ class MapAllocatorCache {
593598
}
594599
}
595600

596-
void releaseIfOlderThan(CachedBlock &Entry, u64 Time) REQUIRES(Mutex) {
597-
if (!Entry.isValid() || !Entry.Time)
598-
return;
599-
if (Entry.Time > Time) {
600-
if (OldestTime == 0 || Entry.Time < OldestTime)
601-
OldestTime = Entry.Time;
602-
return;
603-
}
601+
inline void release(CachedBlock &Entry) {
602+
DCHECK(Entry.Time != 0);
604603
Entry.MemMap.releaseAndZeroPagesToOS(Entry.CommitBase, Entry.CommitSize);
605604
Entry.Time = 0;
606605
}
607606

608607
void releaseOlderThan(u64 Time) EXCLUDES(Mutex) {
609608
ScopedLock L(Mutex);
610-
if (!EntriesCount || OldestTime == 0 || OldestTime > Time)
609+
if (!EntriesCount)
611610
return;
612-
OldestTime = 0;
613-
for (uptr I = 0; I < Config::getQuarantineSize(); I++)
614-
releaseIfOlderThan(Quarantine[I], Time);
615-
for (uptr I = 0; I < Config::getEntriesArraySize(); I++)
616-
releaseIfOlderThan(Entries[I], Time);
617-
}
618611

612+
for (uptr I = 0; I < Config::getQuarantineSize(); I++) {
613+
CachedBlock &ReleaseEntry = Quarantine[I];
614+
if (!ReleaseEntry.isValid() || !ReleaseEntry.Time ||
615+
ReleaseEntry.Time > Time)
616+
continue;
617+
release(ReleaseEntry);
618+
}
619+
620+
// Release oldest entries first by releasing from decommit base
621+
while (LastUnreleasedEntry != CachedBlock::InvalidEntry &&
622+
Entries[LastUnreleasedEntry].Time <= Time) {
623+
release(Entries[LastUnreleasedEntry]);
624+
LastUnreleasedEntry = Entries[LastUnreleasedEntry].Prev;
625+
}
626+
}
619627
HybridMutex Mutex;
620628
u32 EntriesCount GUARDED_BY(Mutex) = 0;
621629
u32 QuarantinePos GUARDED_BY(Mutex) = 0;
622630
atomic_u32 MaxEntriesCount = {};
623631
atomic_uptr MaxEntrySize = {};
624-
u64 OldestTime GUARDED_BY(Mutex) = 0;
625632
atomic_s32 ReleaseToOsIntervalMs = {};
626633
u32 CallsToRetrieve GUARDED_BY(Mutex) = 0;
627634
u32 SuccessfulRetrieves GUARDED_BY(Mutex) = 0;
@@ -636,6 +643,9 @@ class MapAllocatorCache {
636643
u16 LRUTail GUARDED_BY(Mutex) = 0;
637644
// The AvailableHead is the top of the stack of available entries
638645
u16 AvailableHead GUARDED_BY(Mutex) = 0;
646+
// The LastUnreleasedEntry is the least recently used entry that has not
647+
// been released
648+
u16 LastUnreleasedEntry GUARDED_BY(Mutex) = 0;
639649
};
640650

641651
template <typename Config> class MapAllocator {

0 commit comments

Comments
 (0)