hoard2: Remove.
rpmalloc has now been turned on by default for over a week, and no bug reports against it have been filed. So, it's probably safe to remove this as dead code now.
This commit is contained in:
@@ -1,21 +0,0 @@
|
|||||||
SubDir HAIKU_TOP src system libroot posix malloc ;
|
|
||||||
|
|
||||||
UsePrivateHeaders libroot shared ;
|
|
||||||
|
|
||||||
local architectureObject ;
|
|
||||||
for architectureObject in [ MultiArchSubDirSetup ] {
|
|
||||||
on $(architectureObject) {
|
|
||||||
local architecture = $(TARGET_PACKAGING_ARCH) ;
|
|
||||||
|
|
||||||
UsePrivateSystemHeaders ;
|
|
||||||
|
|
||||||
MergeObject <$(architecture)>posix_malloc.o :
|
|
||||||
arch-specific.cpp
|
|
||||||
heap.cpp
|
|
||||||
processheap.cpp
|
|
||||||
superblock.cpp
|
|
||||||
threadheap.cpp
|
|
||||||
wrapper.cpp
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,379 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "arch-specific.h"
|
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
#include <OS.h>
|
|
||||||
#include <Debug.h>
|
|
||||||
#include <syscalls.h>
|
|
||||||
|
|
||||||
#include <libroot_private.h>
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
//#define TRACE_CHUNKS
|
|
||||||
#ifdef TRACE_CHUNKS
|
|
||||||
# define CTRACE(x) debug_printf x
|
|
||||||
#else
|
|
||||||
# define CTRACE(x) ;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace BPrivate;
|
|
||||||
|
|
||||||
struct free_chunk {
|
|
||||||
free_chunk *next;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static const size_t kInitialHeapSize = 64 * B_PAGE_SIZE;
|
|
||||||
// that's about what hoard allocates anyway (should be kHeapIncrement
|
|
||||||
// aligned)
|
|
||||||
|
|
||||||
static const size_t kHeapIncrement = 16 * B_PAGE_SIZE;
|
|
||||||
// the steps in which to increase the heap size (must be a power of 2)
|
|
||||||
|
|
||||||
#if B_HAIKU_64_BIT
|
|
||||||
static const addr_t kHeapReservationBase = 0x1000000000;
|
|
||||||
static const addr_t kHeapReservationSize = 0x1000000000;
|
|
||||||
#else
|
|
||||||
static const addr_t kHeapReservationBase = 0x18000000;
|
|
||||||
static const addr_t kHeapReservationSize = 0x48000000;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static area_id sHeapArea;
|
|
||||||
static hoardLockType sHeapLock;
|
|
||||||
static void *sHeapBase;
|
|
||||||
static addr_t sFreeHeapBase;
|
|
||||||
static size_t sFreeHeapSize, sHeapAreaSize;
|
|
||||||
static free_chunk *sFreeChunks;
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
__init_after_fork(void)
|
|
||||||
{
|
|
||||||
// find the heap area
|
|
||||||
sHeapArea = area_for((void*)sFreeHeapBase);
|
|
||||||
if (sHeapArea < 0) {
|
|
||||||
// Where is it gone?
|
|
||||||
debug_printf("hoard: init_after_fork(): thread %" B_PRId32 ", Heap "
|
|
||||||
"area not found! Base address: %p\n", find_thread(NULL),
|
|
||||||
sHeapBase);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" status_t
|
|
||||||
__init_heap(void)
|
|
||||||
{
|
|
||||||
hoardHeap::initNumProcs();
|
|
||||||
|
|
||||||
// This will locate the heap base at 384 MB and reserve the next 1152 MB
|
|
||||||
// for it. They may get reclaimed by other areas, though, but the maximum
|
|
||||||
// size of the heap is guaranteed until the space is really needed.
|
|
||||||
sHeapBase = (void *)kHeapReservationBase;
|
|
||||||
status_t status = _kern_reserve_address_range((addr_t *)&sHeapBase,
|
|
||||||
B_RANDOMIZED_BASE_ADDRESS, kHeapReservationSize);
|
|
||||||
if (status != B_OK)
|
|
||||||
sHeapBase = NULL;
|
|
||||||
|
|
||||||
uint32 protection = B_READ_AREA | B_WRITE_AREA;
|
|
||||||
if (__gABIVersion < B_HAIKU_ABI_GCC_2_HAIKU)
|
|
||||||
protection |= B_EXECUTE_AREA;
|
|
||||||
sHeapArea = create_area("heap", (void **)&sHeapBase,
|
|
||||||
status == B_OK ? B_EXACT_ADDRESS : B_RANDOMIZED_BASE_ADDRESS,
|
|
||||||
kInitialHeapSize, B_NO_LOCK, protection);
|
|
||||||
if (sHeapArea < B_OK)
|
|
||||||
return sHeapArea;
|
|
||||||
|
|
||||||
sFreeHeapBase = (addr_t)sHeapBase;
|
|
||||||
sHeapAreaSize = kInitialHeapSize;
|
|
||||||
|
|
||||||
hoardLockInit(sHeapLock, "heap");
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__heap_terminate_after()
|
|
||||||
{
|
|
||||||
// nothing to do
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
insert_chunk(free_chunk *newChunk)
|
|
||||||
{
|
|
||||||
free_chunk *chunk = (free_chunk *)sFreeChunks, *smaller = NULL;
|
|
||||||
for (; chunk != NULL; chunk = chunk->next) {
|
|
||||||
if (chunk->size < newChunk->size)
|
|
||||||
smaller = chunk;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (smaller) {
|
|
||||||
newChunk->next = smaller->next;
|
|
||||||
smaller->next = newChunk;
|
|
||||||
} else {
|
|
||||||
newChunk->next = sFreeChunks;
|
|
||||||
sFreeChunks = newChunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
void *
|
|
||||||
hoardSbrk(long size)
|
|
||||||
{
|
|
||||||
assert(size > 0);
|
|
||||||
CTRACE(("sbrk: size = %ld\n", size));
|
|
||||||
|
|
||||||
// align size request
|
|
||||||
size = (size + hoardHeap::ALIGNMENT - 1) & ~(hoardHeap::ALIGNMENT - 1);
|
|
||||||
|
|
||||||
// choose correct protection flags
|
|
||||||
uint32 protection = B_READ_AREA | B_WRITE_AREA;
|
|
||||||
if (__gABIVersion < B_HAIKU_ABI_GCC_2_HAIKU)
|
|
||||||
protection |= B_EXECUTE_AREA;
|
|
||||||
|
|
||||||
hoardLock(sHeapLock);
|
|
||||||
|
|
||||||
// find chunk in free list
|
|
||||||
free_chunk *chunk = sFreeChunks, *last = NULL;
|
|
||||||
for (; chunk != NULL; chunk = chunk->next) {
|
|
||||||
CTRACE((" chunk %p (%ld)\n", chunk, chunk->size));
|
|
||||||
|
|
||||||
if (chunk->size < (size_t)size) {
|
|
||||||
last = chunk;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this chunk is large enough to satisfy the request
|
|
||||||
|
|
||||||
SERIAL_PRINT(("HEAP-%ld: found free chunk to hold %ld bytes\n",
|
|
||||||
find_thread(NULL), size));
|
|
||||||
|
|
||||||
void *address = (void *)chunk;
|
|
||||||
|
|
||||||
if (chunk->size > (size_t)size + sizeof(free_chunk)) {
|
|
||||||
// divide this chunk into smaller bits
|
|
||||||
size_t newSize = chunk->size - size;
|
|
||||||
free_chunk *next = chunk->next;
|
|
||||||
|
|
||||||
chunk = (free_chunk *)((addr_t)chunk + size);
|
|
||||||
chunk->next = next;
|
|
||||||
chunk->size = newSize;
|
|
||||||
|
|
||||||
if (last != NULL) {
|
|
||||||
last->next = next;
|
|
||||||
insert_chunk(chunk);
|
|
||||||
} else
|
|
||||||
sFreeChunks = chunk;
|
|
||||||
} else {
|
|
||||||
chunk = chunk->next;
|
|
||||||
|
|
||||||
if (last != NULL)
|
|
||||||
last->next = chunk;
|
|
||||||
else
|
|
||||||
sFreeChunks = chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There was no chunk, let's see if the area is large enough
|
|
||||||
|
|
||||||
size_t oldHeapSize = sFreeHeapSize;
|
|
||||||
sFreeHeapSize += size;
|
|
||||||
|
|
||||||
// round to next heap increment aligned size
|
|
||||||
size_t incrementAlignedSize = (sFreeHeapSize + kHeapIncrement - 1)
|
|
||||||
& ~(kHeapIncrement - 1);
|
|
||||||
|
|
||||||
if (incrementAlignedSize <= sHeapAreaSize) {
|
|
||||||
SERIAL_PRINT(("HEAP-%ld: heap area large enough for %ld\n",
|
|
||||||
find_thread(NULL), size));
|
|
||||||
// the area is large enough already
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return (void *)(sFreeHeapBase + oldHeapSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to grow the area
|
|
||||||
|
|
||||||
SERIAL_PRINT(("HEAP-%ld: need to resize heap area to %ld (%ld requested)\n",
|
|
||||||
find_thread(NULL), incrementAlignedSize, size));
|
|
||||||
|
|
||||||
status_t status = resize_area(sHeapArea, incrementAlignedSize);
|
|
||||||
if (status != B_OK) {
|
|
||||||
// Either the system is out of memory or another area is in the way and
|
|
||||||
// prevents ours from being resized. As a special case of the latter
|
|
||||||
// the user might have mmap()ed something over malloc()ed memory. This
|
|
||||||
// splits the heap area in two, the first one retaining the original
|
|
||||||
// area ID. In either case, if there's still memory, it is a good idea
|
|
||||||
// to try and allocate a new area.
|
|
||||||
sFreeHeapSize = oldHeapSize;
|
|
||||||
|
|
||||||
if (status == B_NO_MEMORY) {
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t newHeapSize = (size + kHeapIncrement - 1) / kHeapIncrement
|
|
||||||
* kHeapIncrement;
|
|
||||||
|
|
||||||
// First try at the location directly after the current heap area, if
|
|
||||||
// that is still in the reserved memory region.
|
|
||||||
void* base = (void*)(sFreeHeapBase + sHeapAreaSize);
|
|
||||||
area_id area = -1;
|
|
||||||
if (sHeapBase != NULL
|
|
||||||
&& base >= sHeapBase
|
|
||||||
&& (addr_t)base + newHeapSize
|
|
||||||
<= (addr_t)sHeapBase + kHeapReservationSize) {
|
|
||||||
area = create_area("heap", &base, B_EXACT_ADDRESS, newHeapSize,
|
|
||||||
B_NO_LOCK, protection);
|
|
||||||
|
|
||||||
if (area == B_NO_MEMORY) {
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we don't have an area yet, try again with a free location
|
|
||||||
// allocation.
|
|
||||||
if (area < 0) {
|
|
||||||
base = (void*)(sFreeHeapBase + sHeapAreaSize);
|
|
||||||
area = create_area("heap", &base, B_RANDOMIZED_BASE_ADDRESS,
|
|
||||||
newHeapSize, B_NO_LOCK, protection);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (area < 0) {
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have a new area, so make it the new heap area.
|
|
||||||
sHeapArea = area;
|
|
||||||
sFreeHeapBase = (addr_t)base;
|
|
||||||
sHeapAreaSize = newHeapSize;
|
|
||||||
sFreeHeapSize = size;
|
|
||||||
oldHeapSize = 0;
|
|
||||||
} else
|
|
||||||
sHeapAreaSize = incrementAlignedSize;
|
|
||||||
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return (void *)(sFreeHeapBase + oldHeapSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardUnsbrk(void *ptr, long size)
|
|
||||||
{
|
|
||||||
CTRACE(("unsbrk: %p, %ld!\n", ptr, size));
|
|
||||||
|
|
||||||
hoardLock(sHeapLock);
|
|
||||||
|
|
||||||
// TODO: hoard always allocates and frees in typical sizes, so we could
|
|
||||||
// save a lot of effort if we just had a similar mechanism
|
|
||||||
|
|
||||||
// We add this chunk to our free list - first, try to find an adjacent
|
|
||||||
// chunk, so that we can merge them together
|
|
||||||
|
|
||||||
free_chunk *chunk = (free_chunk *)sFreeChunks, *last = NULL, *smaller = NULL;
|
|
||||||
for (; chunk != NULL; chunk = chunk->next) {
|
|
||||||
if ((addr_t)chunk + chunk->size == (addr_t)ptr
|
|
||||||
|| (addr_t)ptr + size == (addr_t)chunk) {
|
|
||||||
// chunks are adjacent - merge them
|
|
||||||
|
|
||||||
CTRACE((" found adjacent chunks: %p, %ld\n", chunk, chunk->size));
|
|
||||||
if (last)
|
|
||||||
last->next = chunk->next;
|
|
||||||
else
|
|
||||||
sFreeChunks = chunk->next;
|
|
||||||
|
|
||||||
if ((addr_t)chunk < (addr_t)ptr)
|
|
||||||
chunk->size += size;
|
|
||||||
else {
|
|
||||||
free_chunk *newChunk = (free_chunk *)ptr;
|
|
||||||
newChunk->next = chunk->next;
|
|
||||||
newChunk->size = size + chunk->size;
|
|
||||||
chunk = newChunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
insert_chunk(chunk);
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = chunk;
|
|
||||||
|
|
||||||
if (chunk->size < (size_t)size)
|
|
||||||
smaller = chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we didn't find an adjacent chunk, so insert the new chunk into the list
|
|
||||||
|
|
||||||
free_chunk *newChunk = (free_chunk *)ptr;
|
|
||||||
newChunk->size = size;
|
|
||||||
if (smaller) {
|
|
||||||
newChunk->next = smaller->next;
|
|
||||||
smaller->next = newChunk;
|
|
||||||
} else {
|
|
||||||
newChunk->next = sFreeChunks;
|
|
||||||
sFreeChunks = newChunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
hoardUnlock(sHeapLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardLockInit(hoardLockType &lock, const char *name)
|
|
||||||
{
|
|
||||||
mutex_init_etc(&lock, name, MUTEX_FLAG_ADAPTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardLock(hoardLockType &lock)
|
|
||||||
{
|
|
||||||
mutex_lock(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardUnlock(hoardLockType &lock)
|
|
||||||
{
|
|
||||||
mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardYield(void)
|
|
||||||
{
|
|
||||||
_kern_thread_yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef _ARCH_SPECIFIC_H_
|
|
||||||
#define _ARCH_SPECIFIC_H_
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <new>
|
|
||||||
|
|
||||||
#include <OS.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <locks.h>
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: some kind of adaptive mutex (i.e. trying to spin for a while before
|
|
||||||
// may be a better choice
|
|
||||||
typedef mutex hoardLockType;
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
///// Lock-related wrappers.
|
|
||||||
|
|
||||||
void hoardLockInit(hoardLockType &lock, const char *name);
|
|
||||||
void hoardLock(hoardLockType &lock);
|
|
||||||
void hoardUnlock(hoardLockType &lock);
|
|
||||||
|
|
||||||
///// Memory-related wrapper.
|
|
||||||
|
|
||||||
void *hoardSbrk(long size);
|
|
||||||
void hoardUnsbrk(void *ptr, long size);
|
|
||||||
|
|
||||||
///// Other.
|
|
||||||
|
|
||||||
void hoardYield(void);
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
|
|
||||||
#endif // _ARCH_SPECIFIC_H_
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef _BLOCK_H_
|
|
||||||
#define _BLOCK_H_
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
//#include <assert.h>
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
class superblock;
|
|
||||||
|
|
||||||
class block {
|
|
||||||
public:
|
|
||||||
block(superblock * sb)
|
|
||||||
:
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
_magic(FREE_BLOCK_MAGIC),
|
|
||||||
#endif
|
|
||||||
_next(NULL), _mySuperblock(sb)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
block &
|
|
||||||
operator=(const block & b)
|
|
||||||
{
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
_magic = b._magic;
|
|
||||||
#endif
|
|
||||||
_next = b._next;
|
|
||||||
_mySuperblock = b._mySuperblock;
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
_requestedSize = b._requestedSize;
|
|
||||||
#endif
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ALLOCATED_BLOCK_MAGIC = 0xcafecafe,
|
|
||||||
FREE_BLOCK_MAGIC = 0xbabebabe
|
|
||||||
};
|
|
||||||
|
|
||||||
// Mark this block as free.
|
|
||||||
inline void markFree(void);
|
|
||||||
|
|
||||||
// Mark this block as allocated.
|
|
||||||
inline void markAllocated(void);
|
|
||||||
|
|
||||||
// Is this block valid? (i.e.,
|
|
||||||
// does it have the right magic number?)
|
|
||||||
inline const int isValid(void) const;
|
|
||||||
|
|
||||||
// Return the block's superblock pointer.
|
|
||||||
inline superblock *getSuperblock(void);
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
void
|
|
||||||
setRequestedSize(size_t s)
|
|
||||||
{
|
|
||||||
_requestedSize = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
getRequestedSize(void)
|
|
||||||
{
|
|
||||||
return _requestedSize;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if USE_PRIVATE_HEAPS
|
|
||||||
void
|
|
||||||
setActualSize(size_t s)
|
|
||||||
{
|
|
||||||
_actualSize = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
getActualSize(void)
|
|
||||||
{
|
|
||||||
return _actualSize;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
void
|
|
||||||
setNext(block * b)
|
|
||||||
{
|
|
||||||
_next = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
block *
|
|
||||||
getNext(void)
|
|
||||||
{
|
|
||||||
return _next;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
void
|
|
||||||
setCallStack(int index, void *address)
|
|
||||||
{
|
|
||||||
_callStack[index] = address;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
getCallStack(int index)
|
|
||||||
{
|
|
||||||
return _callStack[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
setAllocatedSize(size_t size)
|
|
||||||
{
|
|
||||||
_allocatedSize = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t
|
|
||||||
getAllocatedSize()
|
|
||||||
{
|
|
||||||
return _allocatedSize;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
#if USE_PRIVATE_HEAPS
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
union {
|
|
||||||
unsigned long _magic;
|
|
||||||
double _d1; // For alignment.
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
block *_next; // The next block in a linked-list of blocks.
|
|
||||||
size_t _actualSize; // The actual size of the block.
|
|
||||||
|
|
||||||
union {
|
|
||||||
double _d2; // For alignment.
|
|
||||||
superblock *_mySuperblock; // A pointer to my superblock.
|
|
||||||
};
|
|
||||||
#else // ! USE_PRIVATE_HEAPS
|
|
||||||
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
union {
|
|
||||||
unsigned long _magic;
|
|
||||||
double _d3; // For alignment.
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
block *_next; // The next block in a linked-list of blocks.
|
|
||||||
superblock *_mySuperblock; // A pointer to my superblock.
|
|
||||||
#endif // USE_PRIVATE_HEAPS
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
void *_callStack[HEAP_CALL_STACK_SIZE];
|
|
||||||
size_t _allocatedSize;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
union {
|
|
||||||
double _d4; // This is just for alignment purposes.
|
|
||||||
size_t _requestedSize; // The amount of space requested (vs. allocated).
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Disable copying.
|
|
||||||
block(const block &);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
|
||||||
block::getSuperblock(void)
|
|
||||||
{
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
assert(isValid());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return _mySuperblock;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
block::markFree(void)
|
|
||||||
{
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
assert(_magic == ALLOCATED_BLOCK_MAGIC);
|
|
||||||
_magic = FREE_BLOCK_MAGIC;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
block::markAllocated(void)
|
|
||||||
{
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
assert(_magic == FREE_BLOCK_MAGIC);
|
|
||||||
_magic = ALLOCATED_BLOCK_MAGIC;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const int
|
|
||||||
block::isValid(void) const
|
|
||||||
{
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
return _magic == FREE_BLOCK_MAGIC
|
|
||||||
|| _magic == ALLOCATED_BLOCK_MAGIC;
|
|
||||||
#else
|
|
||||||
return 1;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
|
|
||||||
#endif // _BLOCK_H_
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
#ifndef _CONFIG_H_
|
|
||||||
#define _CONFIG_H_
|
|
||||||
|
|
||||||
#ifndef _REENTRANT
|
|
||||||
# define _REENTRANT // If defined, generate a multithreaded-capable version.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef USER_LOCKS
|
|
||||||
# define USER_LOCKS 1 // Use our own user-level locks if they're available for the current architecture.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define HEAP_LOG 0 // If non-zero, keep a log of heap accesses.
|
|
||||||
|
|
||||||
|
|
||||||
///// You should not change anything below here. /////
|
|
||||||
|
|
||||||
|
|
||||||
// The base of the exponential used for size classes.
|
|
||||||
// An object is in size class i if
|
|
||||||
// base^(b+i-1) * ALIGNMENT < size <= base^(b+i) * ALIGNMENT,
|
|
||||||
// where b = log_base(ALIGNMENT).
|
|
||||||
// Note that this puts an upper-limit on internal fragmentation:
|
|
||||||
// if SIZE_CLASS_BASE is 1.2, then we will never see more than
|
|
||||||
// 20% internal fragmentation (for aligned requests).
|
|
||||||
|
|
||||||
#define SIZE_CLASS_BASE 1.2
|
|
||||||
#define MAX_INTERNAL_FRAGMENTATION 2
|
|
||||||
|
|
||||||
// The number of groups of superblocks we maintain based on what
|
|
||||||
// fraction of the superblock is empty. NB: This number must be at
|
|
||||||
// least 2, and is 1 greater than the EMPTY_FRACTION in heap.h.
|
|
||||||
|
|
||||||
enum { SUPERBLOCK_FULLNESS_GROUP = 9 };
|
|
||||||
|
|
||||||
|
|
||||||
// DO NOT CHANGE THESE. They require running of maketable to replace
|
|
||||||
// the values in heap.cpp for the _numBlocks array.
|
|
||||||
|
|
||||||
#define HEAP_DEBUG 0 // If non-zero, keeps extra info for sanity checking.
|
|
||||||
#define HEAP_STATS 0 // If non-zero, maintain blowup statistics.
|
|
||||||
#define HEAP_FRAG_STATS 0 // If non-zero, maintain fragmentation statistics.
|
|
||||||
|
|
||||||
// A simple (and slow) leak checker
|
|
||||||
#define HEAP_LEAK_CHECK 0
|
|
||||||
#define HEAP_CALL_STACK_SIZE 8
|
|
||||||
|
|
||||||
// A simple wall checker
|
|
||||||
#define HEAP_WALL 0
|
|
||||||
#define HEAP_WALL_SIZE 32
|
|
||||||
|
|
||||||
// CACHE_LINE = The number of bytes in a cache line.
|
|
||||||
|
|
||||||
#if defined(i386) || defined(WIN32)
|
|
||||||
# define CACHE_LINE 32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef sparc
|
|
||||||
# define CACHE_LINE 64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __sgi
|
|
||||||
# define CACHE_LINE 128
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef CACHE_LINE
|
|
||||||
// We don't know what the architecture is,
|
|
||||||
// so go for the gusto.
|
|
||||||
#define CACHE_LINE 64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __GNUG__
|
|
||||||
// Use the max operator, an extension to C++ found in GNU C++.
|
|
||||||
# define MAX(a,b) ((a) >? (b))
|
|
||||||
#else
|
|
||||||
# define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#endif // _CONFIG_H_
|
|
||||||
@@ -1,460 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "heap.h"
|
|
||||||
#include "processheap.h"
|
|
||||||
#include "superblock.h"
|
|
||||||
|
|
||||||
using namespace BPrivate;
|
|
||||||
|
|
||||||
// NB: Use maketable.cpp to update this
|
|
||||||
// if SIZE_CLASSES, ALIGNMENT, SIZE_CLASS_BASE, MAX_EMPTY_SUPERBLOCKS,
|
|
||||||
// or SUPERBLOCK_SIZE changes.
|
|
||||||
|
|
||||||
#if (MAX_INTERNAL_FRAGMENTATION == 2)
|
|
||||||
|
|
||||||
size_t hoardHeap::_sizeTable[hoardHeap::SIZE_CLASSES] = {
|
|
||||||
8UL, 16UL, 24UL, 32UL, 40UL, 48UL, 56UL, 72UL, 80UL, 96UL, 120UL, 144UL,
|
|
||||||
168UL, 200UL, 240UL, 288UL, 344UL, 416UL, 496UL, 592UL, 712UL, 856UL,
|
|
||||||
1024UL, 1232UL, 1472UL, 1768UL, 2120UL, 2544UL, 3048UL, 3664UL,
|
|
||||||
4392UL, 5272UL, 6320UL, 7584UL, 9104UL, 10928UL, 13112UL, 15728UL,
|
|
||||||
18872UL, 22648UL, 27176UL, 32616UL, 39136UL, 46960UL, 56352UL,
|
|
||||||
67624UL, 81144UL, 97376UL, 116848UL, 140216UL, 168256UL, 201904UL,
|
|
||||||
242288UL, 290744UL, 348896UL, 418672UL, 502408UL, 602888UL, 723464UL,
|
|
||||||
868152UL, 1041784UL, 1250136UL, 1500160UL, 1800192UL, 2160232UL,
|
|
||||||
2592280UL, 3110736UL, 3732880UL, 4479456UL, 5375344UL, 6450408UL,
|
|
||||||
7740496UL, 9288592UL, 11146312UL, 13375568UL, 16050680UL, 19260816UL,
|
|
||||||
23112984UL, 27735576UL, 33282688UL, 39939224UL, 47927072UL,
|
|
||||||
57512488UL, 69014984UL, 82817976UL, 99381576UL, 119257888UL,
|
|
||||||
143109472UL, 171731360UL, 206077632UL, 247293152UL, 296751776UL,
|
|
||||||
356102144UL, 427322560UL, 512787072UL, 615344512UL, 738413376UL,
|
|
||||||
886096064UL, 1063315264UL
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t hoardHeap::_threshold[hoardHeap::SIZE_CLASSES] = {
|
|
||||||
4096UL, 2048UL, 1364UL, 1024UL, 816UL, 680UL, 584UL, 452UL, 408UL,
|
|
||||||
340UL, 272UL, 224UL, 192UL, 160UL, 136UL, 112UL, 92UL, 76UL, 64UL,
|
|
||||||
52UL, 44UL, 36UL, 32UL, 24UL, 20UL, 16UL, 12UL, 12UL, 8UL, 8UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL
|
|
||||||
};
|
|
||||||
|
|
||||||
#elif (MAX_INTERNAL_FRAGMENTATION == 6)
|
|
||||||
|
|
||||||
size_t hoardHeap::_sizeTable[hoardHeap::SIZE_CLASSES] = {
|
|
||||||
8UL, 16UL, 24UL, 32UL, 48UL, 72UL, 112UL, 176UL, 288UL, 456UL, 728UL,
|
|
||||||
1160UL, 1848UL, 2952UL, 4728UL, 7560UL, 12096UL, 19344UL, 30952UL,
|
|
||||||
49520UL, 79232UL, 126768UL, 202832UL, 324520UL, 519232UL, 830768UL,
|
|
||||||
1329232UL, 2126768UL, 3402824UL, 5444520UL, 8711232UL, 13937968UL,
|
|
||||||
22300752UL, 35681200UL, 57089912UL, 91343856UL, 146150176UL,
|
|
||||||
233840256UL, 374144416UL, 598631040UL, 957809728UL, 1532495488UL
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t hoardHeap::_threshold[hoardHeap::SIZE_CLASSES] = {
|
|
||||||
4096UL, 2048UL, 1364UL, 1024UL, 680UL, 452UL, 292UL, 184UL, 112UL, 68UL,
|
|
||||||
44UL, 28UL, 16UL, 8UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL
|
|
||||||
};
|
|
||||||
|
|
||||||
#elif (MAX_INTERNAL_FRAGMENTATION == 10)
|
|
||||||
|
|
||||||
size_t hoardHeap::_sizeTable[hoardHeap::SIZE_CLASSES] = {
|
|
||||||
8UL, 16UL, 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL,
|
|
||||||
8192UL, 16384UL, 32768UL, 65536UL, 131072UL, 262144UL, 524288UL,
|
|
||||||
1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL,
|
|
||||||
67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL,
|
|
||||||
2147483648UL
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t hoardHeap::_threshold[hoardHeap::SIZE_CLASSES] = {
|
|
||||||
4096UL, 2048UL, 1024UL, 512UL, 256UL, 128UL, 64UL, 32UL, 16UL, 8UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL, 4UL,
|
|
||||||
4UL, 4UL, 4UL, 4UL
|
|
||||||
};
|
|
||||||
|
|
||||||
#else
|
|
||||||
# error "Undefined size class base."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
int hoardHeap::fMaxThreadHeaps = 1;
|
|
||||||
int hoardHeap::_numProcessors;
|
|
||||||
int hoardHeap::_numProcessorsMask;
|
|
||||||
|
|
||||||
|
|
||||||
// Return ceil(log_2(num)).
|
|
||||||
// num must be positive.
|
|
||||||
|
|
||||||
static int
|
|
||||||
lg(int num)
|
|
||||||
{
|
|
||||||
assert(num > 0);
|
|
||||||
int power = 0;
|
|
||||||
int n = 1;
|
|
||||||
// Invariant: 2^power == n.
|
|
||||||
while (n < num) {
|
|
||||||
n <<= 1;
|
|
||||||
power++;
|
|
||||||
}
|
|
||||||
return power;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
|
||||||
|
|
||||||
|
|
||||||
hoardHeap::hoardHeap(void)
|
|
||||||
:
|
|
||||||
_index(0), _reusableSuperblocks(NULL), _reusableSuperblocksCount(0)
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
, _magic(HEAP_MAGIC)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
initLock();
|
|
||||||
|
|
||||||
for (int i = 0; i < SUPERBLOCK_FULLNESS_GROUP; i++) {
|
|
||||||
for (int j = 0; j < SIZE_CLASSES; j++) {
|
|
||||||
// Initialize all superblocks lists to empty.
|
|
||||||
_superblocks[i][j] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int k = 0; k < SIZE_CLASSES; k++) {
|
|
||||||
_leastEmptyBin[k] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::insertSuperblock(int sizeclass,
|
|
||||||
superblock *sb, processHeap *pHeap)
|
|
||||||
{
|
|
||||||
assert(sb->isValid());
|
|
||||||
assert(sb->getBlockSizeClass() == sizeclass);
|
|
||||||
assert(sb->getPrev() == NULL);
|
|
||||||
assert(sb->getNext() == NULL);
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
|
|
||||||
// Now it's ours.
|
|
||||||
sb->setOwner(this);
|
|
||||||
|
|
||||||
// How full is this superblock? We'll use this information to put
|
|
||||||
// it into the right 'bin'.
|
|
||||||
sb->computeFullness();
|
|
||||||
int fullness = sb->getFullness();
|
|
||||||
|
|
||||||
// Update the stats.
|
|
||||||
incStats(sizeclass, sb->getNumBlocks() - sb->getNumAvailable(),
|
|
||||||
sb->getNumBlocks());
|
|
||||||
|
|
||||||
if (fullness == 0
|
|
||||||
&& sb->getNumBlocks() > 1
|
|
||||||
&& sb->getNumBlocks() == sb->getNumAvailable()) {
|
|
||||||
// Recycle this superblock.
|
|
||||||
#if 0
|
|
||||||
removeSuperblock(sb, sizeclass);
|
|
||||||
// Update the stats.
|
|
||||||
decStats(sizeclass,
|
|
||||||
sb->getNumBlocks() - sb->getNumAvailable(), sb->getNumBlocks());
|
|
||||||
// Free it immediately.
|
|
||||||
const size_t s = sizeFromClass(sizeclass);
|
|
||||||
const int blksize = align(sizeof(block) + s);
|
|
||||||
#if HEAP_LOG
|
|
||||||
// Record the memory deallocation.
|
|
||||||
MemoryRequest m;
|
|
||||||
m.deallocate((int)sb->getNumBlocks() *
|
|
||||||
(int)sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
pHeap->getLog(getIndex()).append(m);
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
|
|
||||||
pHeap->setDeallocated(0, sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hoardUnsbrk(sb, align(sizeof(superblock) + blksize));
|
|
||||||
#else
|
|
||||||
recycle(sb);
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
// Insert it into the appropriate list.
|
|
||||||
superblock *&head = _superblocks[fullness][sizeclass];
|
|
||||||
sb->insertBefore(head);
|
|
||||||
head = sb;
|
|
||||||
assert(head->isValid());
|
|
||||||
|
|
||||||
// Reset the least-empty bin counter.
|
|
||||||
_leastEmptyBin[sizeclass] = RESET_LEAST_EMPTY_BIN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
|
||||||
hoardHeap::removeMaxSuperblock(int sizeclass)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
|
|
||||||
superblock *head = NULL;
|
|
||||||
|
|
||||||
// First check the reusable superblocks list.
|
|
||||||
|
|
||||||
head = reuse(sizeclass);
|
|
||||||
if (head) {
|
|
||||||
// We found one. Since we're removing this superblock, update the
|
|
||||||
// stats accordingly.
|
|
||||||
decStats(sizeclass,
|
|
||||||
head->getNumBlocks() - head->getNumAvailable(),
|
|
||||||
head->getNumBlocks());
|
|
||||||
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instead of finding the superblock with the most available space
|
|
||||||
// (something that would either involve a linear scan through the
|
|
||||||
// superblocks or maintaining the superblocks in sorted order), we
|
|
||||||
// just pick one that is no more than
|
|
||||||
// 1/(SUPERBLOCK_FULLNESS_GROUP-1) more full than the superblock
|
|
||||||
// with the most available space. We start with the emptiest group.
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
// Note: the last group (SUPERBLOCK_FULLNESS_GROUP - 1) is full, so
|
|
||||||
// we never need to check it. But for robustness, we leave it in.
|
|
||||||
while (i < SUPERBLOCK_FULLNESS_GROUP) {
|
|
||||||
head = _superblocks[i][sizeclass];
|
|
||||||
if (head)
|
|
||||||
break;
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!head)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Make sure that this superblock is at least 1/EMPTY_FRACTION
|
|
||||||
// empty.
|
|
||||||
assert(head->getNumAvailable() * EMPTY_FRACTION >= head->getNumBlocks());
|
|
||||||
|
|
||||||
removeSuperblock(head, sizeclass);
|
|
||||||
|
|
||||||
assert(head->isValid());
|
|
||||||
assert(head->getPrev() == NULL);
|
|
||||||
assert(head->getNext() == NULL);
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::removeSuperblock(superblock *sb, int sizeclass)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
|
|
||||||
assert(sb->isValid());
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
assert(sb->getBlockSizeClass() == sizeclass);
|
|
||||||
|
|
||||||
for (int i = 0; i < SUPERBLOCK_FULLNESS_GROUP; i++) {
|
|
||||||
if (sb == _superblocks[i][sizeclass]) {
|
|
||||||
_superblocks[i][sizeclass] = sb->getNext();
|
|
||||||
if (_superblocks[i][sizeclass] != NULL) {
|
|
||||||
assert(_superblocks[i][sizeclass]->isValid());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb->remove();
|
|
||||||
decStats(sizeclass, sb->getNumBlocks() - sb->getNumAvailable(),
|
|
||||||
sb->getNumBlocks());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::moveSuperblock(superblock *sb,
|
|
||||||
int sizeclass, int fromBin, int toBin)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(sb->isValid());
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
assert(sb->getBlockSizeClass() == sizeclass);
|
|
||||||
assert(sb->getFullness() == toBin);
|
|
||||||
|
|
||||||
// Remove the superblock from the old bin.
|
|
||||||
|
|
||||||
superblock *&oldHead = _superblocks[fromBin][sizeclass];
|
|
||||||
if (sb == oldHead) {
|
|
||||||
oldHead = sb->getNext();
|
|
||||||
if (oldHead != NULL) {
|
|
||||||
assert(oldHead->isValid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sb->remove();
|
|
||||||
|
|
||||||
// Insert the superblock into the new bin.
|
|
||||||
|
|
||||||
superblock *&newHead = _superblocks[toBin][sizeclass];
|
|
||||||
sb->insertBefore(newHead);
|
|
||||||
newHead = sb;
|
|
||||||
assert(newHead->isValid());
|
|
||||||
|
|
||||||
// Reset the least-empty bin counter.
|
|
||||||
_leastEmptyBin[sizeclass] = RESET_LEAST_EMPTY_BIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// The heap lock must be held when this procedure is called.
|
|
||||||
|
|
||||||
int
|
|
||||||
hoardHeap::freeBlock(block * &b, superblock * &sb,
|
|
||||||
int sizeclass, processHeap *pHeap)
|
|
||||||
{
|
|
||||||
assert(sb->isValid());
|
|
||||||
assert(b->isValid());
|
|
||||||
assert(this == sb->getOwner());
|
|
||||||
|
|
||||||
const int oldFullness = sb->getFullness();
|
|
||||||
sb->putBlock(b);
|
|
||||||
decUStats(sizeclass);
|
|
||||||
const int newFullness = sb->getFullness();
|
|
||||||
|
|
||||||
// Free big superblocks.
|
|
||||||
if (sb->getNumBlocks() == 1) {
|
|
||||||
removeSuperblock(sb, sizeclass);
|
|
||||||
const size_t s = sizeFromClass(sizeclass);
|
|
||||||
const int blksize = align(sizeof(block) + s);
|
|
||||||
#if HEAP_LOG
|
|
||||||
// Record the memory deallocation.
|
|
||||||
MemoryRequest m;
|
|
||||||
m.deallocate((int)sb->getNumBlocks()
|
|
||||||
* (int)sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
pHeap->getLog(getIndex()).append(m);
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
pHeap->setDeallocated(0,
|
|
||||||
sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
#endif
|
|
||||||
hoardUnsbrk(sb, align(sizeof(superblock) + blksize));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the fullness value has changed, move the superblock.
|
|
||||||
if (newFullness != oldFullness) {
|
|
||||||
moveSuperblock(sb, sizeclass, oldFullness, newFullness);
|
|
||||||
} else {
|
|
||||||
// Move the superblock to the front of its list (to reduce
|
|
||||||
// paging).
|
|
||||||
superblock *&head = _superblocks[newFullness][sizeclass];
|
|
||||||
if (sb != head) {
|
|
||||||
sb->remove();
|
|
||||||
sb->insertBefore(head);
|
|
||||||
head = sb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the superblock is now empty, recycle it.
|
|
||||||
|
|
||||||
if ((newFullness == 0) && (sb->getNumBlocks() == sb->getNumAvailable())) {
|
|
||||||
removeSuperblock(sb, sizeclass);
|
|
||||||
#if 0
|
|
||||||
// Free it immediately.
|
|
||||||
const size_t s = sizeFromClass(sizeclass);
|
|
||||||
const int blksize = align(sizeof(block) + s);
|
|
||||||
#if HEAP_LOG
|
|
||||||
// Record the memory deallocation.
|
|
||||||
MemoryRequest m;
|
|
||||||
m.deallocate((int)sb->getNumBlocks()
|
|
||||||
* (int)sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
pHeap->getLog(getIndex()).append(m);
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
pHeap->setDeallocated(0,
|
|
||||||
sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hoardUnsbrk(sb, align(sizeof(superblock) + blksize));
|
|
||||||
return 1;
|
|
||||||
#else
|
|
||||||
recycle(sb);
|
|
||||||
// Update the stats. This restores the stats to their state
|
|
||||||
// before the call to removeSuperblock, above.
|
|
||||||
incStats(sizeclass,
|
|
||||||
sb->getNumBlocks() - sb->getNumAvailable(), sb->getNumBlocks());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is the process heap, then we're done.
|
|
||||||
if (this == (hoardHeap *)pHeap)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Release a superblock, if necessary.
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check to see if the amount free exceeds the release threshold
|
|
||||||
// (two superblocks worth of blocks for a given sizeclass) and if
|
|
||||||
// the heap is sufficiently empty.
|
|
||||||
//
|
|
||||||
|
|
||||||
// We never move anything to the process heap if we're on a
|
|
||||||
// uniprocessor.
|
|
||||||
if (_numProcessors > 1) {
|
|
||||||
int inUse, allocated;
|
|
||||||
getStats(sizeclass, inUse, allocated);
|
|
||||||
if ((inUse < allocated - getReleaseThreshold(sizeclass))
|
|
||||||
&& (EMPTY_FRACTION * inUse <
|
|
||||||
EMPTY_FRACTION * allocated - allocated)) {
|
|
||||||
|
|
||||||
// We've crossed the magical threshold. Find the superblock with
|
|
||||||
// the most free blocks and give it to the process heap.
|
|
||||||
superblock *const maxSb = removeMaxSuperblock(sizeclass);
|
|
||||||
assert(maxSb != NULL);
|
|
||||||
|
|
||||||
// Update the statistics.
|
|
||||||
|
|
||||||
assert(maxSb->getNumBlocks() >= maxSb->getNumAvailable());
|
|
||||||
|
|
||||||
// Give the superblock back to the process heap.
|
|
||||||
pHeap->release(maxSb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::initNumProcs(void)
|
|
||||||
{
|
|
||||||
system_info info;
|
|
||||||
if (get_system_info(&info) != B_OK)
|
|
||||||
hoardHeap::_numProcessors = 1;
|
|
||||||
else
|
|
||||||
hoardHeap::_numProcessors = info.cpu_count;
|
|
||||||
|
|
||||||
fMaxThreadHeaps = 1 << (lg(_numProcessors) + 1);
|
|
||||||
_numProcessorsMask = fMaxThreadHeaps - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,538 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/* hoardHeap, the base class for threadHeap and processHeap. */
|
|
||||||
|
|
||||||
#ifndef _HEAP_H_
|
|
||||||
#define _HEAP_H_
|
|
||||||
|
|
||||||
#include <OS.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "arch-specific.h"
|
|
||||||
#include "superblock.h"
|
|
||||||
#include "heapstats.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
class processHeap;
|
|
||||||
|
|
||||||
class hoardHeap {
|
|
||||||
public:
|
|
||||||
hoardHeap(void);
|
|
||||||
|
|
||||||
// A superblock that holds more than one object must hold at least
|
|
||||||
// this many bytes.
|
|
||||||
enum { SUPERBLOCK_SIZE = 8192 };
|
|
||||||
|
|
||||||
// A thread heap must be at least 1/EMPTY_FRACTION empty before we
|
|
||||||
// start returning superblocks to the process heap.
|
|
||||||
enum { EMPTY_FRACTION = SUPERBLOCK_FULLNESS_GROUP - 1 };
|
|
||||||
|
|
||||||
// Reset value for the least-empty bin. The last bin
|
|
||||||
// (SUPERBLOCK_FULLNESS_GROUP-1) is for completely full superblocks,
|
|
||||||
// so we use the next-to-last bin.
|
|
||||||
enum { RESET_LEAST_EMPTY_BIN = SUPERBLOCK_FULLNESS_GROUP - 2 };
|
|
||||||
|
|
||||||
// The number of empty superblocks that we allow any thread heap to
|
|
||||||
// hold once the thread heap has fallen below 1/EMPTY_FRACTION
|
|
||||||
// empty.
|
|
||||||
enum { MAX_EMPTY_SUPERBLOCKS = EMPTY_FRACTION };
|
|
||||||
|
|
||||||
//
|
|
||||||
// The number of size classes. This combined with the
|
|
||||||
// SIZE_CLASS_BASE determine the maximum size of an object.
|
|
||||||
//
|
|
||||||
// NB: Once this is changed, you must execute maketable.cpp and put
|
|
||||||
// the generated values into heap.cpp.
|
|
||||||
|
|
||||||
#if MAX_INTERNAL_FRAGMENTATION == 2
|
|
||||||
enum { SIZE_CLASSES = 115 };
|
|
||||||
#elif MAX_INTERNAL_FRAGMENTATION == 6
|
|
||||||
enum { SIZE_CLASSES = 46 };
|
|
||||||
#elif MAX_INTERNAL_FRAGMENTATION == 10
|
|
||||||
enum { SIZE_CLASSES = 32 };
|
|
||||||
#else
|
|
||||||
# error "Undefined size class base."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Every object is aligned so that it can always hold any type.
|
|
||||||
#ifdef __x86_64__
|
|
||||||
enum { ALIGNMENT = 16 };
|
|
||||||
#else
|
|
||||||
enum { ALIGNMENT = sizeof(double) };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// ANDing with this rounds to ALIGNMENT.
|
|
||||||
enum { ALIGNMENT_MASK = ALIGNMENT - 1 };
|
|
||||||
|
|
||||||
// Used for sanity checking.
|
|
||||||
enum { HEAP_MAGIC = 0x0badcafe };
|
|
||||||
|
|
||||||
// Get the usage and allocated statistics.
|
|
||||||
inline void getStats(int sizeclass, int &U, int &A);
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
// How much is the maximum ever in use for this size class?
|
|
||||||
inline int maxInUse(int sizeclass);
|
|
||||||
|
|
||||||
// How much is the maximum memory allocated for this size class?
|
|
||||||
inline int maxAllocated(int sizeclass);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Insert a superblock into our list.
|
|
||||||
void insertSuperblock(int sizeclass, superblock *sb, processHeap *pHeap);
|
|
||||||
|
|
||||||
// Remove the superblock with the most free space.
|
|
||||||
superblock *removeMaxSuperblock(int sizeclass);
|
|
||||||
|
|
||||||
// Find an available superblock (i.e., with some space in it).
|
|
||||||
inline superblock *findAvailableSuperblock(int sizeclass,
|
|
||||||
block * &b, processHeap * pHeap);
|
|
||||||
|
|
||||||
// Lock this heap.
|
|
||||||
inline void lock(void);
|
|
||||||
|
|
||||||
// Unlock this heap.
|
|
||||||
inline void unlock(void);
|
|
||||||
|
|
||||||
// Init this heap lock.
|
|
||||||
inline void initLock(void);
|
|
||||||
|
|
||||||
// Set our index number (which heap we are).
|
|
||||||
inline void setIndex(int i);
|
|
||||||
|
|
||||||
// Get our index number (which heap we are).
|
|
||||||
inline int getIndex(void);
|
|
||||||
|
|
||||||
// Free a block into a superblock.
|
|
||||||
// This is used by processHeap::free().
|
|
||||||
// Returns 1 iff the superblock was munmapped.
|
|
||||||
int freeBlock(block * &b, superblock * &sb, int sizeclass,
|
|
||||||
processHeap * pHeap);
|
|
||||||
|
|
||||||
//// Utility functions ////
|
|
||||||
|
|
||||||
// Return the size class for a given size.
|
|
||||||
inline static int sizeClass(const size_t sz);
|
|
||||||
|
|
||||||
// Return the size corresponding to a given size class.
|
|
||||||
inline static size_t sizeFromClass(const int sizeclass);
|
|
||||||
|
|
||||||
// Return the release threshold corresponding to a given size class.
|
|
||||||
inline static int getReleaseThreshold(const int sizeclass);
|
|
||||||
|
|
||||||
// Return how many blocks of a given size class fit into a superblock.
|
|
||||||
inline static int numBlocks(const int sizeclass);
|
|
||||||
|
|
||||||
// Align a value.
|
|
||||||
inline static size_t align(const size_t sz);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Disable copying and assignment.
|
|
||||||
|
|
||||||
hoardHeap(const hoardHeap &);
|
|
||||||
const hoardHeap & operator=(const hoardHeap &);
|
|
||||||
|
|
||||||
// Recycle a superblock.
|
|
||||||
inline void recycle(superblock *);
|
|
||||||
|
|
||||||
// Reuse a superblock (if one is available).
|
|
||||||
inline superblock *reuse(int sizeclass);
|
|
||||||
|
|
||||||
// Remove a particular superblock.
|
|
||||||
void removeSuperblock(superblock *, int sizeclass);
|
|
||||||
|
|
||||||
// Move a particular superblock from one bin to another.
|
|
||||||
void moveSuperblock(superblock *,
|
|
||||||
int sizeclass, int fromBin, int toBin);
|
|
||||||
|
|
||||||
// Update memory in-use and allocated statistics.
|
|
||||||
// (*UStats = just update U.)
|
|
||||||
inline void incStats(int sizeclass, int updateU, int updateA);
|
|
||||||
inline void incUStats(int sizeclass);
|
|
||||||
|
|
||||||
inline void decStats(int sizeclass, int updateU, int updateA);
|
|
||||||
inline void decUStats(int sizeclass);
|
|
||||||
|
|
||||||
//// Members ////
|
|
||||||
|
|
||||||
// Heap statistics.
|
|
||||||
heapStats _stats[SIZE_CLASSES];
|
|
||||||
|
|
||||||
// The per-heap lock.
|
|
||||||
hoardLockType _lock;
|
|
||||||
|
|
||||||
// Which heap this is (0 = the process (global) heap).
|
|
||||||
int _index;
|
|
||||||
|
|
||||||
// Reusable superblocks.
|
|
||||||
superblock *_reusableSuperblocks;
|
|
||||||
int _reusableSuperblocksCount;
|
|
||||||
|
|
||||||
// Lists of superblocks.
|
|
||||||
superblock *_superblocks[SUPERBLOCK_FULLNESS_GROUP][SIZE_CLASSES];
|
|
||||||
|
|
||||||
// The current least-empty superblock bin.
|
|
||||||
int _leastEmptyBin[SIZE_CLASSES];
|
|
||||||
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
// For sanity checking.
|
|
||||||
const unsigned long _magic;
|
|
||||||
#else
|
|
||||||
# define _magic HEAP_MAGIC
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// The lookup table for size classes.
|
|
||||||
static size_t _sizeTable[SIZE_CLASSES];
|
|
||||||
|
|
||||||
// The lookup table for release thresholds.
|
|
||||||
static size_t _threshold[SIZE_CLASSES];
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void initNumProcs(void);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The maximum number of thread heaps we allow. (NOT the maximum
|
|
||||||
// number of threads -- Hoard imposes no such limit.) This must be
|
|
||||||
// a power of two! NB: This number is twice the maximum number of
|
|
||||||
// PROCESSORS supported by Hoard.
|
|
||||||
static int fMaxThreadHeaps;
|
|
||||||
|
|
||||||
// number of CPUs, cached
|
|
||||||
static int _numProcessors;
|
|
||||||
static int _numProcessorsMask;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::incStats(int sizeclass, int updateU, int updateA)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(updateU >= 0);
|
|
||||||
assert(updateA >= 0);
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
_stats[sizeclass].incStats(updateU, updateA);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::incUStats(int sizeclass)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
_stats[sizeclass].incUStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::decStats(int sizeclass, int updateU, int updateA)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(updateU >= 0);
|
|
||||||
assert(updateA >= 0);
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
_stats[sizeclass].decStats(updateU, updateA);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::decUStats(int sizeclass)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
_stats[sizeclass].decUStats();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::getStats(int sizeclass, int &U, int &A)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
_stats[sizeclass].getStats(U, A);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
int
|
|
||||||
hoardHeap::maxInUse(int sizeclass)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
return _stats[sizeclass].getUmax();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
hoardHeap::maxAllocated(int sizeclass)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
return _stats[sizeclass].getAmax();
|
|
||||||
}
|
|
||||||
#endif // HEAP_STATS
|
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
|
||||||
hoardHeap::findAvailableSuperblock(int sizeclass,
|
|
||||||
block * &b, processHeap * pHeap)
|
|
||||||
{
|
|
||||||
assert(this);
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
|
|
||||||
superblock *sb = NULL;
|
|
||||||
int reUsed = 0;
|
|
||||||
|
|
||||||
// Look through the superblocks, starting with the almost-full ones
|
|
||||||
// and going to the emptiest ones. The Least Empty Bin for a
|
|
||||||
// sizeclass is a conservative approximation (fixed after one
|
|
||||||
// iteration) of the first bin that has superblocks in it, starting
|
|
||||||
// with (surprise) the least-empty bin.
|
|
||||||
|
|
||||||
for (int i = _leastEmptyBin[sizeclass]; i >= 0; i--) {
|
|
||||||
sb = _superblocks[i][sizeclass];
|
|
||||||
if (sb == NULL) {
|
|
||||||
if (i == _leastEmptyBin[sizeclass]) {
|
|
||||||
// There wasn't a superblock in this bin,
|
|
||||||
// so we adjust the least empty bin.
|
|
||||||
_leastEmptyBin[sizeclass]--;
|
|
||||||
}
|
|
||||||
} else if (sb->getNumAvailable() > 0) {
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sb = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
if (sb == NULL) {
|
|
||||||
// Try to reuse a superblock.
|
|
||||||
sb = reuse(sizeclass);
|
|
||||||
if (sb) {
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
reUsed = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (sb != NULL) {
|
|
||||||
// Sanity checks:
|
|
||||||
// This superblock is 'valid'.
|
|
||||||
assert(sb->isValid());
|
|
||||||
// This superblock has the right ownership.
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
|
|
||||||
int oldFullness = sb->getFullness();
|
|
||||||
|
|
||||||
// Now get a block from the superblock.
|
|
||||||
// This superblock must have space available.
|
|
||||||
b = sb->getBlock();
|
|
||||||
assert(b != NULL);
|
|
||||||
|
|
||||||
// Update the stats.
|
|
||||||
incUStats(sizeclass);
|
|
||||||
|
|
||||||
if (reUsed) {
|
|
||||||
insertSuperblock(sizeclass, sb, pHeap);
|
|
||||||
// Fix the stats (since insert will just have incremented them
|
|
||||||
// by this amount).
|
|
||||||
decStats(sizeclass,
|
|
||||||
sb->getNumBlocks() - sb->getNumAvailable(),
|
|
||||||
sb->getNumBlocks());
|
|
||||||
} else {
|
|
||||||
// If we've crossed a fullness group,
|
|
||||||
// move the superblock.
|
|
||||||
int fullness = sb->getFullness();
|
|
||||||
|
|
||||||
if (fullness != oldFullness) {
|
|
||||||
// Move the superblock.
|
|
||||||
moveSuperblock(sb, sizeclass, oldFullness, fullness);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Either we didn't find a superblock or we did and got a block.
|
|
||||||
assert((sb == NULL) || (b != NULL));
|
|
||||||
// Either we didn't get a block or we did and we also got a superblock.
|
|
||||||
assert((b == NULL) || (sb != NULL));
|
|
||||||
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
hoardHeap::sizeClass(const size_t sz)
|
|
||||||
{
|
|
||||||
// Find the size class for a given object size
|
|
||||||
// (the smallest i such that _sizeTable[i] >= sz).
|
|
||||||
int sizeclass = 0;
|
|
||||||
while (_sizeTable[sizeclass] < sz) {
|
|
||||||
sizeclass++;
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
}
|
|
||||||
return sizeclass;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t
|
|
||||||
hoardHeap::sizeFromClass(const int sizeclass)
|
|
||||||
{
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
return _sizeTable[sizeclass];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
hoardHeap::getReleaseThreshold(const int sizeclass)
|
|
||||||
{
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
return _threshold[sizeclass];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
hoardHeap::numBlocks(const int sizeclass)
|
|
||||||
{
|
|
||||||
assert(sizeclass >= 0);
|
|
||||||
assert(sizeclass < SIZE_CLASSES);
|
|
||||||
const size_t s = sizeFromClass(sizeclass);
|
|
||||||
assert(s > 0);
|
|
||||||
const int blksize = align(sizeof(block) + s);
|
|
||||||
// Compute the number of blocks that will go into this superblock.
|
|
||||||
int nb = max_c(1, ((SUPERBLOCK_SIZE - sizeof(superblock)) / blksize));
|
|
||||||
return nb;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::lock(void)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
hoardLock(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::unlock(void)
|
|
||||||
{
|
|
||||||
assert(_magic == HEAP_MAGIC);
|
|
||||||
hoardUnlock(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::initLock(void)
|
|
||||||
{
|
|
||||||
// Initialize the per-heap lock.
|
|
||||||
hoardLockInit(_lock, "hoard heap");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t
|
|
||||||
hoardHeap::align(const size_t sz)
|
|
||||||
{
|
|
||||||
// Align sz up to the nearest multiple of ALIGNMENT.
|
|
||||||
// This is much faster than using multiplication
|
|
||||||
// and division.
|
|
||||||
return (sz + ALIGNMENT_MASK) & ~ALIGNMENT_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::setIndex(int i)
|
|
||||||
{
|
|
||||||
_index = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
hoardHeap::getIndex(void)
|
|
||||||
{
|
|
||||||
return _index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
hoardHeap::recycle(superblock *sb)
|
|
||||||
{
|
|
||||||
assert(sb != NULL);
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
assert(sb->getNumBlocks() > 1);
|
|
||||||
assert(sb->getNext() == NULL);
|
|
||||||
assert(sb->getPrev() == NULL);
|
|
||||||
assert(hoardHeap::numBlocks(sb->getBlockSizeClass()) > 1);
|
|
||||||
sb->insertBefore(_reusableSuperblocks);
|
|
||||||
_reusableSuperblocks = sb;
|
|
||||||
++_reusableSuperblocksCount;
|
|
||||||
// printf ("count: %d => %d\n", getIndex(), _reusableSuperblocksCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
|
||||||
hoardHeap::reuse(int sizeclass)
|
|
||||||
{
|
|
||||||
if (_reusableSuperblocks == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Make sure that we aren't using a sizeclass
|
|
||||||
// that is too big for a 'normal' superblock.
|
|
||||||
if (hoardHeap::numBlocks(sizeclass) <= 1)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Pop off a superblock from the reusable-superblock list.
|
|
||||||
assert(_reusableSuperblocksCount > 0);
|
|
||||||
superblock *sb = _reusableSuperblocks;
|
|
||||||
_reusableSuperblocks = sb->getNext();
|
|
||||||
sb->remove();
|
|
||||||
assert(sb->getNumBlocks() > 1);
|
|
||||||
--_reusableSuperblocksCount;
|
|
||||||
|
|
||||||
// Reformat the superblock if necessary.
|
|
||||||
if (sb->getBlockSizeClass() != sizeclass) {
|
|
||||||
decStats(sb->getBlockSizeClass(),
|
|
||||||
sb->getNumBlocks() - sb->getNumAvailable(),
|
|
||||||
sb->getNumBlocks());
|
|
||||||
|
|
||||||
sb = new((char *)sb) superblock(numBlocks(sizeclass),
|
|
||||||
sizeclass, this);
|
|
||||||
|
|
||||||
incStats(sizeclass,
|
|
||||||
sb->getNumBlocks() - sb->getNumAvailable(),
|
|
||||||
sb->getNumBlocks());
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(sb->getOwner() == this);
|
|
||||||
assert(sb->getBlockSizeClass() == sizeclass);
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
|
|
||||||
#endif // _HEAP_H_
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
#ifndef _HEAPSTATS_H_
|
|
||||||
#define _HEAPSTATS_H_
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
//#include <stdio.h>
|
|
||||||
//#include <assert.h>
|
|
||||||
|
|
||||||
|
|
||||||
class heapStats {
|
|
||||||
public:
|
|
||||||
heapStats(void)
|
|
||||||
: U(0), A(0)
|
|
||||||
#if HEAP_STATS
|
|
||||||
, Umax(0), Amax(0)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const heapStats & operator=(const heapStats & p);
|
|
||||||
|
|
||||||
inline void incStats(int updateU, int updateA);
|
|
||||||
inline void incUStats(void);
|
|
||||||
|
|
||||||
inline void decStats(int updateU, int updateA);
|
|
||||||
inline void decUStats(void);
|
|
||||||
inline void decUStats(int &Uout, int &Aout);
|
|
||||||
|
|
||||||
inline void getStats(int &Uout, int &Aout);
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
inline int getUmax(void);
|
|
||||||
inline int getAmax(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
// U and A *must* be the first items in this class --
|
|
||||||
// we will depend on this to atomically update them.
|
|
||||||
|
|
||||||
int U; // Memory in use.
|
|
||||||
int A; // Memory allocated.
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
int Umax;
|
|
||||||
int Amax;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
inline void
|
|
||||||
heapStats::incStats(int updateU, int updateA)
|
|
||||||
{
|
|
||||||
assert(updateU >= 0);
|
|
||||||
assert(updateA >= 0);
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
U += updateU;
|
|
||||||
A += updateA;
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
Amax = MAX(Amax, A);
|
|
||||||
Umax = MAX(Umax, U);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void
|
|
||||||
heapStats::incUStats(void)
|
|
||||||
{
|
|
||||||
assert(U < A);
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
U++;
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
Umax = MAX(Umax, U);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void
|
|
||||||
heapStats::decStats(int updateU, int updateA)
|
|
||||||
{
|
|
||||||
assert(updateU >= 0);
|
|
||||||
assert(updateA >= 0);
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U >= updateU);
|
|
||||||
assert(A >= updateA);
|
|
||||||
U -= updateU;
|
|
||||||
A -= updateA;
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void
|
|
||||||
heapStats::decUStats(int &Uout, int &Aout)
|
|
||||||
{
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U > 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
U--;
|
|
||||||
Uout = U;
|
|
||||||
Aout = A;
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void
|
|
||||||
heapStats::decUStats(void)
|
|
||||||
{
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U > 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
U--;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline void
|
|
||||||
heapStats::getStats(int &Uout, int &Aout)
|
|
||||||
{
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
Uout = U;
|
|
||||||
Aout = A;
|
|
||||||
assert(U <= A);
|
|
||||||
assert(U >= 0);
|
|
||||||
assert(A >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_STATS
|
|
||||||
inline int
|
|
||||||
heapStats::getUmax(void)
|
|
||||||
{
|
|
||||||
return Umax;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline int
|
|
||||||
heapStats::getAmax(void)
|
|
||||||
{
|
|
||||||
return Amax;
|
|
||||||
}
|
|
||||||
#endif // HEAP_STATS
|
|
||||||
|
|
||||||
#endif // _HEAPSTATS_H_
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#if USE_PRIVATE_HEAPS
|
|
||||||
# include "privateheap.h"
|
|
||||||
# define HEAPTYPE privateHeap
|
|
||||||
#else
|
|
||||||
# define HEAPTYPE threadHeap
|
|
||||||
# include "threadheap.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "processheap.h"
|
|
||||||
|
|
||||||
using namespace BPrivate;
|
|
||||||
|
|
||||||
|
|
||||||
processHeap::processHeap()
|
|
||||||
:
|
|
||||||
theap((HEAPTYPE*)hoardSbrk(sizeof(HEAPTYPE) * fMaxThreadHeaps)),
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
_currentAllocated(0),
|
|
||||||
_currentRequested(0),
|
|
||||||
_maxAllocated(0),
|
|
||||||
_inUseAtMaxAllocated(0),
|
|
||||||
_maxRequested(0),
|
|
||||||
#endif
|
|
||||||
#if HEAP_LOG
|
|
||||||
_log((Log<MemoryRequest>*)
|
|
||||||
hoardSbrk(sizeof(Log<MemoryRequest>) * (fMaxThreadHeaps + 1))),
|
|
||||||
#endif
|
|
||||||
_buffer(NULL),
|
|
||||||
_bufferCount(0)
|
|
||||||
{
|
|
||||||
if (theap == NULL)
|
|
||||||
return;
|
|
||||||
new(theap) HEAPTYPE[fMaxThreadHeaps];
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
if (_log == NULL)
|
|
||||||
return;
|
|
||||||
new(_log) Log<MemoryRequest>[fMaxThreadHeaps + 1];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int i;
|
|
||||||
// The process heap is heap 0.
|
|
||||||
setIndex(0);
|
|
||||||
for (i = 0; i < fMaxThreadHeaps; i++) {
|
|
||||||
// Set every thread's process heap to this one.
|
|
||||||
theap[i].setpHeap(this);
|
|
||||||
// Set every thread heap's index.
|
|
||||||
theap[i].setIndex(i + 1);
|
|
||||||
}
|
|
||||||
#if HEAP_LOG
|
|
||||||
for (i = 0; i < fMaxThreadHeaps + 1; i++) {
|
|
||||||
char fname[255];
|
|
||||||
sprintf(fname, "log%d", i);
|
|
||||||
unlink(fname);
|
|
||||||
_log[i].open(fname);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
hoardLockInit(_statsLock, "hoard stats");
|
|
||||||
#endif
|
|
||||||
hoardLockInit(_bufferLock, "hoard buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Print out statistics information.
|
|
||||||
void
|
|
||||||
processHeap::stats(void)
|
|
||||||
{
|
|
||||||
#if HEAP_STATS
|
|
||||||
int umax = 0;
|
|
||||||
int amax = 0;
|
|
||||||
for (int j = 0; j < fMaxThreadHeaps; j++) {
|
|
||||||
for (int i = 0; i < SIZE_CLASSES; i++) {
|
|
||||||
amax += theap[j].maxAllocated(i) * sizeFromClass(i);
|
|
||||||
umax += theap[j].maxInUse(i) * sizeFromClass(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("Amax <= %d, Umax <= %d\n", amax, umax);
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
amax = getMaxAllocated();
|
|
||||||
umax = getMaxRequested();
|
|
||||||
printf
|
|
||||||
("Maximum allocated = %d\nMaximum in use = %d\nIn use at max allocated = %d\n",
|
|
||||||
amax, umax, getInUseAtMaxAllocated());
|
|
||||||
printf("Still in use = %d\n", _currentRequested);
|
|
||||||
printf("Fragmentation (3) = %f\n",
|
|
||||||
(float)amax / (float)getInUseAtMaxAllocated());
|
|
||||||
printf("Fragmentation (4) = %f\n", (float)amax / (float)umax);
|
|
||||||
#endif
|
|
||||||
#endif // HEAP_STATS
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
printf("closing logs.\n");
|
|
||||||
fflush(stdout);
|
|
||||||
for (int i = 0; i < fMaxThreadHeaps + 1; i++) {
|
|
||||||
_log[i].close();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
void
|
|
||||||
processHeap::setAllocated(int requestedSize, int actualSize)
|
|
||||||
{
|
|
||||||
hoardLock(_statsLock);
|
|
||||||
_currentRequested += requestedSize;
|
|
||||||
_currentAllocated += actualSize;
|
|
||||||
if (_currentRequested > _maxRequested) {
|
|
||||||
_maxRequested = _currentRequested;
|
|
||||||
}
|
|
||||||
if (_currentAllocated > _maxAllocated) {
|
|
||||||
_maxAllocated = _currentAllocated;
|
|
||||||
_inUseAtMaxAllocated = _currentRequested;
|
|
||||||
}
|
|
||||||
hoardUnlock(_statsLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
processHeap::setDeallocated(int requestedSize, int actualSize)
|
|
||||||
{
|
|
||||||
hoardLock(_statsLock);
|
|
||||||
_currentRequested -= requestedSize;
|
|
||||||
_currentAllocated -= actualSize;
|
|
||||||
hoardUnlock(_statsLock);
|
|
||||||
}
|
|
||||||
#endif // HEAP_FRAG_STATS
|
|
||||||
|
|
||||||
|
|
||||||
// free (ptr, pheap):
|
|
||||||
// inputs: a pointer to an object allocated by malloc().
|
|
||||||
// side effects: returns the block to the object's superblock;
|
|
||||||
// updates the thread heap's statistics;
|
|
||||||
// may release the superblock to the process heap.
|
|
||||||
|
|
||||||
void
|
|
||||||
processHeap::free(void *ptr)
|
|
||||||
{
|
|
||||||
// Return if ptr is 0.
|
|
||||||
// This is the behavior prescribed by the standard.
|
|
||||||
if (ptr == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Find the block and superblock corresponding to this ptr.
|
|
||||||
|
|
||||||
block *b = (block *) ptr - 1;
|
|
||||||
assert(b->isValid());
|
|
||||||
|
|
||||||
// Check to see if this block came from a memalign() call.
|
|
||||||
if (((unsigned long)b->getNext() & 1) == 1) {
|
|
||||||
// It did. Set the block to the actual block header.
|
|
||||||
b = (block *) ((unsigned long)b->getNext() & ~1);
|
|
||||||
assert(b->isValid());
|
|
||||||
}
|
|
||||||
|
|
||||||
b->markFree();
|
|
||||||
|
|
||||||
superblock *sb = b->getSuperblock();
|
|
||||||
assert(sb);
|
|
||||||
assert(sb->isValid());
|
|
||||||
|
|
||||||
const int sizeclass = sb->getBlockSizeClass();
|
|
||||||
|
|
||||||
//
|
|
||||||
// Return the block to the superblock,
|
|
||||||
// find the heap that owns this superblock
|
|
||||||
// and update its statistics.
|
|
||||||
//
|
|
||||||
|
|
||||||
hoardHeap *owner;
|
|
||||||
|
|
||||||
// By acquiring the up lock on the superblock,
|
|
||||||
// we prevent it from moving to the global heap.
|
|
||||||
// This eventually pins it down in one heap,
|
|
||||||
// so this loop is guaranteed to terminate.
|
|
||||||
// (It should generally take no more than two iterations.)
|
|
||||||
sb->upLock();
|
|
||||||
while (1) {
|
|
||||||
owner = sb->getOwner();
|
|
||||||
owner->lock();
|
|
||||||
if (owner == sb->getOwner()) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
owner->unlock();
|
|
||||||
}
|
|
||||||
// Suspend to allow ownership to quiesce.
|
|
||||||
hoardYield();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
MemoryRequest m;
|
|
||||||
m.free(ptr);
|
|
||||||
getLog(owner->getIndex()).append(m);
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
setDeallocated(b->getRequestedSize(), 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int sbUnmapped = owner->freeBlock(b, sb, sizeclass, this);
|
|
||||||
|
|
||||||
owner->unlock();
|
|
||||||
if (!sbUnmapped)
|
|
||||||
sb->upUnlock();
|
|
||||||
}
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/* We use one processHeap for the whole program. */
|
|
||||||
|
|
||||||
#ifndef _PROCESSHEAP_H_
|
|
||||||
#define _PROCESSHEAP_H_
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "arch-specific.h"
|
|
||||||
#include "heap.h"
|
|
||||||
#if USE_PRIVATE_HEAPS
|
|
||||||
# include "privateheap.h"
|
|
||||||
# define HEAPTYPE privateHeap
|
|
||||||
#else
|
|
||||||
# define HEAPTYPE threadHeap
|
|
||||||
# include "threadheap.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
# include "memstat.h"
|
|
||||||
# include "log.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
class processHeap : public hoardHeap {
|
|
||||||
public:
|
|
||||||
// Always grab at least this many superblocks' worth of memory which
|
|
||||||
// we parcel out.
|
|
||||||
enum { REFILL_NUMBER_OF_SUPERBLOCKS = 16 };
|
|
||||||
|
|
||||||
processHeap();
|
|
||||||
~processHeap(void)
|
|
||||||
{
|
|
||||||
#if HEAP_STATS
|
|
||||||
stats();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
// Memory deallocation routines.
|
|
||||||
void free(void *ptr);
|
|
||||||
|
|
||||||
// Print out statistics information.
|
|
||||||
void stats(void);
|
|
||||||
|
|
||||||
// Get a thread heap index.
|
|
||||||
inline int getHeapIndex(void);
|
|
||||||
|
|
||||||
// Get thread heap max.
|
|
||||||
inline int getMaxThreadHeaps(void);
|
|
||||||
|
|
||||||
// Get the thread heap with index i.
|
|
||||||
inline HEAPTYPE & getHeap(int i);
|
|
||||||
|
|
||||||
// Extract a superblock.
|
|
||||||
inline superblock *acquire(const int c, hoardHeap * dest);
|
|
||||||
|
|
||||||
// Get space for a superblock.
|
|
||||||
inline char *getSuperblockBuffer(void);
|
|
||||||
|
|
||||||
// Insert a superblock.
|
|
||||||
inline void release(superblock * sb);
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
// Get the log for index i.
|
|
||||||
inline Log < MemoryRequest > &getLog(int i);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
// Declare that we have allocated an object.
|
|
||||||
void setAllocated(int requestedSize, int actualSize);
|
|
||||||
|
|
||||||
// Declare that we have deallocated an object.
|
|
||||||
void setDeallocated(int requestedSize, int actualSize);
|
|
||||||
|
|
||||||
// Return the number of wasted bytes at the high-water mark
|
|
||||||
// (maxAllocated - maxRequested)
|
|
||||||
inline int getFragmentation(void);
|
|
||||||
|
|
||||||
int
|
|
||||||
getMaxAllocated(void)
|
|
||||||
{
|
|
||||||
return _maxAllocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getInUseAtMaxAllocated(void)
|
|
||||||
{
|
|
||||||
return _inUseAtMaxAllocated;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
getMaxRequested(void)
|
|
||||||
{
|
|
||||||
return _maxRequested;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Hide the lock & unlock methods.
|
|
||||||
void
|
|
||||||
lock(void)
|
|
||||||
{
|
|
||||||
hoardHeap::lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
unlock(void)
|
|
||||||
{
|
|
||||||
hoardHeap::unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent copying and assignment.
|
|
||||||
processHeap(const processHeap &);
|
|
||||||
const processHeap & operator=(const processHeap &);
|
|
||||||
|
|
||||||
// The per-thread heaps.
|
|
||||||
HEAPTYPE* theap;
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
// Statistics required to compute fragmentation. We cannot
|
|
||||||
// unintrusively keep track of these on a multiprocessor, because
|
|
||||||
// this would become a bottleneck.
|
|
||||||
|
|
||||||
int _currentAllocated;
|
|
||||||
int _currentRequested;
|
|
||||||
int _maxAllocated;
|
|
||||||
int _maxRequested;
|
|
||||||
int _inUseAtMaxAllocated;
|
|
||||||
int _fragmentation;
|
|
||||||
|
|
||||||
// A lock to protect these statistics.
|
|
||||||
hoardLockType _statsLock;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
Log < MemoryRequest >* _log;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// A lock for the superblock buffer.
|
|
||||||
hoardLockType _bufferLock;
|
|
||||||
|
|
||||||
char *_buffer;
|
|
||||||
int _bufferCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
HEAPTYPE &
|
|
||||||
processHeap::getHeap(int i)
|
|
||||||
{
|
|
||||||
assert(theap != NULL);
|
|
||||||
assert(i >= 0);
|
|
||||||
assert(i < fMaxThreadHeaps);
|
|
||||||
return theap[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
Log<MemoryRequest > &
|
|
||||||
processHeap::getLog(int i)
|
|
||||||
{
|
|
||||||
assert(_log != NULL);
|
|
||||||
assert(i >= 0);
|
|
||||||
assert(i < fMaxThreadHeaps + 1);
|
|
||||||
return _log[i];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
// Hash out the thread id to a heap and return an index to that heap.
|
|
||||||
|
|
||||||
int
|
|
||||||
processHeap::getHeapIndex(void)
|
|
||||||
{
|
|
||||||
// Here we use the number of processors as the maximum number of heaps.
|
|
||||||
// In fact, for efficiency, we just round up to the highest power of two,
|
|
||||||
// times two.
|
|
||||||
int tid = find_thread(NULL) & _numProcessorsMask;
|
|
||||||
assert(tid < fMaxThreadHeaps);
|
|
||||||
return tid;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Return the maximum number of heaps.
|
|
||||||
|
|
||||||
int
|
|
||||||
processHeap::getMaxThreadHeaps(void)
|
|
||||||
{
|
|
||||||
return fMaxThreadHeaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
|
||||||
processHeap::acquire(const int sizeclass, hoardHeap * dest)
|
|
||||||
{
|
|
||||||
lock();
|
|
||||||
|
|
||||||
// Remove the superblock with the most free space.
|
|
||||||
superblock *maxSb = removeMaxSuperblock(sizeclass);
|
|
||||||
if (maxSb)
|
|
||||||
maxSb->setOwner(dest);
|
|
||||||
|
|
||||||
unlock();
|
|
||||||
|
|
||||||
return maxSb;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline char *
|
|
||||||
processHeap::getSuperblockBuffer(void)
|
|
||||||
{
|
|
||||||
char *buf;
|
|
||||||
hoardLock(_bufferLock);
|
|
||||||
if (_bufferCount == 0) {
|
|
||||||
_buffer = (char *)hoardSbrk(SUPERBLOCK_SIZE
|
|
||||||
* REFILL_NUMBER_OF_SUPERBLOCKS);
|
|
||||||
_bufferCount = REFILL_NUMBER_OF_SUPERBLOCKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf = _buffer;
|
|
||||||
_buffer += SUPERBLOCK_SIZE;
|
|
||||||
_bufferCount--;
|
|
||||||
hoardUnlock(_bufferLock);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Put a superblock back into our list of superblocks.
|
|
||||||
|
|
||||||
void
|
|
||||||
processHeap::release(superblock *sb)
|
|
||||||
{
|
|
||||||
assert(EMPTY_FRACTION * sb->getNumAvailable() > sb->getNumBlocks());
|
|
||||||
|
|
||||||
lock();
|
|
||||||
|
|
||||||
// Insert the superblock.
|
|
||||||
insertSuperblock(sb->getBlockSizeClass(), sb, this);
|
|
||||||
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
|
|
||||||
#endif // _PROCESSHEAP_H_
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// The Hoard Multiprocessor Memory Allocator
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*
|
|
||||||
superblock.cpp
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
The superblock class controls a number of blocks (which are
|
|
||||||
allocatable units of memory).
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
Emery Berger | <http://www.cs.utexas.edu/users/emery>
|
|
||||||
Department of Computer Sciences | <http://www.cs.utexas.edu>
|
|
||||||
University of Texas at Austin | <http://www.utexas.edu>
|
|
||||||
========================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "arch-specific.h"
|
|
||||||
#include "config.h"
|
|
||||||
#include "heap.h"
|
|
||||||
#include "processheap.h"
|
|
||||||
#include "superblock.h"
|
|
||||||
|
|
||||||
using namespace BPrivate;
|
|
||||||
|
|
||||||
|
|
||||||
superblock::superblock(int numBlocks, // The number of blocks in the sb.
|
|
||||||
int szclass, // The size class of the blocks.
|
|
||||||
hoardHeap * o) // The heap that "owns" this sb.
|
|
||||||
:
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
_magic(SUPERBLOCK_MAGIC),
|
|
||||||
#endif
|
|
||||||
_sizeClass(szclass),
|
|
||||||
_numBlocks(numBlocks),
|
|
||||||
_numAvailable(0),
|
|
||||||
_fullness(0), _freeList(NULL), _owner(o), _next(NULL), _prev(NULL)
|
|
||||||
{
|
|
||||||
assert(_numBlocks >= 1);
|
|
||||||
|
|
||||||
// Determine the size of each block.
|
|
||||||
const int blksize = hoardHeap::align(sizeof(block)
|
|
||||||
+ hoardHeap::sizeFromClass(_sizeClass));
|
|
||||||
|
|
||||||
// Make sure this size is in fact aligned.
|
|
||||||
assert((blksize & hoardHeap::ALIGNMENT_MASK) == 0);
|
|
||||||
|
|
||||||
// Set the first block to just past this superblock header.
|
|
||||||
block *b = (block *) hoardHeap::align((unsigned long)(this + 1));
|
|
||||||
|
|
||||||
// Initialize all the blocks,
|
|
||||||
// and insert the block pointers into the linked list.
|
|
||||||
for (int i = 0; i < _numBlocks; i++) {
|
|
||||||
// Make sure the block is on a double-word boundary.
|
|
||||||
assert(((unsigned long)b & hoardHeap::ALIGNMENT_MASK) == 0);
|
|
||||||
new(b) block(this);
|
|
||||||
assert(b->getSuperblock() == this);
|
|
||||||
b->setNext(_freeList);
|
|
||||||
_freeList = b;
|
|
||||||
b = (block *)((char *)b + blksize);
|
|
||||||
}
|
|
||||||
|
|
||||||
_numAvailable = _numBlocks;
|
|
||||||
computeFullness();
|
|
||||||
assert((unsigned long)b <= hoardHeap::align(sizeof(superblock) + blksize * _numBlocks)
|
|
||||||
+ (unsigned long)this);
|
|
||||||
|
|
||||||
hoardLockInit(_upLock, "hoard superblock");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
superblock *
|
|
||||||
superblock::makeSuperblock(int sizeclass, processHeap *pHeap)
|
|
||||||
{
|
|
||||||
// We need to get more memory.
|
|
||||||
|
|
||||||
char *buf;
|
|
||||||
int numBlocks = hoardHeap::numBlocks(sizeclass);
|
|
||||||
|
|
||||||
// Compute how much memory we need.
|
|
||||||
unsigned long moreMemory;
|
|
||||||
if (numBlocks > 1) {
|
|
||||||
moreMemory = hoardHeap::SUPERBLOCK_SIZE;
|
|
||||||
assert(moreMemory >= hoardHeap::align(sizeof(superblock)
|
|
||||||
+ (hoardHeap::align(sizeof(block)
|
|
||||||
+ hoardHeap::sizeFromClass(sizeclass))) * numBlocks));
|
|
||||||
|
|
||||||
// Get some memory from the process heap.
|
|
||||||
buf = (char *)pHeap->getSuperblockBuffer();
|
|
||||||
} else {
|
|
||||||
// One object.
|
|
||||||
assert(numBlocks == 1);
|
|
||||||
|
|
||||||
size_t blksize = hoardHeap::align(sizeof(block)
|
|
||||||
+ hoardHeap::sizeFromClass(sizeclass));
|
|
||||||
moreMemory = hoardHeap::align(sizeof(superblock) + blksize);
|
|
||||||
|
|
||||||
// Get space from the system.
|
|
||||||
buf = (char *)hoardSbrk(moreMemory);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we actually got the memory.
|
|
||||||
if (buf == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
buf = (char *)hoardHeap::align((unsigned long)buf);
|
|
||||||
|
|
||||||
// Make sure this buffer is double-word aligned.
|
|
||||||
assert(buf == (char *)hoardHeap::align((unsigned long)buf));
|
|
||||||
assert((((unsigned long)buf) & hoardHeap::ALIGNMENT_MASK) == 0);
|
|
||||||
|
|
||||||
// Instantiate the new superblock in the buffer.
|
|
||||||
return new(buf) superblock(numBlocks, sizeclass, NULL);
|
|
||||||
}
|
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/*
|
|
||||||
superblock.h
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
The superblock class controls a number of blocks (which are
|
|
||||||
allocatable units of memory).
|
|
||||||
------------------------------------------------------------------------
|
|
||||||
Emery Berger | <http://www.cs.utexas.edu/users/emery>
|
|
||||||
Department of Computer Sciences | <http://www.cs.utexas.edu>
|
|
||||||
University of Texas at Austin | <http://www.utexas.edu>
|
|
||||||
========================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SUPERBLOCK_H_
|
|
||||||
#define _SUPERBLOCK_H_
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "arch-specific.h"
|
|
||||||
#include "block.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
class hoardHeap; // forward declaration
|
|
||||||
class processHeap; // forward declaration
|
|
||||||
|
|
||||||
class superblock {
|
|
||||||
public:
|
|
||||||
// Construct a superblock for a given size class and set the heap
|
|
||||||
// owner.
|
|
||||||
superblock(int numblocks, int sizeclass, hoardHeap *owner);
|
|
||||||
~superblock(void) {}
|
|
||||||
|
|
||||||
// Make (allocate or re-use) a superblock for a given size class.
|
|
||||||
static superblock *makeSuperblock(int sizeclass, processHeap *pHeap);
|
|
||||||
|
|
||||||
// Find out who allocated this superblock.
|
|
||||||
inline hoardHeap *getOwner(void);
|
|
||||||
|
|
||||||
// Set the superblock's owner.
|
|
||||||
inline void setOwner(hoardHeap *o);
|
|
||||||
|
|
||||||
// Get a block from the superblock.
|
|
||||||
inline block *getBlock(void);
|
|
||||||
|
|
||||||
// Put a block back in the superblock.
|
|
||||||
inline void putBlock(block *b);
|
|
||||||
|
|
||||||
// How many blocks are available?
|
|
||||||
inline int getNumAvailable(void);
|
|
||||||
|
|
||||||
// How many blocks are there, in total?
|
|
||||||
inline int getNumBlocks(void);
|
|
||||||
|
|
||||||
// What size class are blocks in this superblock?
|
|
||||||
inline int getBlockSizeClass(void);
|
|
||||||
|
|
||||||
// Insert this superblock before the next one.
|
|
||||||
inline void insertBefore(superblock *nextSb);
|
|
||||||
|
|
||||||
// Return the next pointer (to the next superblock in the list).
|
|
||||||
inline superblock *const getNext(void);
|
|
||||||
|
|
||||||
// Return the prev pointer (to the previous superblock in the list).
|
|
||||||
inline superblock *const getPrev(void);
|
|
||||||
|
|
||||||
// Compute the 'fullness' of this superblock.
|
|
||||||
inline void computeFullness(void);
|
|
||||||
|
|
||||||
// Return the 'fullness' of this superblock.
|
|
||||||
inline int getFullness(void);
|
|
||||||
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
// Return the amount of waste in every allocated block.
|
|
||||||
int getMaxInternalFragmentation(void);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Remove this superblock from its linked list.
|
|
||||||
inline void remove(void);
|
|
||||||
|
|
||||||
// Is this superblock valid? (i.e.,
|
|
||||||
// does it have the right magic number?)
|
|
||||||
inline int isValid(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
upLock(void)
|
|
||||||
{
|
|
||||||
hoardLock(_upLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
upUnlock(void)
|
|
||||||
{
|
|
||||||
hoardUnlock(_upLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Disable copying and assignment.
|
|
||||||
|
|
||||||
superblock(const superblock &);
|
|
||||||
const superblock & operator=(const superblock &);
|
|
||||||
|
|
||||||
// Used for sanity checking.
|
|
||||||
enum { SUPERBLOCK_MAGIC = 0xCAFEBABE };
|
|
||||||
|
|
||||||
#if HEAP_DEBUG
|
|
||||||
unsigned long _magic;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const int _sizeClass; // The size class of blocks in the superblock.
|
|
||||||
const int _numBlocks; // The number of blocks in the superblock.
|
|
||||||
int _numAvailable; // The number of blocks available.
|
|
||||||
int _fullness; // How full is this superblock?
|
|
||||||
// (which SUPERBLOCK_FULLNESS group is it in)
|
|
||||||
block *_freeList; // A pointer to the first free block.
|
|
||||||
hoardHeap *_owner; // The heap who owns this superblock.
|
|
||||||
superblock *_next; // The next superblock in the list.
|
|
||||||
superblock *_prev; // The previous superblock in the list.
|
|
||||||
|
|
||||||
hoardLockType _upLock; // Lock this when moving a superblock to the global (process) heap.
|
|
||||||
|
|
||||||
// We insert a cache pad here to prevent false sharing with the
|
|
||||||
// first block (which immediately follows the superblock).
|
|
||||||
double _pad[CACHE_LINE / sizeof(double)];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
hoardHeap *
|
|
||||||
superblock::getOwner(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
hoardHeap *o = _owner;
|
|
||||||
return o;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
superblock::setOwner(hoardHeap *o)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
_owner = o;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
block *
|
|
||||||
superblock::getBlock(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
// Pop off a block from this superblock's freelist,
|
|
||||||
// if there is one available.
|
|
||||||
if (_freeList == NULL) {
|
|
||||||
// The freelist is empty.
|
|
||||||
assert(getNumAvailable() == 0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(getNumAvailable() > 0);
|
|
||||||
block *b = _freeList;
|
|
||||||
_freeList = _freeList->getNext();
|
|
||||||
_numAvailable--;
|
|
||||||
|
|
||||||
b->setNext(NULL);
|
|
||||||
|
|
||||||
computeFullness();
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
superblock::putBlock(block *b)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
// Push a block onto the superblock's freelist.
|
|
||||||
assert(b->isValid());
|
|
||||||
assert(b->getSuperblock() == this);
|
|
||||||
assert(getNumAvailable() < getNumBlocks());
|
|
||||||
b->setNext(_freeList);
|
|
||||||
_freeList = b;
|
|
||||||
_numAvailable++;
|
|
||||||
computeFullness();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
superblock::getNumAvailable(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
return _numAvailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
superblock::getNumBlocks(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
return _numBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
superblock::getBlockSizeClass(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
return _sizeClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
superblock * const
|
|
||||||
superblock::getNext(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
return _next;
|
|
||||||
}
|
|
||||||
|
|
||||||
superblock * const
|
|
||||||
superblock::getPrev(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
return _prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
superblock::insertBefore(superblock * nextSb)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
// Insert this superblock before the next one (nextSb).
|
|
||||||
assert(nextSb != this);
|
|
||||||
_next = nextSb;
|
|
||||||
if (nextSb) {
|
|
||||||
_prev = nextSb->_prev;
|
|
||||||
nextSb->_prev = this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
superblock::remove(void)
|
|
||||||
{
|
|
||||||
// Remove this superblock from a doubly-linked list.
|
|
||||||
if (_next)
|
|
||||||
_next->_prev = _prev;
|
|
||||||
if (_prev)
|
|
||||||
_prev->_next = _next;
|
|
||||||
|
|
||||||
_prev = NULL;
|
|
||||||
_next = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
superblock::isValid(void)
|
|
||||||
{
|
|
||||||
assert(_numBlocks > 0);
|
|
||||||
assert(_numAvailable <= _numBlocks);
|
|
||||||
assert(_sizeClass >= 0);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
superblock::computeFullness(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
_fullness = (((SUPERBLOCK_FULLNESS_GROUP - 1)
|
|
||||||
* (getNumBlocks() - getNumAvailable())) / getNumBlocks());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
|
||||||
superblock::getFullness(void)
|
|
||||||
{
|
|
||||||
assert(isValid());
|
|
||||||
return _fullness;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
|
|
||||||
#endif // _SUPERBLOCK_H_
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
//#include <limits.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "heap.h"
|
|
||||||
#include "threadheap.h"
|
|
||||||
#include "processheap.h"
|
|
||||||
|
|
||||||
using namespace BPrivate;
|
|
||||||
|
|
||||||
|
|
||||||
threadHeap::threadHeap(void)
|
|
||||||
:_pHeap(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// malloc (sz):
|
|
||||||
// inputs: the size of the object to be allocated.
|
|
||||||
// returns: a pointer to an object of the appropriate size.
|
|
||||||
// side effects: allocates a block from a superblock;
|
|
||||||
// may call sbrk() (via makeSuperblock).
|
|
||||||
|
|
||||||
void *
|
|
||||||
threadHeap::malloc(const size_t size)
|
|
||||||
{
|
|
||||||
#if MAX_INTERNAL_FRAGMENTATION == 2
|
|
||||||
if (size > 1063315264UL) {
|
|
||||||
debug_printf("malloc() of %lu bytes asked\n", size);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const int sizeclass = sizeClass(size);
|
|
||||||
block *b = NULL;
|
|
||||||
|
|
||||||
lock();
|
|
||||||
|
|
||||||
// Look for a free block.
|
|
||||||
// We usually have memory locally so we first look for space in the
|
|
||||||
// superblock list.
|
|
||||||
|
|
||||||
superblock *sb = findAvailableSuperblock(sizeclass, b, _pHeap);
|
|
||||||
if (sb == NULL) {
|
|
||||||
// We don't have memory locally.
|
|
||||||
// Try to get more from the process heap.
|
|
||||||
|
|
||||||
assert(_pHeap);
|
|
||||||
sb = _pHeap->acquire((int)sizeclass, this);
|
|
||||||
|
|
||||||
// If we didn't get any memory from the process heap,
|
|
||||||
// we'll have to allocate our own superblock.
|
|
||||||
if (sb == NULL) {
|
|
||||||
sb = superblock::makeSuperblock(sizeclass, _pHeap);
|
|
||||||
if (sb == NULL) {
|
|
||||||
// We're out of memory!
|
|
||||||
unlock();
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#if HEAP_LOG
|
|
||||||
// Record the memory allocation.
|
|
||||||
MemoryRequest m;
|
|
||||||
m.allocate((int)sb->getNumBlocks() *
|
|
||||||
(int)sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
_pHeap->getLog(getIndex()).append(m);
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
_pHeap->setAllocated(0,
|
|
||||||
sb->getNumBlocks() * sizeFromClass(sb->getBlockSizeClass()));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
// Get a block from the superblock.
|
|
||||||
b = sb->getBlock();
|
|
||||||
assert(b != NULL);
|
|
||||||
|
|
||||||
// Insert the superblock into our list.
|
|
||||||
insertSuperblock(sizeclass, sb, _pHeap);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(b != NULL);
|
|
||||||
assert(b->isValid());
|
|
||||||
assert(sb->isValid());
|
|
||||||
|
|
||||||
b->markAllocated();
|
|
||||||
|
|
||||||
#if HEAP_LOG
|
|
||||||
MemoryRequest m;
|
|
||||||
m.malloc((void *)(b + 1), align(size));
|
|
||||||
_pHeap->getLog(getIndex()).append(m);
|
|
||||||
#endif
|
|
||||||
#if HEAP_FRAG_STATS
|
|
||||||
b->setRequestedSize(align(size));
|
|
||||||
_pHeap->setAllocated(align(size), 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unlock();
|
|
||||||
|
|
||||||
// Skip past the block header and return the pointer.
|
|
||||||
return (void *)(b + 1);
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
///-*-C++-*-//////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
// for Shared-Memory Multiprocessors
|
|
||||||
// Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
//
|
|
||||||
// Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
//
|
|
||||||
// This library is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Library General Public License as
|
|
||||||
// published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
//
|
|
||||||
// This library is distributed in the hope that it will be useful, but
|
|
||||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
// Library General Public License for more details.
|
|
||||||
//
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef _THREADHEAP_H_
|
|
||||||
#define _THREADHEAP_H_
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "heap.h"
|
|
||||||
|
|
||||||
namespace BPrivate {
|
|
||||||
|
|
||||||
class processHeap; // forward declaration
|
|
||||||
|
|
||||||
//
|
|
||||||
// We use one threadHeap for each thread (processor).
|
|
||||||
//
|
|
||||||
|
|
||||||
class threadHeap : public hoardHeap {
|
|
||||||
public:
|
|
||||||
threadHeap(void);
|
|
||||||
|
|
||||||
// Memory allocation routines.
|
|
||||||
void *malloc(const size_t sz);
|
|
||||||
inline void *memalign(size_t alignment, size_t sz);
|
|
||||||
|
|
||||||
// Find out how large an allocated object is.
|
|
||||||
inline static size_t objectSize(void *ptr);
|
|
||||||
|
|
||||||
// Set our process heap.
|
|
||||||
inline void setpHeap(processHeap *p);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Prevent copying and assignment.
|
|
||||||
threadHeap(const threadHeap &);
|
|
||||||
const threadHeap &operator=(const threadHeap &);
|
|
||||||
|
|
||||||
// Our process heap.
|
|
||||||
processHeap *_pHeap;
|
|
||||||
|
|
||||||
// We insert a cache pad here to avoid false sharing (the
|
|
||||||
// processHeap holds an array of threadHeaps, and we don't want
|
|
||||||
// these to share any cache lines).
|
|
||||||
double _pad[CACHE_LINE / sizeof(double)];
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void *
|
|
||||||
threadHeap::memalign(size_t alignment, size_t size)
|
|
||||||
{
|
|
||||||
// Calculate the amount of space we need
|
|
||||||
// to satisfy the alignment requirements.
|
|
||||||
|
|
||||||
size_t newSize;
|
|
||||||
|
|
||||||
// If the alignment is less than the required alignment,
|
|
||||||
// just call malloc.
|
|
||||||
if (alignment <= ALIGNMENT)
|
|
||||||
return this->malloc(size);
|
|
||||||
|
|
||||||
if (alignment < sizeof(block))
|
|
||||||
alignment = sizeof(block);
|
|
||||||
|
|
||||||
// Alignment must be a power of two!
|
|
||||||
assert((alignment & (alignment - 1)) == 0);
|
|
||||||
|
|
||||||
// Leave enough room to align the block within the malloced space.
|
|
||||||
newSize = size + sizeof(block) + alignment;
|
|
||||||
|
|
||||||
// Now malloc the space up with a little extra (we'll put the block
|
|
||||||
// pointer in right behind the allocated space).
|
|
||||||
|
|
||||||
void *ptr = this->malloc(newSize);
|
|
||||||
if ((((unsigned long) ptr) & -((long) alignment)) == 0) {
|
|
||||||
// ptr is already aligned, so return it.
|
|
||||||
assert(((unsigned long) ptr % alignment) == 0);
|
|
||||||
return ptr;
|
|
||||||
} else {
|
|
||||||
// Align ptr.
|
|
||||||
char *newptr = (char *)(((unsigned long)ptr + alignment - 1) & -((long)alignment));
|
|
||||||
|
|
||||||
// If there's not enough room for the block header, skip to the
|
|
||||||
// next aligned space within the block..
|
|
||||||
if ((unsigned long)newptr - (unsigned long)ptr < sizeof(block))
|
|
||||||
newptr += alignment;
|
|
||||||
|
|
||||||
assert(((unsigned long)newptr % alignment) == 0);
|
|
||||||
|
|
||||||
// Copy the block from the start of the allocated memory.
|
|
||||||
block *b = ((block *)ptr - 1);
|
|
||||||
|
|
||||||
assert(b->isValid());
|
|
||||||
assert(b->getSuperblock()->isValid());
|
|
||||||
|
|
||||||
// Make sure there's enough room for the block header.
|
|
||||||
assert(((unsigned long)newptr - (unsigned long)ptr) >=
|
|
||||||
sizeof(block));
|
|
||||||
|
|
||||||
block *p = ((block *)newptr - 1);
|
|
||||||
|
|
||||||
// Make sure there's enough room allocated for size bytes.
|
|
||||||
assert(((unsigned long)p - sizeof(block)) >= (unsigned long)b);
|
|
||||||
|
|
||||||
if (p != b) {
|
|
||||||
assert((unsigned long)newptr > (unsigned long)ptr);
|
|
||||||
// Copy the block header.
|
|
||||||
*p = *b;
|
|
||||||
assert(p->isValid());
|
|
||||||
assert(p->getSuperblock()->isValid());
|
|
||||||
|
|
||||||
// Set the next pointer to point to b with the 1 bit set.
|
|
||||||
// When this block is freed, it will be treated specially.
|
|
||||||
p->setNext((block *)((unsigned long)b | 1));
|
|
||||||
} else
|
|
||||||
assert(ptr != newptr);
|
|
||||||
|
|
||||||
assert(((unsigned long)ptr + newSize) >=
|
|
||||||
((unsigned long)newptr + size));
|
|
||||||
return newptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
size_t
|
|
||||||
threadHeap::objectSize(void *ptr)
|
|
||||||
{
|
|
||||||
// Find the superblock pointer.
|
|
||||||
block *b = ((block *)ptr - 1);
|
|
||||||
assert(b->isValid());
|
|
||||||
superblock *sb = b->getSuperblock();
|
|
||||||
assert(sb);
|
|
||||||
|
|
||||||
// Return the size.
|
|
||||||
return sizeFromClass(sb->getBlockSizeClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void threadHeap::setpHeap(processHeap *p)
|
|
||||||
{
|
|
||||||
_pHeap = p;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace BPrivate
|
|
||||||
|
|
||||||
#endif // _THREADHEAP_H_
|
|
||||||
@@ -1,600 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2002-2007, Haiku Inc.
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Hoard: A Fast, Scalable, and Memory-Efficient Allocator
|
|
||||||
* for Shared-Memory Multiprocessors
|
|
||||||
* Contact author: Emery Berger, http://www.cs.utexas.edu/users/emery
|
|
||||||
*
|
|
||||||
* Copyright (c) 1998-2000, The University of Texas at Austin.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Library General Public License as
|
|
||||||
* published by the Free Software Foundation, http://www.fsf.org.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "threadheap.h"
|
|
||||||
#include "processheap.h"
|
|
||||||
#include "arch-specific.h"
|
|
||||||
|
|
||||||
#include <image.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <errno_private.h>
|
|
||||||
#include <user_thread.h>
|
|
||||||
|
|
||||||
#include "tracing_config.h"
|
|
||||||
|
|
||||||
using namespace BPrivate;
|
|
||||||
|
|
||||||
|
|
||||||
#if USER_MALLOC_TRACING
|
|
||||||
# define KTRACE(format...) ktrace_printf(format)
|
|
||||||
#else
|
|
||||||
# define KTRACE(format...) do {} while (false)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
static block* sUsedList = NULL;
|
|
||||||
static hoardLockType sUsedLock = MUTEX_INITIALIZER("");
|
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
Finds the closest symbol that comes before the given address.
|
|
||||||
*/
|
|
||||||
static status_t
|
|
||||||
get_symbol_for_address(void* address, char *imageBuffer, size_t imageBufferSize,
|
|
||||||
char* buffer, size_t bufferSize, int32& offset)
|
|
||||||
{
|
|
||||||
offset = -1;
|
|
||||||
|
|
||||||
image_info info;
|
|
||||||
int32 cookie = 0;
|
|
||||||
while (get_next_image_info(0, &cookie, &info) == B_OK) {
|
|
||||||
if (((addr_t)info.text > (addr_t)address
|
|
||||||
|| (addr_t)info.text + info.text_size < (addr_t)address)
|
|
||||||
&& ((addr_t)info.data > (addr_t)address
|
|
||||||
|| (addr_t)info.data + info.data_size < (addr_t)address))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char name[256];
|
|
||||||
int32 index = 0;
|
|
||||||
int32 nameLength = sizeof(name);
|
|
||||||
int32 symbolType;
|
|
||||||
void* location;
|
|
||||||
while (get_nth_image_symbol(info.id, index, name, &nameLength,
|
|
||||||
&symbolType, &location) == B_OK) {
|
|
||||||
if ((addr_t)address >= (addr_t)location) {
|
|
||||||
// see if this is better than what we have
|
|
||||||
int32 newOffset = (addr_t)address - (addr_t)location;
|
|
||||||
|
|
||||||
if (offset == -1 || offset > newOffset) {
|
|
||||||
const char* imageName = strrchr(info.name, '/');
|
|
||||||
if (imageName != NULL)
|
|
||||||
strlcpy(imageBuffer, imageName + 1, imageBufferSize);
|
|
||||||
else
|
|
||||||
strlcpy(imageBuffer, info.name, imageBufferSize);
|
|
||||||
|
|
||||||
strlcpy(buffer, name, bufferSize);
|
|
||||||
offset = newOffset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nameLength = sizeof(name);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset != -1 ? B_OK : B_ENTRY_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
dump_block(block* b)
|
|
||||||
{
|
|
||||||
printf(" %p, %ld bytes: call stack", b + 1, b->getAllocatedSize());
|
|
||||||
|
|
||||||
for (int i = 0; i < HEAP_CALL_STACK_SIZE; i++) {
|
|
||||||
if (b->getCallStack(i) != NULL) {
|
|
||||||
char image[256];
|
|
||||||
char name[256];
|
|
||||||
int32 offset;
|
|
||||||
if (get_symbol_for_address(b->getCallStack(i), image, sizeof(image),
|
|
||||||
name, sizeof(name), offset) != B_OK) {
|
|
||||||
strcpy(name, "???");
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(": %p (%s:%s+0x%lx)", b->getCallStack(i), image, name, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void __dump_allocated(void);
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__dump_allocated(void)
|
|
||||||
{
|
|
||||||
hoardLock(sUsedLock);
|
|
||||||
|
|
||||||
puts("allocated:\n");
|
|
||||||
|
|
||||||
block* b = sUsedList;
|
|
||||||
while (b != NULL) {
|
|
||||||
dump_block(b);
|
|
||||||
|
|
||||||
b = b->getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
hoardUnlock(sUsedLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_address(void* address, size_t size)
|
|
||||||
{
|
|
||||||
block *b = (block *)address - 1;
|
|
||||||
|
|
||||||
#ifdef __i386__
|
|
||||||
// set call stack
|
|
||||||
struct stack_frame {
|
|
||||||
struct stack_frame* previous;
|
|
||||||
void* return_address;
|
|
||||||
};
|
|
||||||
|
|
||||||
stack_frame* frame = (stack_frame*)get_stack_frame();
|
|
||||||
|
|
||||||
for (int i = 0; i < HEAP_CALL_STACK_SIZE; i++) {
|
|
||||||
if (frame != NULL) {
|
|
||||||
b->setCallStack(i, frame->return_address);
|
|
||||||
frame = frame->previous;
|
|
||||||
} else
|
|
||||||
b->setCallStack(i, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
b->setAllocatedSize(size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hoardLock(sUsedLock);
|
|
||||||
|
|
||||||
b->setNext(sUsedList);
|
|
||||||
sUsedList = b;
|
|
||||||
|
|
||||||
hoardUnlock(sUsedLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
remove_address(void* address)
|
|
||||||
{
|
|
||||||
block* b = (block *)address - 1;
|
|
||||||
hoardLock(sUsedLock);
|
|
||||||
|
|
||||||
if (sUsedList == b) {
|
|
||||||
// we're lucky, it's the first block in the list
|
|
||||||
sUsedList = b->getNext();
|
|
||||||
} else {
|
|
||||||
// search for block in the used list (very slow!)
|
|
||||||
block* last = sUsedList;
|
|
||||||
while (last != NULL && last->getNext() != b) {
|
|
||||||
last = last->getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last == NULL) {
|
|
||||||
printf("freed block not in used list!\n");
|
|
||||||
dump_block(b);
|
|
||||||
} else
|
|
||||||
last->setNext(b->getNext());
|
|
||||||
}
|
|
||||||
|
|
||||||
hoardUnlock(sUsedLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HEAP_LEAK_CHECK
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
|
|
||||||
static void*
|
|
||||||
set_wall(void* addr, size_t size)
|
|
||||||
{
|
|
||||||
size_t *start = (size_t*)addr;
|
|
||||||
|
|
||||||
start[0] = size;
|
|
||||||
memset(start + 1, 0x88, HEAP_WALL_SIZE - sizeof(size_t));
|
|
||||||
memset((uint8*)addr + size - HEAP_WALL_SIZE, 0x66, HEAP_WALL_SIZE);
|
|
||||||
|
|
||||||
return (uint8*)addr + HEAP_WALL_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void*
|
|
||||||
check_wall(uint8* buffer)
|
|
||||||
{
|
|
||||||
buffer -= HEAP_WALL_SIZE;
|
|
||||||
size_t size = *(size_t*)buffer;
|
|
||||||
|
|
||||||
if (threadHeap::objectSize(buffer) < size)
|
|
||||||
debugger("invalid size");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < HEAP_WALL_SIZE; i++) {
|
|
||||||
if (i >= sizeof(size_t) && buffer[i] != 0x88) {
|
|
||||||
debug_printf("allocation %p, size %ld front wall clobbered at byte %ld.\n",
|
|
||||||
buffer + HEAP_WALL_SIZE, size - 2 * HEAP_WALL_SIZE, i);
|
|
||||||
debugger("front wall clobbered");
|
|
||||||
}
|
|
||||||
if (buffer[i + size - HEAP_WALL_SIZE] != 0x66) {
|
|
||||||
debug_printf("allocation %p, size %ld back wall clobbered at byte %ld.\n",
|
|
||||||
buffer + HEAP_WALL_SIZE, size - 2 * HEAP_WALL_SIZE, i);
|
|
||||||
debugger("back wall clobbered");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HEAP_WALL
|
|
||||||
|
|
||||||
inline static processHeap *
|
|
||||||
getAllocator(void)
|
|
||||||
{
|
|
||||||
static char *buffer = (char *)hoardSbrk(sizeof(processHeap));
|
|
||||||
static processHeap *theAllocator = new (buffer) processHeap();
|
|
||||||
|
|
||||||
return theAllocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__heap_before_fork(void)
|
|
||||||
{
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
for (int i = 0; i < pHeap->getMaxThreadHeaps(); i++)
|
|
||||||
pHeap->getHeap(i).lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void __init_after_fork(void);
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__heap_after_fork_child(void)
|
|
||||||
{
|
|
||||||
__init_after_fork();
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
for (int i = 0; i < pHeap->getMaxThreadHeaps(); i++)
|
|
||||||
pHeap->getHeap(i).initLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__heap_after_fork_parent(void)
|
|
||||||
{
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
for (int i = 0; i < pHeap->getMaxThreadHeaps(); i++)
|
|
||||||
pHeap->getHeap(i).unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__heap_thread_init(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
__heap_thread_exit(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - public functions
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void *
|
|
||||||
malloc(size_t size)
|
|
||||||
{
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
size += 2 * HEAP_WALL_SIZE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
defer_signals();
|
|
||||||
|
|
||||||
void *addr = pHeap->getHeap(pHeap->getHeapIndex()).malloc(size);
|
|
||||||
if (addr == NULL) {
|
|
||||||
undefer_signals();
|
|
||||||
__set_errno(B_NO_MEMORY);
|
|
||||||
KTRACE("malloc(%lu) -> NULL", size);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
add_address(addr, size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
undefer_signals();
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
addr = set_wall(addr, size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
KTRACE("malloc(%lu) -> %p", size, addr);
|
|
||||||
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void *
|
|
||||||
calloc(size_t nelem, size_t elsize)
|
|
||||||
{
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
size_t size = nelem * elsize;
|
|
||||||
void *ptr = NULL;
|
|
||||||
|
|
||||||
if ((nelem > 0) && ((size/nelem) != elsize))
|
|
||||||
goto nomem;
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
size += 2 * HEAP_WALL_SIZE;
|
|
||||||
|
|
||||||
if (nelem == 0 || elsize == 0)
|
|
||||||
goto ok;
|
|
||||||
if (size < (nelem * size)&& size < (elsize * size))
|
|
||||||
goto nomem;
|
|
||||||
|
|
||||||
ok:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
defer_signals();
|
|
||||||
|
|
||||||
ptr = pHeap->getHeap(pHeap->getHeapIndex()).malloc(size);
|
|
||||||
if (ptr == NULL) {
|
|
||||||
undefer_signals();
|
|
||||||
nomem:
|
|
||||||
__set_errno(B_NO_MEMORY);
|
|
||||||
KTRACE("calloc(%lu, %lu) -> NULL", nelem, elsize);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
add_address(ptr, size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
undefer_signals();
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
ptr = set_wall(ptr, size);
|
|
||||||
size -= 2 * HEAP_WALL_SIZE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Zero out the malloc'd block.
|
|
||||||
memset(ptr, 0, size);
|
|
||||||
KTRACE("calloc(%lu, %lu) -> %p", nelem, elsize, ptr);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void
|
|
||||||
free(void *ptr)
|
|
||||||
{
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
if (ptr == NULL)
|
|
||||||
return;
|
|
||||||
KTRACE("free(%p)", ptr);
|
|
||||||
ptr = check_wall((uint8*)ptr);
|
|
||||||
#else
|
|
||||||
KTRACE("free(%p)", ptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
defer_signals();
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
if (ptr != NULL)
|
|
||||||
remove_address(ptr);
|
|
||||||
#endif
|
|
||||||
pHeap->free(ptr);
|
|
||||||
|
|
||||||
undefer_signals();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void *
|
|
||||||
memalign(size_t alignment, size_t size)
|
|
||||||
{
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
debug_printf("memalign() is not yet supported by the wall code.\n");
|
|
||||||
return NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
defer_signals();
|
|
||||||
|
|
||||||
void *addr = pHeap->getHeap(pHeap->getHeapIndex()).memalign(alignment,
|
|
||||||
size);
|
|
||||||
if (addr == NULL) {
|
|
||||||
undefer_signals();
|
|
||||||
__set_errno(B_NO_MEMORY);
|
|
||||||
KTRACE("memalign(%lu, %lu) -> NULL", alignment, size);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
add_address(addr, size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
undefer_signals();
|
|
||||||
|
|
||||||
KTRACE("memalign(%lu, %lu) -> %p", alignment, size, addr);
|
|
||||||
return addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" int
|
|
||||||
posix_memalign(void **_pointer, size_t alignment, size_t size)
|
|
||||||
{
|
|
||||||
if ((alignment & (sizeof(void *) - 1)) != 0 || _pointer == NULL)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
debug_printf("posix_memalign() is not yet supported by the wall code.\n");
|
|
||||||
return -1;
|
|
||||||
#endif
|
|
||||||
static processHeap *pHeap = getAllocator();
|
|
||||||
defer_signals();
|
|
||||||
void *pointer = pHeap->getHeap(pHeap->getHeapIndex()).memalign(alignment,
|
|
||||||
size);
|
|
||||||
if (pointer == NULL) {
|
|
||||||
undefer_signals();
|
|
||||||
KTRACE("posix_memalign(%p, %lu, %lu) -> NULL", _pointer, alignment,
|
|
||||||
size);
|
|
||||||
return B_NO_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_LEAK_CHECK
|
|
||||||
add_address(pointer, size);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
undefer_signals();
|
|
||||||
|
|
||||||
*_pointer = pointer;
|
|
||||||
KTRACE("posix_memalign(%p, %lu, %lu) -> %p", _pointer, alignment, size,
|
|
||||||
pointer);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void *
|
|
||||||
valloc(size_t size)
|
|
||||||
{
|
|
||||||
return memalign(B_PAGE_SIZE, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" void *
|
|
||||||
realloc(void *ptr, size_t size)
|
|
||||||
{
|
|
||||||
if (ptr == NULL)
|
|
||||||
return malloc(size);
|
|
||||||
|
|
||||||
if (size == 0) {
|
|
||||||
free(ptr);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the existing object can hold the new size,
|
|
||||||
// just return it.
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
size += 2 * HEAP_WALL_SIZE;
|
|
||||||
ptr = (uint8*)ptr - HEAP_WALL_SIZE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t objSize = threadHeap::objectSize(ptr);
|
|
||||||
if (objSize >= size) {
|
|
||||||
#if HEAP_WALL
|
|
||||||
check_wall((uint8*)ptr + HEAP_WALL_SIZE);
|
|
||||||
ptr = set_wall(ptr, size);
|
|
||||||
#endif
|
|
||||||
KTRACE("realloc(%p, %lu) -> %p", ptr, size, ptr);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HEAP_WALL
|
|
||||||
size -= 2 * HEAP_WALL_SIZE;
|
|
||||||
objSize -= 2 * HEAP_WALL_SIZE;
|
|
||||||
ptr = (uint8*)ptr + HEAP_WALL_SIZE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Allocate a new block of size sz.
|
|
||||||
void *buffer = malloc(size);
|
|
||||||
if (buffer == NULL) {
|
|
||||||
// Allocation failed, leave old block and return
|
|
||||||
__set_errno(B_NO_MEMORY);
|
|
||||||
KTRACE("realloc(%p, %lu) -> NULL", ptr, size);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the contents of the original object
|
|
||||||
// up to the size of the new block.
|
|
||||||
|
|
||||||
size_t minSize = (objSize < size) ? objSize : size;
|
|
||||||
memcpy(buffer, ptr, minSize);
|
|
||||||
|
|
||||||
// Free the old block.
|
|
||||||
free(ptr);
|
|
||||||
|
|
||||||
// Return a pointer to the new one.
|
|
||||||
KTRACE("realloc(%p, %lu) -> %p", ptr, size, buffer);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" size_t
|
|
||||||
malloc_usable_size(void *ptr)
|
|
||||||
{
|
|
||||||
if (ptr == NULL)
|
|
||||||
return 0;
|
|
||||||
return threadHeap::objectSize(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - BeOS specific extensions
|
|
||||||
|
|
||||||
|
|
||||||
struct mstats {
|
|
||||||
size_t bytes_total;
|
|
||||||
size_t chunks_used;
|
|
||||||
size_t bytes_used;
|
|
||||||
size_t chunks_free;
|
|
||||||
size_t bytes_free;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" struct mstats mstats(void);
|
|
||||||
|
|
||||||
extern "C" struct mstats
|
|
||||||
mstats(void)
|
|
||||||
{
|
|
||||||
// Note, the stats structure is not thread-safe, but it doesn't
|
|
||||||
// matter that much either
|
|
||||||
processHeap *heap = getAllocator();
|
|
||||||
static struct mstats stats;
|
|
||||||
|
|
||||||
int allocated = 0;
|
|
||||||
int used = 0;
|
|
||||||
int chunks = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < hoardHeap::SIZE_CLASSES; i++) {
|
|
||||||
int classUsed, classAllocated;
|
|
||||||
heap->getStats(i, classUsed, classAllocated);
|
|
||||||
|
|
||||||
if (classUsed > 0)
|
|
||||||
chunks++;
|
|
||||||
|
|
||||||
allocated += classAllocated;
|
|
||||||
used += classUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
stats.bytes_total = allocated;
|
|
||||||
stats.chunks_used = chunks;
|
|
||||||
stats.bytes_used = used;
|
|
||||||
stats.chunks_free = hoardHeap::SIZE_CLASSES - chunks;
|
|
||||||
stats.bytes_free = allocated - used;
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user