279 lines
6.8 KiB
C
279 lines
6.8 KiB
C
/*
|
|
* 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 <stdint.h>
|
|
#include <stdio.h>
|
|
#include <heap.h>
|
|
#include <seos.h>
|
|
|
|
#define TIDX_HEAP_EXTRA 2 // must be >= 0; best if > 0, don't make it > 7, since it unnecessarily limits max heap size we can manage
|
|
|
|
#define TIDX_HEAP_BITS (TASK_IDX_BITS + TIDX_HEAP_EXTRA)
|
|
|
|
#define TIDX_MASK ((1 << TIDX_HEAP_BITS) - 1)
|
|
#define MAX_HEAP_ORDER (31 - TIDX_HEAP_BITS)
|
|
|
|
#if MAX_HEAP_ORDER < 16
|
|
# error Too little HEAP is available
|
|
#endif
|
|
|
|
struct HeapNode {
|
|
|
|
struct HeapNode* prev;
|
|
uint32_t size: MAX_HEAP_ORDER;
|
|
uint32_t used: 1;
|
|
uint32_t tidx: TIDX_HEAP_BITS; // TASK_IDX_BITS to uniquely identify task; + extra bits of redundant counter add extra protection
|
|
uint8_t data[];
|
|
};
|
|
|
|
#ifdef FORCE_HEAP_IN_DOT_DATA
|
|
|
|
static uint8_t __attribute__ ((aligned (8))) gHeap[HEAP_SIZE];
|
|
|
|
#define REAL_HEAP_SIZE ((HEAP_SIZE) &~ 7)
|
|
#define ALIGNED_HEAP_START (&gHeap)
|
|
|
|
#else
|
|
|
|
extern uint8_t __heap_end[], __heap_start[];
|
|
#define ALIGNED_HEAP_START (uint8_t*)((((uintptr_t)&__heap_start) + 7) &~ 7)
|
|
#define ALIGNED_HEAP_END (uint8_t*)(((uintptr_t)&__heap_end) &~ 7)
|
|
|
|
#define REAL_HEAP_SIZE (ALIGNED_HEAP_END - ALIGNED_HEAP_START)
|
|
|
|
|
|
#endif
|
|
|
|
static struct HeapNode* gHeapHead;
|
|
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 = REAL_HEAP_SIZE;
|
|
struct HeapNode* node;
|
|
|
|
node = gHeapHead = (struct HeapNode*)ALIGNED_HEAP_START;
|
|
|
|
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 = gHeapHead, *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 = gHeapHead;
|
|
|
|
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->tidx = 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;
|
|
best->tidx = osGetCurrentTid();
|
|
ret = best->data;
|
|
|
|
out:
|
|
trylockRelease(&gHeapLock);
|
|
return ret;
|
|
}
|
|
|
|
void heapFree(void* ptr)
|
|
{
|
|
struct HeapNode *node, *t;
|
|
bool haveLock;
|
|
|
|
if (ptr == NULL) {
|
|
// NULL is a valid reply from heapAlloc, and thus it is not an error for
|
|
// us to receive it here. We just ignore it.
|
|
return;
|
|
}
|
|
|
|
haveLock = trylockTryTake(&gHeapLock);
|
|
|
|
node = ((struct HeapNode*)ptr) - 1;
|
|
node->used = 0;
|
|
node->tidx = 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;
|
|
}
|
|
|
|
int heapFreeAll(uint32_t tid)
|
|
{
|
|
struct HeapNode *node;
|
|
bool haveLock;
|
|
int count = 0;
|
|
|
|
if (!tid)
|
|
return -1;
|
|
|
|
// this can only fail if called from interrupt
|
|
haveLock = trylockTryTake(&gHeapLock);
|
|
if (!haveLock)
|
|
return -1;
|
|
|
|
tid &= TIDX_MASK;
|
|
for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
|
|
if (node->tidx == tid) {
|
|
node->used = 0;
|
|
node->tidx = 0;
|
|
count++;
|
|
}
|
|
}
|
|
gNeedFreeMerge = count > 0;
|
|
trylockRelease(&gHeapLock);
|
|
|
|
return count;
|
|
}
|
|
|
|
int heapGetFreeSize(int *numChunks, int *largestChunk)
|
|
{
|
|
struct HeapNode *node;
|
|
bool haveLock;
|
|
int bytes = 0;
|
|
*numChunks = *largestChunk = 0;
|
|
|
|
// this can only fail if called from interrupt
|
|
haveLock = trylockTryTake(&gHeapLock);
|
|
if (!haveLock)
|
|
return -1;
|
|
|
|
for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
|
|
if (!node->used) {
|
|
if (node->size > *largestChunk)
|
|
*largestChunk = node->size;
|
|
bytes += node->size + sizeof(struct HeapNode);
|
|
(*numChunks)++;
|
|
}
|
|
}
|
|
trylockRelease(&gHeapLock);
|
|
|
|
return bytes;
|
|
}
|
|
|
|
int heapGetTaskSize(uint32_t tid)
|
|
{
|
|
struct HeapNode *node;
|
|
bool haveLock;
|
|
int bytes = 0;
|
|
|
|
// this can only fail if called from interrupt
|
|
haveLock = trylockTryTake(&gHeapLock);
|
|
if (!haveLock)
|
|
return -1;
|
|
|
|
tid &= TIDX_MASK;
|
|
for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
|
|
if (node->used && node->tidx == tid) {
|
|
bytes += node->size + sizeof(struct HeapNode);
|
|
}
|
|
}
|
|
trylockRelease(&gHeapLock);
|
|
|
|
return bytes;
|
|
}
|