Skip to content

Commit aa29200

Browse files
committed
[scudo] Update secondary cache time-based release logic.
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 9e9971b commit aa29200

File tree

1 file changed

+34
-21
lines changed

1 file changed

+34
-21
lines changed

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

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,10 @@ class MapAllocatorCache {
318318
}
319319
CachedBlock PrevEntry = Quarantine[QuarantinePos];
320320
Quarantine[QuarantinePos] = Entry;
321-
if (OldestTime == 0)
322-
OldestTime = Entry.Time;
323321
Entry = PrevEntry;
322+
// Set entry time once more to reflect time
323+
// that quarantined memory was placed in the cache
324+
Entry.Time = Time;
324325
}
325326

326327
// All excess entries are evicted from the cache
@@ -331,9 +332,6 @@ class MapAllocatorCache {
331332
}
332333

333334
insert(Entry);
334-
335-
if (OldestTime == 0)
336-
OldestTime = Entry.Time;
337335
} while (0);
338336

339337
for (MemMapT &EvictMemMap : EvictionMemMaps)
@@ -514,6 +512,11 @@ class MapAllocatorCache {
514512
return (EntriesCount >= atomic_load_relaxed(&MaxEntriesCount));
515513
}
516514

515+
inline void advanceDecommitBase() REQUIRES(Mutex) {
516+
if (DecommitBase != CachedBlock::InvalidEntry)
517+
DecommitBase = Entries[DecommitBase].Prev;
518+
}
519+
517520
void insert(const CachedBlock &Entry) REQUIRES(Mutex) {
518521
DCHECK_LT(EntriesCount, atomic_load_relaxed(&MaxEntriesCount));
519522

@@ -532,6 +535,9 @@ class MapAllocatorCache {
532535
Entries[LRUHead].Prev = static_cast<u16>(FreeIndex);
533536
}
534537

538+
if (DecommitBase == CachedBlock::InvalidEntry)
539+
DecommitBase = static_cast<u16>(FreeIndex);
540+
535541
Entries[FreeIndex] = Entry;
536542
Entries[FreeIndex].Next = LRUHead;
537543
Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
@@ -549,6 +555,9 @@ class MapAllocatorCache {
549555

550556
Entries[I].invalidate();
551557

558+
if (I == DecommitBase)
559+
advanceDecommitBase();
560+
552561
if (I == LRUHead)
553562
LRUHead = Entries[I].Next;
554563
else
@@ -590,35 +599,36 @@ class MapAllocatorCache {
590599
}
591600
}
592601

593-
void releaseIfOlderThan(CachedBlock &Entry, u64 Time) REQUIRES(Mutex) {
594-
if (!Entry.isValid() || !Entry.Time)
595-
return;
596-
if (Entry.Time > Time) {
597-
if (OldestTime == 0 || Entry.Time < OldestTime)
598-
OldestTime = Entry.Time;
599-
return;
600-
}
602+
inline void release(CachedBlock &Entry) {
603+
DCHECK(Entry.Time != 0);
601604
Entry.MemMap.releaseAndZeroPagesToOS(Entry.CommitBase, Entry.CommitSize);
602605
Entry.Time = 0;
603606
}
604607

605608
void releaseOlderThan(u64 Time) EXCLUDES(Mutex) {
606609
ScopedLock L(Mutex);
607-
if (!EntriesCount || OldestTime == 0 || OldestTime > Time)
610+
if (!EntriesCount)
608611
return;
609-
OldestTime = 0;
610-
for (uptr I = 0; I < Config::getQuarantineSize(); I++)
611-
releaseIfOlderThan(Quarantine[I], Time);
612-
for (uptr I = 0; I < Config::getEntriesArraySize(); I++)
613-
releaseIfOlderThan(Entries[I], Time);
614-
}
615612

613+
for (uptr I = 0; I < Config::getQuarantineSize(); I++) {
614+
CachedBlock &ReleaseEntry = Quarantine[I];
615+
if (!ReleaseEntry.isValid() || ReleaseEntry.Time > Time)
616+
continue;
617+
release(ReleaseEntry);
618+
}
619+
620+
// Release oldest entries first by releasing from decommit base
621+
while (DecommitBase != CachedBlock::InvalidEntry &&
622+
Entries[DecommitBase].Time <= Time) {
623+
release(Entries[DecommitBase]);
624+
advanceDecommitBase();
625+
}
626+
}
616627
HybridMutex Mutex;
617628
u32 EntriesCount GUARDED_BY(Mutex) = 0;
618629
u32 QuarantinePos GUARDED_BY(Mutex) = 0;
619630
atomic_u32 MaxEntriesCount = {};
620631
atomic_uptr MaxEntrySize = {};
621-
u64 OldestTime GUARDED_BY(Mutex) = 0;
622632
atomic_s32 ReleaseToOsIntervalMs = {};
623633
u32 CallsToRetrieve GUARDED_BY(Mutex) = 0;
624634
u32 SuccessfulRetrieves GUARDED_BY(Mutex) = 0;
@@ -633,6 +643,9 @@ class MapAllocatorCache {
633643
u16 LRUTail GUARDED_BY(Mutex) = 0;
634644
// The AvailableHead is the top of the stack of available entries
635645
u16 AvailableHead GUARDED_BY(Mutex) = 0;
646+
// The DecommitBase is the least recently used entry that has not
647+
// been released
648+
u16 DecommitBase GUARDED_BY(Mutex) = 0;
636649
};
637650

638651
template <typename Config> class MapAllocator {

0 commit comments

Comments
 (0)