Files
sousa-gecko/memory/build/BaseAlloc.h
T
serge-sans-paille 18c0b122d9 Bug 2034832 - Make memory/ headers compatible with current static analysis r=pbone
- use = default when relevant

This matches the clang-tidy rules:

- https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-equals-default.html

While we're at it, also harmonize use of size_t vs std::size_t.

That we already use on source files

Differential Revision: https://phabricator.services.mozilla.com/D296630
2026-04-29 06:20:46 +00:00

165 lines
5.4 KiB
C++

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifndef BASEALLOC_H
#define BASEALLOC_H
#include <algorithm>
#include "Constants.h"
#include "Mutex.h"
#include "RedBlackTree.h"
#include "Utils.h"
#include "mozilla/DoublyLinkedList.h"
#include "mozilla/fallible.h"
#include "BaseAllocInternals.h"
// The base allocator is a simple memory allocator used internally by
// mozjemalloc for its own structures.
class BaseAlloc {
public:
constexpr BaseAlloc() = default;
void Init() MOZ_REQUIRES(gInitLock);
// These functions are exposed with MFBT_API so they can be called from
// gtests.
MFBT_API void* alloc(size_t aSize) MOZ_EXCLUDES(mMutex);
MFBT_API void* calloc(size_t aNumber, size_t aSize) MOZ_EXCLUDES(mMutex);
// usable_size is safe both with and without the lock.
MFBT_API size_t usable_size(void* aPtr);
MFBT_API void free(void* aPtr) MOZ_EXCLUDES(mMutex);
MFBT_API void* realloc(void* aPtr, size_t aNewSize) MOZ_EXCLUDES(mMutex);
Mutex mMutex;
struct Stats {
size_t mMapped = 0;
size_t mCommitted = 0;
};
Stats GetStats() MOZ_EXCLUDES(mMutex) {
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mStats.mMapped >= mStats.mCommitted);
return mStats;
}
private:
// A power-of-two that's at least 16 bytes and no more than the size of
// the cell or metadata, so probably 16 bytes. This "quantum" is
// used for the object size plus metadata.
constexpr static base_alloc_size_t kBaseQuantum = mozilla::RoundUpPow2(
std::max({size_t(16), sizeof(BaseAllocCell), sizeof(BaseAllocMetadata)}));
constexpr static unsigned kBaseQuantumMask = kBaseQuantum - 1;
constexpr static unsigned kBaseQuantumLog2 =
mozilla::CeilingLog2(kBaseQuantum);
// The minimum possible allocation size. See get_list_index_for_size().
constexpr static unsigned kBaseMinimumSize =
(kCacheLineSize > kBaseQuantum * 2) ? (kCacheLineSize - kBaseQuantum * 2)
: kBaseQuantum;
// The maximum object size handled by the regular free lists (before
// deferring to the oversize rbtree). This should fit arena_t.
constexpr static base_alloc_size_t kMaxSizeForLists = 4096;
static_assert(std::has_single_bit(kMaxSizeForLists));
// There are no more than 3 size classes ber cache line. See
// get_list_index_for_size().
constexpr static unsigned kNumFreeLists =
kMaxSizeForLists / kCacheLineSize *
std::min(kCacheLineSize / kBaseQuantum, size_t(3));
static base_alloc_size_t size_round_up(base_alloc_size_t aSize);
static unsigned get_list_index_for_size(base_alloc_size_t aSize);
BaseAllocCell* alloc_cell(base_alloc_size_t aSize) MOZ_REQUIRES(mMutex);
// Allocate from a free list.
BaseAllocCell* alloc_from_list(base_alloc_size_t aSize) MOZ_REQUIRES(mMutex);
// Allocate from the oversize tree.
BaseAllocCell* oversize_alloc(base_alloc_size_t aSize) MOZ_REQUIRES(mMutex);
// Allocate from the decommitted tree, this will recommit memory as
// needed.
BaseAllocCell* decommitted_alloc(base_alloc_size_t aSize)
MOZ_REQUIRES(mMutex);
// Remove the cell from its free list.
void Unlink(BaseAllocCell* cell) MOZ_REQUIRES(mMutex);
// Add the cell to the free list.
void Link(BaseAllocCell* cell) MOZ_REQUIRES(mMutex);
mozilla::DoublyLinkedList<BaseAllocCell>
mFreeLists[kNumFreeLists] MOZ_GUARDED_BY(mMutex);
RedBlackTree<BaseAllocCell, BaseAllocCellRBTrait> mFreeListOversize
MOZ_GUARDED_BY(mMutex);
// Cells with some memory pages decommitted.
RedBlackTree<BaseAllocCell, BaseAllocCellRBTrait> mFreeListDecommitted
MOZ_GUARDED_BY(mMutex);
// Allocate a new chunk and attempt to split it to return a cell at least
// minsize. The other half of the split is added to the appropriate free
// list.
BaseAllocCell* chunk_alloc(base_alloc_size_t aSize) MOZ_REQUIRES(mMutex);
// Attempt to merge any decommitted cells smaller than aSize committing
// them in the process. If it forms at least one cell at least aSize
// large then it returns true. This is used during allocation before
// checking the decommitted cells.
bool merge_decommitted_cells(base_alloc_size_t aSize) MOZ_REQUIRES(mMutex);
void MaybeTrim(BaseAllocCell* aCell, base_alloc_size_t aSizeRequest,
bool aDecommit = false) MOZ_REQUIRES(mMutex);
Stats mStats MOZ_GUARDED_BY(mMutex);
friend BaseAllocCell;
};
MFBT_API extern BaseAlloc sBaseAlloc;
// Other classes may inherit from BaseAllocClass to get new and delete
// methods that use the base allocator.
struct BaseAllocClass {
void* operator new(size_t aSize) noexcept {
void* ret = sBaseAlloc.alloc(aSize);
if (!ret) {
_malloc_message(_getprogname(), ": (malloc) Out of memory\n");
MOZ_CRASH();
}
return ret;
}
void* operator new[](size_t aSize) noexcept {
void* ret = sBaseAlloc.alloc(aSize);
if (!ret) {
_malloc_message(_getprogname(), ": (malloc) Out of memory\n");
MOZ_CRASH();
}
return ret;
}
void* operator new(size_t aCount, const mozilla::fallible_t&) noexcept {
return sBaseAlloc.alloc(aCount);
}
void* operator new[](size_t aCount, const mozilla::fallible_t&) noexcept {
return sBaseAlloc.alloc(aCount);
}
void operator delete(void* aPtr) { sBaseAlloc.free(aPtr); }
void operator delete[](void* aPtr) { sBaseAlloc.free(aPtr); }
};
#endif /* ! BASEALLOC_H */