/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Dalvik.h"
#include "alloc/HeapTable.h"
#include "alloc/HeapInternal.h"
#include <limits.h> // for INT_MAX
static void *heapTableRealloc(void *oldPtr, size_t newSize)
{
/* Don't just call realloc(), in case the native system
* doesn't malloc() on realloc(NULL).
*/
if (oldPtr != NULL) {
return realloc(oldPtr, newSize);
} else {
return malloc(newSize);
}
}
void dvmHeapHeapTableFree(void *ptr)
{
free(ptr);
}
#define heapRefTableIsFull(refs) \
({ \
const HeapRefTable *HRTIF_refs = (refs); \
dvmIsReferenceTableFull(refs); \
})
bool dvmHeapInitHeapRefTable(HeapRefTable *refs, size_t nelems)
{
memset(refs, 0, sizeof(*refs));
return dvmInitReferenceTable(refs, nelems, INT_MAX);
}
/* Frees the array inside the HeapRefTable, not the HeapRefTable itself.
*/
void dvmHeapFreeHeapRefTable(HeapRefTable *refs)
{
dvmClearReferenceTable(refs);
}
/*
* Large, non-contiguous reference tables
*/
#define kLargeHeapRefTableNElems 1024
bool dvmHeapAddRefToLargeTable(LargeHeapRefTable **tableP, Object *ref)
{
LargeHeapRefTable *table;
assert(tableP != NULL);
assert(ref != NULL);
/* Make sure that a table with a free slot is
* at the head of the list.
*/
if (*tableP != NULL) {
table = *tableP;
LargeHeapRefTable *prevTable;
/* Find an empty slot for this reference.
*/
prevTable = NULL;
while (table != NULL && heapRefTableIsFull(&table->refs)) {
prevTable = table;
table = table->next;
}
if (table != NULL) {
if (prevTable != NULL) {
/* Move the table to the head of the list.
*/
prevTable->next = table->next;
table->next = *tableP;
*tableP = table;
}
/* else it's already at the head. */
goto insert;
}
/* else all tables are already full;
* fall through to the alloc case.
*/
}
/* Allocate a new table.
*/
table = (LargeHeapRefTable *)heapTableRealloc(NULL,
sizeof(LargeHeapRefTable));
if (table == NULL) {
LOGE_HEAP("Can't allocate a new large ref table\n");
return false;
}
if (!dvmHeapInitHeapRefTable(&table->refs, kLargeHeapRefTableNElems)) {
LOGE_HEAP("Can't initialize a new large ref table\n");
dvmHeapHeapTableFree(table);
return false;
}
/* Stick it at the head.
*/
table->next = *tableP;
*tableP = table;
insert:
/* Insert the reference.
*/
assert(table == *tableP);
assert(table != NULL);
assert(!heapRefTableIsFull(&table->refs));
*table->refs.nextEntry++ = ref;
return true;
}
bool dvmHeapAddTableToLargeTable(LargeHeapRefTable **tableP, HeapRefTable *refs)
{
LargeHeapRefTable *table;
/* Allocate a node.
*/
table = (LargeHeapRefTable *)heapTableRealloc(NULL,
sizeof(LargeHeapRefTable));
if (table == NULL) {
LOGE_HEAP("Can't allocate a new large ref table\n");
return false;
}
table->refs = *refs;
/* Insert the table into the list.
*/
table->next = *tableP;
*tableP = table;
return true;
}
/* Frees everything associated with the LargeHeapRefTable.
*/
void dvmHeapFreeLargeTable(LargeHeapRefTable *table)
{
while (table != NULL) {
LargeHeapRefTable *next = table->next;
dvmHeapFreeHeapRefTable(&table->refs);
dvmHeapHeapTableFree(table);
table = next;
}
}
Object *dvmHeapGetNextObjectFromLargeTable(LargeHeapRefTable **pTable)
{
LargeHeapRefTable *table;
Object *obj;
assert(pTable != NULL);
obj = NULL;
table = *pTable;
if (table != NULL) {
GcHeap *gcHeap = gDvm.gcHeap;
HeapRefTable *refs = &table->refs;
/* We should never have an empty table node in the list.
*/
assert(dvmReferenceTableEntries(refs) != 0);
/* Remove and return the last entry in the list.
*/
obj = *--refs->nextEntry;
/* If this was the last entry in the table node,
* free it and patch up the list.
*/
if (refs->nextEntry == refs->table) {
*pTable = table->next;
dvmClearReferenceTable(refs);
dvmHeapHeapTableFree(table);
}
}
return obj;
}
void dvmHeapMarkLargeTableRefs(LargeHeapRefTable *table, bool stripLowBits)
{
while (table != NULL) {
Object **ref, **lastRef;
ref = table->refs.table;
lastRef = table->refs.nextEntry;
if (stripLowBits) {
/* This case is used for marking reference objects that
* are still waiting for the heap worker thread to get to
* them. The referents pointed to by the references are
* marked when a SCHEDULED_REFERENCE_MAGIC is encountered
* during scanning.
*/
while (ref < lastRef) {
dvmMarkObjectNonNull((Object *)((uintptr_t)*ref++ & ~3));
}
} else {
while (ref < lastRef) {
dvmMarkObjectNonNull(*ref++);
}
}
table = table->next;
}
}