@@ -247,6 +247,7 @@ class MapAllocatorCache {
247
247
// The cache is initially empty
248
248
LRUHead = CachedBlock::InvalidEntry;
249
249
LRUTail = CachedBlock::InvalidEntry;
250
+ LastUnreleasedEntry = CachedBlock::InvalidEntry;
250
251
251
252
// Available entries will be retrieved starting from the beginning of the
252
253
// Entries array
@@ -321,9 +322,13 @@ class MapAllocatorCache {
321
322
}
322
323
CachedBlock PrevEntry = Quarantine[QuarantinePos];
323
324
Quarantine[QuarantinePos] = Entry;
324
- if (OldestTime == 0 )
325
- OldestTime = Entry.Time ;
326
325
Entry = PrevEntry;
326
+ // Update the entry time to reflect the time that the
327
+ // quarantined memory is placed in the Entries array
328
+ // If an entry has a time value of 0, it has already
329
+ // been released, so its time value must remain 0
330
+ if (Entry.Time != 0 )
331
+ Entry.Time = Time;
327
332
}
328
333
329
334
// All excess entries are evicted from the cache
@@ -334,16 +339,12 @@ class MapAllocatorCache {
334
339
}
335
340
336
341
insert (Entry);
337
-
338
- if (OldestTime == 0 )
339
- OldestTime = Entry.Time ;
340
342
} while (0 );
341
343
342
344
for (MemMapT &EvictMemMap : EvictionMemMaps)
343
345
unmapCallBack (EvictMemMap);
344
346
345
347
if (Interval >= 0 ) {
346
- // TODO: Add ReleaseToOS logic to LRU algorithm
347
348
releaseOlderThan (Time - static_cast <u64>(Interval) * 1000000 );
348
349
}
349
350
}
@@ -523,22 +524,37 @@ class MapAllocatorCache {
523
524
// Cache should be populated with valid entries when not empty
524
525
DCHECK_NE (AvailableHead, CachedBlock::InvalidEntry);
525
526
526
- u32 FreeIndex = AvailableHead;
527
+ u16 FreeIndex = AvailableHead;
527
528
AvailableHead = Entries[AvailableHead].Next ;
529
+ Entries[FreeIndex] = Entry;
528
530
529
- if (EntriesCount == 0 ) {
530
- LRUTail = static_cast <u16>(FreeIndex);
531
+ // Check list order
532
+ if (EntriesCount > 1 )
533
+ DCHECK_GE (Entries[LRUHead].Time , Entries[Entries[LRUHead].Next ].Time );
534
+
535
+ // Released entry goes after LastUnreleasedEntry rather than at LRUHead
536
+ if (Entry.Time == 0 && LastUnreleasedEntry != CachedBlock::InvalidEntry) {
537
+ Entries[FreeIndex].Next = Entries[LastUnreleasedEntry].Next ;
538
+ Entries[FreeIndex].Prev = LastUnreleasedEntry;
539
+ Entries[LastUnreleasedEntry].Next = FreeIndex;
540
+ if (LRUTail == LastUnreleasedEntry) {
541
+ LRUTail = FreeIndex;
542
+ } else {
543
+ Entries[Entries[FreeIndex].Next ].Prev = FreeIndex;
544
+ }
531
545
} else {
532
- // Check list order
533
- if (EntriesCount > 1 )
534
- DCHECK_GE (Entries[LRUHead].Time , Entries[Entries[LRUHead].Next ].Time );
535
- Entries[LRUHead].Prev = static_cast <u16>(FreeIndex);
546
+ Entries[FreeIndex].Next = LRUHead;
547
+ Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
548
+ if (EntriesCount == 0 ) {
549
+ LRUTail = FreeIndex;
550
+ } else {
551
+ Entries[LRUHead].Prev = FreeIndex;
552
+ }
553
+ LRUHead = FreeIndex;
554
+ if (LastUnreleasedEntry == CachedBlock::InvalidEntry)
555
+ LastUnreleasedEntry = FreeIndex;
536
556
}
537
557
538
- Entries[FreeIndex] = Entry;
539
- Entries[FreeIndex].Next = LRUHead;
540
- Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
541
- LRUHead = static_cast <u16>(FreeIndex);
542
558
EntriesCount++;
543
559
544
560
// Availability stack should not have available entries when all entries
@@ -552,6 +568,9 @@ class MapAllocatorCache {
552
568
553
569
Entries[I].invalidate ();
554
570
571
+ if (I == LastUnreleasedEntry)
572
+ LastUnreleasedEntry = Entries[LastUnreleasedEntry].Prev ;
573
+
555
574
if (I == LRUHead)
556
575
LRUHead = Entries[I].Next ;
557
576
else
@@ -593,35 +612,39 @@ class MapAllocatorCache {
593
612
}
594
613
}
595
614
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
- }
615
+ inline void release (CachedBlock &Entry) {
616
+ DCHECK (Entry.Time != 0 );
604
617
Entry.MemMap .releaseAndZeroPagesToOS (Entry.CommitBase , Entry.CommitSize );
605
618
Entry.Time = 0 ;
606
619
}
607
620
608
621
void releaseOlderThan (u64 Time) EXCLUDES(Mutex) {
609
622
ScopedLock L (Mutex);
610
- if (!EntriesCount || OldestTime == 0 || OldestTime > Time )
623
+ if (!EntriesCount)
611
624
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
- }
618
625
626
+ // TODO: Add conditional to skip iteration over quarantine
627
+ // if quarantine is disabled
628
+ for (uptr I = 0 ; I < Config::getQuarantineSize (); I++) {
629
+ CachedBlock &ReleaseEntry = Quarantine[I];
630
+ if (!ReleaseEntry.isValid () || !ReleaseEntry.Time ||
631
+ ReleaseEntry.Time > Time)
632
+ continue ;
633
+ release (ReleaseEntry);
634
+ }
635
+
636
+ // Release oldest entries first by releasing from decommit base
637
+ while (LastUnreleasedEntry != CachedBlock::InvalidEntry &&
638
+ Entries[LastUnreleasedEntry].Time <= Time) {
639
+ release (Entries[LastUnreleasedEntry]);
640
+ LastUnreleasedEntry = Entries[LastUnreleasedEntry].Prev ;
641
+ }
642
+ }
619
643
HybridMutex Mutex;
620
644
u32 EntriesCount GUARDED_BY (Mutex) = 0;
621
645
u32 QuarantinePos GUARDED_BY (Mutex) = 0;
622
646
atomic_u32 MaxEntriesCount = {};
623
647
atomic_uptr MaxEntrySize = {};
624
- u64 OldestTime GUARDED_BY (Mutex) = 0;
625
648
atomic_s32 ReleaseToOsIntervalMs = {};
626
649
u32 CallsToRetrieve GUARDED_BY (Mutex) = 0;
627
650
u32 SuccessfulRetrieves GUARDED_BY (Mutex) = 0;
@@ -636,6 +659,9 @@ class MapAllocatorCache {
636
659
u16 LRUTail GUARDED_BY (Mutex) = 0;
637
660
// The AvailableHead is the top of the stack of available entries
638
661
u16 AvailableHead GUARDED_BY (Mutex) = 0;
662
+ // The LastUnreleasedEntry is the least recently used entry that has not
663
+ // been released
664
+ u16 LastUnreleasedEntry GUARDED_BY (Mutex) = 0;
639
665
};
640
666
641
667
template <typename Config> class MapAllocator {
0 commit comments