/* 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 */