blob: 8f27a29de66549dd6bcc8d57d831054d5939e648 [file] [log] [blame]
/*
* Copyright (C) 2016 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 <trylock.h>
#include <atomic.h>
#include <stdio.h>
#include <heap.h>
struct HeapNode {
struct HeapNode* prev;
uint32_t size:31;
uint32_t used: 1;
uint8_t data[];
};
static uint8_t __attribute__ ((aligned (8))) gHeap[HEAP_SIZE];
static TRYLOCK_DECL_STATIC(gHeapLock) = TRYLOCK_INIT_STATIC();
static volatile uint8_t gNeedFreeMerge = false; /* cannot be bool since its size is ill defined */
static struct HeapNode *gHeapTail;
static inline struct HeapNode* heapPrvGetNext(struct HeapNode* node)
{
return (gHeapTail == node) ? NULL : (struct HeapNode*)(node->data + node->size);
}
bool heapInit(void)
{
uint32_t size = sizeof(gHeap);
struct HeapNode* node;
node = (struct HeapNode*)gHeap;
if (size < sizeof(struct HeapNode))
return false;
gHeapTail = node;
node->used = 0;
node->prev = NULL;
node->size = size - sizeof(struct HeapNode);
return true;
}
//called to merge free chunks in case free() was unable to last time it tried. only call with lock held please
static void heapMergeFreeChunks(void)
{
while (atomicXchgByte(&gNeedFreeMerge, false)) {
struct HeapNode *node = (struct HeapNode*)gHeap, *next;
while (node) {
next = heapPrvGetNext(node);
if (!node->used && next && !next->used) { /* merged */
node->size += sizeof(struct HeapNode) + next->size;
next = heapPrvGetNext(node);
if (next)
next->prev = node;
else
gHeapTail = node;
}
else
node = next;
}
}
}
void* heapAlloc(uint32_t sz)
{
struct HeapNode *node, *best = NULL;
void* ret = NULL;
if (!trylockTryTake(&gHeapLock))
return NULL;
/* merge free chunks to help better use space */
heapMergeFreeChunks();
sz = (sz + 3) &~ 3;
node = (struct HeapNode*)gHeap;
while (node) {
if (!node->used && node->size >= sz && (!best || best->size > node->size)) {
best = node;
if (best->size == sz)
break;
}
node = heapPrvGetNext(node);
}
if (!best) //alloc failed
goto out;
if (best->size - sz > sizeof(struct HeapNode)) { //there is a point to split up the chunk
node = (struct HeapNode*)(best->data + sz);
node->used = 0;
node->size = best->size - sz - sizeof(struct HeapNode);
node->prev = best;
if (best != gHeapTail)
heapPrvGetNext(node)->prev = node;
else
gHeapTail = node;
best->size = sz;
}
best->used = 1;
ret = best->data;
out:
trylockRelease(&gHeapLock);
return ret;
}
void heapFree(void* ptr)
{
struct HeapNode *node, *t;
bool haveLock;
haveLock = trylockTryTake(&gHeapLock);
node = ((struct HeapNode*)ptr) - 1;
node->used = 0;
if (haveLock) {
while (node->prev && !node->prev->used)
node = node->prev;
while ((t = heapPrvGetNext(node)) && !t->used) {
node->size += sizeof(struct HeapNode) + t->size;
if (gHeapTail == t)
gHeapTail = node;
}
if ((t = heapPrvGetNext(node)))
t->prev = node;
trylockRelease(&gHeapLock);
}
else
gNeedFreeMerge = true;
}