blob: 37819543140cc4ad9ff005e1301072a37e395961 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
#include <stddef.h>
#include <vm/vm_address_region.h>
#include <zxcpp/new.h>
#include <fbl/intrusive_single_list.h>
#include <fbl/ref_ptr.h>
#include <fbl/type_support.h>
namespace fbl {
// Arena is a fast memory allocator for objects of a single size.
// Both Alloc() and Free() are always O(1) and memory always comes
// from a single contigous chunck of page-aligned memory.
//
// The control structures and data are not interleaved so it is
// more resilient to memory bugs than traditional pool allocators.
//
// The overhead per object is two pointers (16 bytes in 64-bits)
class Arena {
public:
Arena() = default;
~Arena();
zx_status_t Init(const char* name, size_t ob_size, size_t max_count);
void* Alloc();
void Free(void* addr);
bool in_range(void* addr) const {
return data_.InRange(static_cast<char*>(addr));
}
void* start() const { return data_.start(); }
void* end() const { return data_.end(); }
// Dumps information about the Arena using printf().
// TIP: Use "k zx htinfo" to dump the handle table at runtime.
void Dump() const;
// Returns the number of outstanding allocations from this arena.
size_t DiagnosticCount() const {
return count_;
}
private:
Arena(const Arena&) = delete;
Arena& operator=(const Arena&) = delete;
struct Node : public SinglyLinkedListable<Node*> {
explicit Node(void* s)
: slot(s) {}
void* slot;
};
SinglyLinkedList<Node*> free_;
// A memory pool that manually commits/decommits memory as
// necessary, avoiding demand-paging.
class Pool {
public:
// Initializes the pool. |mapping| must be fully backed by its
// VMO, but should not have any committed pages. |slot_size|
// is the size of object returned/accepted by Pop/Push.
void Init(const char* name, fbl::RefPtr<VmMapping> mapping,
size_t slot_size);
// Returns a pointer to a |slot_size| piece of memory, or nullptr
// if there is no memory available.
void* Pop();
// Makes |p| available to be returned by future calls to Pop.
// |p| must be the pointer most recently returned by Pop, or
// the method will ASSERT.
void Push(void* p);
// Returns true if |addr| could have been returned by Pop and has
// not been reclaimed by Push.
bool InRange(void* addr) const {
return (addr >= start_ && addr < top_);
}
// The lowest address of the memory managed by this Pool.
// Pop will only return values > |start| (besides nullptr).
char* start() const { return start_; }
// The highest address of the memory managed by this Pool.
// Pop will only return values <= |end|-|slot_size| (besides nullptr).
char* end() const { return end_; }
// Dumps information about the Pool using printf().
void Dump() const;
private:
const char name_[32] = {};
fbl::RefPtr<VmMapping> mapping_;
size_t slot_size_;
char* start_;
char* top_; // |start|..|top| contains all allocated slots.
char* committed_; // |start|..|mapped| is committed.
char* committed_max_; // Largest committed_ value seen.
char* end_; // |mapped|..|end| is not committed.
};
Pool control_; // Free list nodes
Pool data_; // Objects
// Parent VMAR of our memory.
fbl::RefPtr<VmAddressRegion> vmar_;
// Outstanding allocation count.
size_t count_;
friend class ArenaTestFriend;
};
// TypedArena Convenience wrapper that handles:
// 1- C++ type enformcement
// 2- Calls constructors and destructors
// 3- Serializes access according to the Mtx type or
// use fbl::NullMutex to use external serialization.
//
template <typename T, typename Mtx>
class TypedArena {
public:
status_t Init(const char* name, size_t max_count) TA_NO_THREAD_SAFETY_ANALYSIS {
return arena_.Init(name, sizeof(T), max_count);
}
template <typename... Args>
T* New(Args&&... args) {
mutex_.Acquire();
void* addr = arena_.Alloc();
mutex_.Release();
return addr ? new (addr) T(fbl::forward<Args>(args)...) : nullptr;
};
void Delete(T* obj) {
obj->~T();
RawFree(obj);
}
void RawFree(void* mem) {
mutex_.Acquire();
arena_.Free(mem);
mutex_.Release();
}
// Returns the number of outstanding allocations from this arena.
size_t DiagnosticCount() const {
mutex_.Acquire();
auto count = arena_.DiagnosticCount();
mutex_.Release();
return count;
}
private:
mutable Mtx mutex_;
Arena arena_ TA_GUARDED(mutex_);
};
} // namespace fbl