@@ -184,14 +184,6 @@ template <typename T> class NonZeroLengthArray<T, 0> {
184184template <typename Config, void (*unmapCallBack)(MemMapT &) = unmap>
185185class MapAllocatorCache {
186186public:
187- typedef enum { COMMITTED = 0 , DECOMMITTED = 1 , NONE } EntryListT;
188-
189- // TODO: Refactor the intrusive list to support non-pointer link type
190- typedef struct {
191- u16 Head;
192- u16 Tail;
193- } ListInfo;
194-
195187 void getStats (ScopedString *Str) {
196188 ScopedLock L (Mutex);
197189 uptr Integral;
@@ -209,18 +201,13 @@ class MapAllocatorCache {
209201 SuccessfulRetrieves, CallsToRetrieve, Integral, Fractional);
210202 Str->append (" Cache Entry Info (Most Recent -> Least Recent):\n " );
211203
212- auto printList = [&](EntryListT ListType) REQUIRES (Mutex) {
213- for (u32 I = EntryLists[ListType].Head ; I != CachedBlock::InvalidEntry;
214- I = Entries[I].Next ) {
215- CachedBlock &Entry = Entries[I];
216- Str->append (" StartBlockAddress: 0x%zx, EndBlockAddress: 0x%zx, "
217- " BlockSize: %zu %s\n " ,
218- Entry.CommitBase , Entry.CommitBase + Entry.CommitSize ,
219- Entry.CommitSize , Entry.Time == 0 ? " [R]" : " " );
220- }
221- };
222- printList (COMMITTED);
223- printList (DECOMMITTED);
204+ for (u32 I = LRUHead; I != CachedBlock::InvalidEntry; I = Entries[I].Next ) {
205+ CachedBlock &Entry = Entries[I];
206+ Str->append (" StartBlockAddress: 0x%zx, EndBlockAddress: 0x%zx, "
207+ " BlockSize: %zu %s\n " ,
208+ Entry.CommitBase , Entry.CommitBase + Entry.CommitSize ,
209+ Entry.CommitSize , Entry.Time == 0 ? " [R]" : " " );
210+ }
224211 }
225212
226213 // Ensure the default maximum specified fits the array.
@@ -244,10 +231,8 @@ class MapAllocatorCache {
244231 setOption (Option::ReleaseInterval, static_cast <sptr>(ReleaseToOsInterval));
245232
246233 // The cache is initially empty
247- EntryLists[COMMITTED].Head = CachedBlock::InvalidEntry;
248- EntryLists[COMMITTED].Tail = CachedBlock::InvalidEntry;
249- EntryLists[DECOMMITTED].Head = CachedBlock::InvalidEntry;
250- EntryLists[DECOMMITTED].Tail = CachedBlock::InvalidEntry;
234+ LRUHead = CachedBlock::InvalidEntry;
235+ LRUTail = CachedBlock::InvalidEntry;
251236
252237 // Available entries will be retrieved starting from the beginning of the
253238 // Entries array
@@ -265,6 +250,7 @@ class MapAllocatorCache {
265250 const s32 Interval = atomic_load_relaxed (&ReleaseToOsIntervalMs);
266251 u64 Time;
267252 CachedBlock Entry;
253+
268254 Entry.CommitBase = CommitBase;
269255 Entry.CommitSize = CommitSize;
270256 Entry.BlockBegin = BlockBegin;
@@ -326,27 +312,18 @@ class MapAllocatorCache {
326312 Entry = PrevEntry;
327313 }
328314
329- // All excess entries are evicted from the cache.
330- // DECOMMITTED entries, being older than the COMMITTED
331- // entries, are evicted first in least recently used (LRU)
332- // fashioned followed by the COMMITTED entries
315+ // All excess entries are evicted from the cache
333316 while (needToEvict ()) {
334- EntryListT EvictionListType;
335- if (EntryLists[DECOMMITTED].Tail == CachedBlock::InvalidEntry)
336- EvictionListType = COMMITTED;
337- else
338- EvictionListType = DECOMMITTED;
339317 // Save MemMaps of evicted entries to perform unmap outside of lock
340- EvictionMemMaps.push_back (
341- Entries[EntryLists[EvictionListType].Tail ].MemMap );
342- remove (EntryLists[EvictionListType].Tail , EvictionListType);
318+ EvictionMemMaps.push_back (Entries[LRUTail].MemMap );
319+ remove (LRUTail);
343320 }
344321
345- insert (Entry, (Entry. Time == 0 ) ? DECOMMITTED : COMMITTED );
322+ insert (Entry);
346323
347324 if (OldestTime == 0 )
348325 OldestTime = Entry.Time ;
349- } while (0 ); // ScopedLock L(Mutex);
326+ } while (0 );
350327
351328 for (MemMapT &EvictMemMap : EvictionMemMaps)
352329 unmapCallBack (EvictMemMap);
@@ -363,14 +340,17 @@ class MapAllocatorCache {
363340 // 10% of the requested size proved to be the optimal choice for
364341 // retrieving cached blocks after testing several options.
365342 constexpr u32 FragmentedBytesDivisor = 10 ;
343+ bool Found = false ;
366344 CachedBlock Entry;
367- uptr OptimalFitIndex = CachedBlock::InvalidEntry;
368- uptr MinDiff = UINTPTR_MAX;
369- EntryListT OptimalFitListType = NONE;
370345 EntryHeaderPos = 0 ;
371-
372- auto FindAvailableEntry = [&](EntryListT ListType) REQUIRES (Mutex) {
373- for (uptr I = EntryLists[ListType].Head ; I != CachedBlock::InvalidEntry;
346+ {
347+ ScopedLock L (Mutex);
348+ CallsToRetrieve++;
349+ if (EntriesCount == 0 )
350+ return {};
351+ u32 OptimalFitIndex = 0 ;
352+ uptr MinDiff = UINTPTR_MAX;
353+ for (u32 I = LRUHead; I != CachedBlock::InvalidEntry;
374354 I = Entries[I].Next ) {
375355 const uptr CommitBase = Entries[I].CommitBase ;
376356 const uptr CommitSize = Entries[I].CommitSize ;
@@ -380,48 +360,34 @@ class MapAllocatorCache {
380360 if (HeaderPos > CommitBase + CommitSize)
381361 continue ;
382362 if (HeaderPos < CommitBase ||
383- AllocPos > CommitBase + PageSize * MaxUnusedCachePages)
363+ AllocPos > CommitBase + PageSize * MaxUnusedCachePages) {
384364 continue ;
385-
365+ }
366+ Found = true ;
386367 const uptr Diff = HeaderPos - CommitBase;
387- // immediately use a cached block if it's size is close enough to
388- // the requested size.
368+ // immediately use a cached block if it's size is close enough to the
369+ // requested size.
389370 const uptr MaxAllowedFragmentedBytes =
390371 (CommitBase + CommitSize - HeaderPos) / FragmentedBytesDivisor;
391372 if (Diff <= MaxAllowedFragmentedBytes) {
392373 OptimalFitIndex = I;
393374 EntryHeaderPos = HeaderPos;
394- OptimalFitListType = ListType;
395- return true ;
375+ break ;
396376 }
397-
398377 // keep track of the smallest cached block
399378 // that is greater than (AllocSize + HeaderSize)
400379 if (Diff > MinDiff)
401380 continue ;
402381 OptimalFitIndex = I;
403382 MinDiff = Diff;
404- OptimalFitListType = ListType;
405383 EntryHeaderPos = HeaderPos;
406384 }
407- return (OptimalFitIndex != CachedBlock::InvalidEntry);
408- };
409-
410- {
411- ScopedLock L (Mutex);
412- CallsToRetrieve++;
413- if (EntriesCount == 0 )
414- return {};
415-
416- // Prioritize valid fit from COMMITTED entries over
417- // optimal fit from DECOMMITTED entries
418- if (!FindAvailableEntry (COMMITTED) && !FindAvailableEntry (DECOMMITTED))
419- return {};
420-
421- Entry = Entries[OptimalFitIndex];
422- remove (OptimalFitIndex, OptimalFitListType);
423- SuccessfulRetrieves++;
424- } // ScopedLock L(Mutex);
385+ if (Found) {
386+ Entry = Entries[OptimalFitIndex];
387+ remove (OptimalFitIndex);
388+ SuccessfulRetrieves++;
389+ }
390+ }
425391
426392 return Entry;
427393 }
@@ -466,15 +432,10 @@ class MapAllocatorCache {
466432 Quarantine[I].invalidate ();
467433 }
468434 }
469- auto disableLists = [&](EntryListT EntryList) REQUIRES (Mutex) {
470- for (u32 I = EntryLists[EntryList].Head ; I != CachedBlock::InvalidEntry;
471- I = Entries[I].Next ) {
472- Entries[I].MemMap .setMemoryPermission (Entries[I].CommitBase ,
473- Entries[I].CommitSize , 0 );
474- }
475- };
476- disableLists (COMMITTED);
477- disableLists (DECOMMITTED);
435+ for (u32 I = LRUHead; I != CachedBlock::InvalidEntry; I = Entries[I].Next ) {
436+ Entries[I].MemMap .setMemoryPermission (Entries[I].CommitBase ,
437+ Entries[I].CommitSize , 0 );
438+ }
478439 QuarantinePos = -1U ;
479440 }
480441
@@ -489,7 +450,7 @@ class MapAllocatorCache {
489450 return (EntriesCount >= atomic_load_relaxed (&MaxEntriesCount));
490451 }
491452
492- void insert (const CachedBlock &Entry, EntryListT ListType ) REQUIRES(Mutex) {
453+ void insert (const CachedBlock &Entry) REQUIRES(Mutex) {
493454 DCHECK_LT (EntriesCount, atomic_load_relaxed (&MaxEntriesCount));
494455
495456 // Cache should be populated with valid entries when not empty
@@ -498,86 +459,66 @@ class MapAllocatorCache {
498459 u32 FreeIndex = AvailableHead;
499460 AvailableHead = Entries[AvailableHead].Next ;
500461
462+ if (EntriesCount == 0 ) {
463+ LRUTail = static_cast <u16 >(FreeIndex);
464+ } else {
465+ // Check list order
466+ if (EntriesCount > 1 )
467+ DCHECK_GE (Entries[LRUHead].Time , Entries[Entries[LRUHead].Next ].Time );
468+ Entries[LRUHead].Prev = static_cast <u16 >(FreeIndex);
469+ }
470+
501471 Entries[FreeIndex] = Entry;
502- pushFront (FreeIndex, ListType);
472+ Entries[FreeIndex].Next = LRUHead;
473+ Entries[FreeIndex].Prev = CachedBlock::InvalidEntry;
474+ LRUHead = static_cast <u16 >(FreeIndex);
503475 EntriesCount++;
504476
505- if (Entries[EntryLists[ListType].Head ].Next != CachedBlock::InvalidEntry) {
506- DCHECK_GE (Entries[EntryLists[ListType].Head ].Time ,
507- Entries[Entries[EntryLists[ListType].Head ].Next ].Time );
508- }
509477 // Availability stack should not have available entries when all entries
510478 // are in use
511479 if (EntriesCount == Config::getEntriesArraySize ())
512480 DCHECK_EQ (AvailableHead, CachedBlock::InvalidEntry);
513481 }
514482
515- // Joins the entries adjacent to Entries[I], effectively
516- // unlinking Entries[I] from the list
517- void unlink (uptr I, EntryListT ListType) REQUIRES(Mutex) {
518- if (I == EntryLists[ListType].Head )
519- EntryLists[ListType].Head = Entries[I].Next ;
483+ void remove (uptr I) REQUIRES(Mutex) {
484+ DCHECK (Entries[I].isValid ());
485+
486+ Entries[I].invalidate ();
487+
488+ if (I == LRUHead)
489+ LRUHead = Entries[I].Next ;
520490 else
521491 Entries[Entries[I].Prev ].Next = Entries[I].Next ;
522492
523- if (I == EntryLists[ListType]. Tail )
524- EntryLists[ListType]. Tail = Entries[I].Prev ;
493+ if (I == LRUTail )
494+ LRUTail = Entries[I].Prev ;
525495 else
526496 Entries[Entries[I].Next ].Prev = Entries[I].Prev ;
527- }
528-
529- // Invalidates Entries[I], removes Entries[I] from list, and pushes
530- // Entries[I] onto the stack of available entries
531- void remove (uptr I, EntryListT ListType) REQUIRES(Mutex) {
532- DCHECK (Entries[I].isValid ());
533-
534- Entries[I].invalidate ();
535497
536- unlink (I, ListType);
537498 Entries[I].Next = AvailableHead;
538499 AvailableHead = static_cast <u16 >(I);
539500 EntriesCount--;
540501
541502 // Cache should not have valid entries when not empty
542503 if (EntriesCount == 0 ) {
543- DCHECK_EQ (EntryLists[COMMITTED].Head , CachedBlock::InvalidEntry);
544- DCHECK_EQ (EntryLists[COMMITTED].Tail , CachedBlock::InvalidEntry);
545- DCHECK_EQ (EntryLists[DECOMMITTED].Head , CachedBlock::InvalidEntry);
546- DCHECK_EQ (EntryLists[DECOMMITTED].Tail , CachedBlock::InvalidEntry);
504+ DCHECK_EQ (LRUHead, CachedBlock::InvalidEntry);
505+ DCHECK_EQ (LRUTail, CachedBlock::InvalidEntry);
547506 }
548507 }
549508
550- inline void pushFront (uptr I, EntryListT ListType) REQUIRES(Mutex) {
551- if (EntryLists[ListType].Tail == CachedBlock::InvalidEntry)
552- EntryLists[ListType].Tail = static_cast <u16 >(I);
553- else
554- Entries[EntryLists[ListType].Head ].Prev = static_cast <u16 >(I);
555-
556- Entries[I].Next = EntryLists[ListType].Head ;
557- Entries[I].Prev = CachedBlock::InvalidEntry;
558- EntryLists[ListType].Head = static_cast <u16 >(I);
559- }
560-
561509 void empty () {
562510 MemMapT MapInfo[Config::getEntriesArraySize ()];
563511 uptr N = 0 ;
564512 {
565513 ScopedLock L (Mutex);
566- auto emptyList = [&](EntryListT ListType) REQUIRES (Mutex) {
567- for (uptr I = EntryLists[ListType].Head ;
568- I != CachedBlock::InvalidEntry;) {
569- uptr ToRemove = I;
570- I = Entries[I].Next ;
571- MapInfo[N] = Entries[ToRemove].MemMap ;
572- remove (ToRemove, ListType);
573- N++;
574- }
575- };
576- emptyList (COMMITTED);
577- emptyList (DECOMMITTED);
514+ for (uptr I = 0 ; I < Config::getEntriesArraySize (); I++) {
515+ if (!Entries[I].isValid ())
516+ continue ;
517+ MapInfo[N] = Entries[I].MemMap ;
518+ remove (I);
519+ N++;
520+ }
578521 EntriesCount = 0 ;
579- for (uptr I = 0 ; I < Config::getEntriesArraySize (); I++)
580- DCHECK (!Entries[I].isValid ());
581522 }
582523 for (uptr I = 0 ; I < N; I++) {
583524 MemMapT &MemMap = MapInfo[I];
@@ -604,14 +545,8 @@ class MapAllocatorCache {
604545 OldestTime = 0 ;
605546 for (uptr I = 0 ; I < Config::getQuarantineSize (); I++)
606547 releaseIfOlderThan (Quarantine[I], Time);
607- for (u16 I = EntryLists[COMMITTED].Head ; I != CachedBlock::InvalidEntry;
608- I = Entries[I].Next ) {
609- if (Entries[I].Time && Entries[I].Time <= Time) {
610- unlink (I, COMMITTED);
611- pushFront (I, DECOMMITTED);
612- }
548+ for (uptr I = 0 ; I < Config::getEntriesArraySize (); I++)
613549 releaseIfOlderThan (Entries[I], Time);
614- }
615550 }
616551
617552 HybridMutex Mutex;
@@ -628,12 +563,10 @@ class MapAllocatorCache {
628563 NonZeroLengthArray<CachedBlock, Config::getQuarantineSize()>
629564 Quarantine GUARDED_BY (Mutex) = {};
630565
631- // EntryLists stores the head and tail indices of all
632- // lists being used to store valid cache entries.
633- // Currently there are lists storing COMMITTED and DECOMMITTED entries.
634- // COMMITTED entries have memory chunks that have not been released to the OS
635- // DECOMMITTED entries have memory chunks that have been released to the OS
636- ListInfo EntryLists[2 ] GUARDED_BY(Mutex) = {};
566+ // The LRUHead of the cache is the most recently used cache entry
567+ u16 LRUHead GUARDED_BY (Mutex) = 0;
568+ // The LRUTail of the cache is the least recently used cache entry
569+ u16 LRUTail GUARDED_BY (Mutex) = 0;
637570 // The AvailableHead is the top of the stack of available entries
638571 u16 AvailableHead GUARDED_BY (Mutex) = 0;
639572};
@@ -773,7 +706,6 @@ MapAllocator<Config>::tryAllocateFromCache(const Options &Options, uptr Size,
773706 }
774707 return Ptr;
775708}
776-
777709// As with the Primary, the size passed to this function includes any desired
778710// alignment, so that the frontend can align the user allocation. The hint
779711// parameter allows us to unmap spurious memory when dealing with larger
0 commit comments