mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-21 14:33:54 +02:00
189 lines
6 KiB
C++
189 lines
6 KiB
C++
//===-- local_cache.h -------------------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SCUDO_LOCAL_CACHE_H_
|
|
#define SCUDO_LOCAL_CACHE_H_
|
|
|
|
#include "internal_defs.h"
|
|
#include "list.h"
|
|
#include "platform.h"
|
|
#include "report.h"
|
|
#include "stats.h"
|
|
#include "string_utils.h"
|
|
|
|
namespace scudo {
|
|
|
|
template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache {
|
|
typedef typename SizeClassAllocator::SizeClassMap SizeClassMap;
|
|
typedef typename SizeClassAllocator::CompactPtrT CompactPtrT;
|
|
|
|
void init(GlobalStats *S, SizeClassAllocator *A) {
|
|
DCHECK(isEmpty());
|
|
Stats.init();
|
|
if (LIKELY(S))
|
|
S->link(&Stats);
|
|
Allocator = A;
|
|
initCache();
|
|
}
|
|
|
|
void destroy(GlobalStats *S) {
|
|
drain();
|
|
if (LIKELY(S))
|
|
S->unlink(&Stats);
|
|
}
|
|
|
|
void *allocate(uptr ClassId) {
|
|
DCHECK_LT(ClassId, NumClasses);
|
|
PerClass *C = &PerClassArray[ClassId];
|
|
if (C->Count == 0) {
|
|
// Refill half of the number of max cached.
|
|
DCHECK_GT(C->MaxCount / 2, 0U);
|
|
if (UNLIKELY(!refill(C, ClassId, C->MaxCount / 2)))
|
|
return nullptr;
|
|
DCHECK_GT(C->Count, 0);
|
|
}
|
|
// We read ClassSize first before accessing Chunks because it's adjacent to
|
|
// Count, while Chunks might be further off (depending on Count). That keeps
|
|
// the memory accesses in close quarters.
|
|
const uptr ClassSize = C->ClassSize;
|
|
CompactPtrT CompactP = C->Chunks[--C->Count];
|
|
Stats.add(StatAllocated, ClassSize);
|
|
Stats.sub(StatFree, ClassSize);
|
|
return Allocator->decompactPtr(ClassId, CompactP);
|
|
}
|
|
|
|
bool deallocate(uptr ClassId, void *P) {
|
|
CHECK_LT(ClassId, NumClasses);
|
|
PerClass *C = &PerClassArray[ClassId];
|
|
|
|
// If the cache is full, drain half of blocks back to the main allocator.
|
|
const bool NeedToDrainCache = C->Count == C->MaxCount;
|
|
if (NeedToDrainCache)
|
|
drain(C, ClassId);
|
|
// See comment in allocate() about memory accesses.
|
|
const uptr ClassSize = C->ClassSize;
|
|
C->Chunks[C->Count++] =
|
|
Allocator->compactPtr(ClassId, reinterpret_cast<uptr>(P));
|
|
Stats.sub(StatAllocated, ClassSize);
|
|
Stats.add(StatFree, ClassSize);
|
|
|
|
return NeedToDrainCache;
|
|
}
|
|
|
|
bool isEmpty() const {
|
|
for (uptr I = 0; I < NumClasses; ++I)
|
|
if (PerClassArray[I].Count)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void drain() {
|
|
// Drain BatchClassId last as it may be needed while draining normal blocks.
|
|
for (uptr I = 0; I < NumClasses; ++I) {
|
|
if (I == BatchClassId)
|
|
continue;
|
|
while (PerClassArray[I].Count > 0)
|
|
drain(&PerClassArray[I], I);
|
|
}
|
|
while (PerClassArray[BatchClassId].Count > 0)
|
|
drain(&PerClassArray[BatchClassId], BatchClassId);
|
|
DCHECK(isEmpty());
|
|
}
|
|
|
|
void *getBatchClassBlock() {
|
|
void *B = allocate(BatchClassId);
|
|
if (UNLIKELY(!B))
|
|
reportOutOfMemory(SizeClassAllocator::getSizeByClassId(BatchClassId));
|
|
return B;
|
|
}
|
|
|
|
LocalStats &getStats() { return Stats; }
|
|
|
|
void getStats(ScopedString *Str) {
|
|
bool EmptyCache = true;
|
|
for (uptr I = 0; I < NumClasses; ++I) {
|
|
if (PerClassArray[I].Count == 0)
|
|
continue;
|
|
|
|
EmptyCache = false;
|
|
// The size of BatchClass is set to 0 intentionally. See the comment in
|
|
// initCache() for more details.
|
|
const uptr ClassSize = I == BatchClassId
|
|
? SizeClassAllocator::getSizeByClassId(I)
|
|
: PerClassArray[I].ClassSize;
|
|
// Note that the string utils don't support printing u16 thus we cast it
|
|
// to a common use type uptr.
|
|
Str->append(" %02zu (%6zu): cached: %4zu max: %4zu\n", I, ClassSize,
|
|
static_cast<uptr>(PerClassArray[I].Count),
|
|
static_cast<uptr>(PerClassArray[I].MaxCount));
|
|
}
|
|
|
|
if (EmptyCache)
|
|
Str->append(" No block is cached.\n");
|
|
}
|
|
|
|
static u16 getMaxCached(uptr Size) {
|
|
return Min(SizeClassMap::MaxNumCachedHint,
|
|
SizeClassMap::getMaxCachedHint(Size));
|
|
}
|
|
|
|
private:
|
|
static const uptr NumClasses = SizeClassMap::NumClasses;
|
|
static const uptr BatchClassId = SizeClassMap::BatchClassId;
|
|
struct alignas(SCUDO_CACHE_LINE_SIZE) PerClass {
|
|
u16 Count;
|
|
u16 MaxCount;
|
|
// Note: ClassSize is zero for the transfer batch.
|
|
uptr ClassSize;
|
|
CompactPtrT Chunks[2 * SizeClassMap::MaxNumCachedHint];
|
|
};
|
|
PerClass PerClassArray[NumClasses] = {};
|
|
LocalStats Stats;
|
|
SizeClassAllocator *Allocator = nullptr;
|
|
|
|
NOINLINE void initCache() {
|
|
for (uptr I = 0; I < NumClasses; I++) {
|
|
PerClass *P = &PerClassArray[I];
|
|
const uptr Size = SizeClassAllocator::getSizeByClassId(I);
|
|
P->MaxCount = static_cast<u16>(2 * getMaxCached(Size));
|
|
if (I != BatchClassId) {
|
|
P->ClassSize = Size;
|
|
} else {
|
|
// ClassSize in this struct is only used for malloc/free stats, which
|
|
// should only track user allocations, not internal movements.
|
|
P->ClassSize = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void destroyBatch(uptr ClassId, void *B) {
|
|
if (ClassId != BatchClassId)
|
|
deallocate(BatchClassId, B);
|
|
}
|
|
|
|
NOINLINE bool refill(PerClass *C, uptr ClassId, u16 MaxRefill) {
|
|
const u16 NumBlocksRefilled =
|
|
Allocator->popBlocks(this, ClassId, C->Chunks, MaxRefill);
|
|
DCHECK_LE(NumBlocksRefilled, MaxRefill);
|
|
C->Count = static_cast<u16>(C->Count + NumBlocksRefilled);
|
|
return NumBlocksRefilled != 0;
|
|
}
|
|
|
|
NOINLINE void drain(PerClass *C, uptr ClassId) {
|
|
const u16 Count = Min(static_cast<u16>(C->MaxCount / 2), C->Count);
|
|
Allocator->pushBlocks(this, ClassId, &C->Chunks[0], Count);
|
|
// u16 will be promoted to int by arithmetic type conversion.
|
|
C->Count = static_cast<u16>(C->Count - Count);
|
|
for (u16 I = 0; I < C->Count; I++)
|
|
C->Chunks[I] = C->Chunks[I + Count];
|
|
}
|
|
};
|
|
|
|
} // namespace scudo
|
|
|
|
#endif // SCUDO_LOCAL_CACHE_H_
|