mirror of
https://bitbucket.org/librepilot/librepilot.git
synced 2024-11-30 08:24:11 +01:00
1187 lines
33 KiB
C
1187 lines
33 KiB
C
|
/*
|
||
|
# This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
|
||
|
#
|
||
|
# This file is part of the PyMite VM.
|
||
|
# The PyMite VM is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU GENERAL PUBLIC LICENSE Version 2.
|
||
|
#
|
||
|
# The PyMite VM 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.
|
||
|
# A copy of the GNU GENERAL PUBLIC LICENSE Version 2
|
||
|
# is seen in the file COPYING in this directory.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#undef __FILE_ID__
|
||
|
#define __FILE_ID__ 0x06
|
||
|
|
||
|
|
||
|
/**
|
||
|
* \file
|
||
|
* \brief VM Heap
|
||
|
*
|
||
|
* VM heap operations.
|
||
|
* All of PyMite's dynamic memory is obtained from this heap.
|
||
|
* The heap provides dynamic memory on demand.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "pm.h"
|
||
|
|
||
|
|
||
|
/** Checks for heap size definition. */
|
||
|
#ifndef PM_HEAP_SIZE
|
||
|
#warning PM_HEAP_SIZE not defined in src/platform/<yourplatform>/pmfeatures.h
|
||
|
#elif PM_HEAP_SIZE & 3
|
||
|
#error PM_HEAP_SIZE is not a multiple of four
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/** The size of the temporary roots stack */
|
||
|
#define HEAP_NUM_TEMP_ROOTS 24
|
||
|
|
||
|
/**
|
||
|
* The maximum size a live chunk can be (a live chunk is one that is in use).
|
||
|
* The live chunk size is limited by the size field in the *object* descriptor.
|
||
|
* That field is nine bits with two assumed least significant bits (zeros):
|
||
|
* (0x1FF << 2) == 2044
|
||
|
*/
|
||
|
#define HEAP_MAX_LIVE_CHUNK_SIZE 2044
|
||
|
|
||
|
/**
|
||
|
* The maximum size a free chunk can be (a free chunk is one that is not in use).
|
||
|
* The free chunk size is limited by the size field in the *heap* descriptor.
|
||
|
* That field is fourteen bits with two assumed least significant bits (zeros):
|
||
|
* (0x3FFF << 2) == 65532
|
||
|
*/
|
||
|
#define HEAP_MAX_FREE_CHUNK_SIZE 65532
|
||
|
|
||
|
/** The minimum size a chunk can be (rounded up to a multiple of 4) */
|
||
|
#define HEAP_MIN_CHUNK_SIZE ((sizeof(PmHeapDesc_t) + 3) & ~3)
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Gets the GC's mark bit for the object.
|
||
|
* This MUST NOT be called on objects that are free.
|
||
|
*/
|
||
|
#define OBJ_GET_GCVAL(pobj) ((((pPmObj_t)pobj)->od >> OD_MARK_SHIFT) & 1)
|
||
|
|
||
|
/**
|
||
|
* Sets the GC's mark bit for the object
|
||
|
* This MUST NOT be called on objects that are free.
|
||
|
*/
|
||
|
#ifdef HAVE_GC
|
||
|
#define OBJ_SET_GCVAL(pobj, gcval) \
|
||
|
do \
|
||
|
{ \
|
||
|
((pPmObj_t)pobj)->od = (gcval) ? ((pPmObj_t)pobj)->od | OD_MARK_BIT \
|
||
|
: ((pPmObj_t)pobj)->od & ~OD_MARK_BIT;\
|
||
|
} \
|
||
|
while (0)
|
||
|
#else
|
||
|
#define OBJ_SET_GCVAL(pobj, gcval)
|
||
|
#endif /* HAVE_GC */
|
||
|
|
||
|
|
||
|
/**
|
||
|
* The following is a diagram of the heap descriptor at the head of the chunk:
|
||
|
* @verbatim
|
||
|
* MSb LSb
|
||
|
* 7 6 5 4 3 2 1 0
|
||
|
* pchunk-> +-+-+-+-+-+-+-+-+
|
||
|
* | S[9:2] | S := Size of the chunk (2 LSbs dropped)
|
||
|
* +-+-+-----------+ F := Chunk free bit (not in use)
|
||
|
* |F|R| S[15:10] | R := Bit reserved for future use
|
||
|
* +-+-+-----------+
|
||
|
* | P(L) | P := hd_prev: Pointer to previous node
|
||
|
* | P(H) | N := hd_next: Pointer to next node
|
||
|
* | N(L) |
|
||
|
* | N(H) | Theoretical min size == 6
|
||
|
* +---------------+ Effective min size == 8
|
||
|
* | unused space | (12 on 32-bit MCUs)
|
||
|
* ... ...
|
||
|
* | end chunk |
|
||
|
* +---------------+
|
||
|
* @endverbatim
|
||
|
*/
|
||
|
typedef struct PmHeapDesc_s
|
||
|
{
|
||
|
/** Heap descriptor */
|
||
|
uint16_t hd;
|
||
|
|
||
|
/** Ptr to prev heap chunk */
|
||
|
struct PmHeapDesc_s *prev;
|
||
|
|
||
|
/** Ptr to next heap chunk */
|
||
|
struct PmHeapDesc_s *next;
|
||
|
} PmHeapDesc_t,
|
||
|
*pPmHeapDesc_t;
|
||
|
|
||
|
typedef struct PmHeap_s
|
||
|
{
|
||
|
/*
|
||
|
* WARNING: Leave 'base' field at the top of struct to increase chance
|
||
|
* of alignment when compiler doesn't recognize the aligned attribute
|
||
|
* which is specific to GCC
|
||
|
*/
|
||
|
/** Global declaration of heap. */
|
||
|
uint8_t base[PM_HEAP_SIZE];
|
||
|
|
||
|
/** Ptr to list of free chunks; sorted smallest to largest. */
|
||
|
pPmHeapDesc_t pfreelist;
|
||
|
|
||
|
/** The amount of heap space available in free list */
|
||
|
#if PM_HEAP_SIZE > 65535
|
||
|
uint32_t avail;
|
||
|
#else
|
||
|
uint16_t avail;
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_GC
|
||
|
/** Garbage collection mark value */
|
||
|
uint8_t gcval;
|
||
|
|
||
|
/** Boolean to indicate if GC should run automatically */
|
||
|
uint8_t auto_gc;
|
||
|
|
||
|
/* #239: Fix GC when 2+ unlinked allocs occur */
|
||
|
/** Stack of objects to be held as temporary roots */
|
||
|
pPmObj_t temp_roots[HEAP_NUM_TEMP_ROOTS];
|
||
|
|
||
|
uint8_t temp_root_index;
|
||
|
#endif /* HAVE_GC */
|
||
|
|
||
|
} PmHeap_t,
|
||
|
*pPmHeap_t;
|
||
|
|
||
|
|
||
|
/** The PyMite heap */
|
||
|
static PmHeap_t pmHeap PM_PLAT_HEAP_ATTR;
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
static void
|
||
|
heap_gcPrintFreelist(void)
|
||
|
{
|
||
|
pPmHeapDesc_t pchunk = pmHeap.pfreelist;
|
||
|
|
||
|
printf("DEBUG: pmHeap.avail = %d\n", pmHeap.avail);
|
||
|
printf("DEBUG: freelist:\n");
|
||
|
while (pchunk != C_NULL)
|
||
|
{
|
||
|
printf("DEBUG: free chunk (%d bytes) @ 0x%0x\n",
|
||
|
OBJ_GET_SIZE(pchunk), (int)pchunk);
|
||
|
pchunk = pchunk->next;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
/** DEBUG: dumps the heap and roots list to a file */
|
||
|
static void
|
||
|
heap_dump(void)
|
||
|
{
|
||
|
static int n = 0;
|
||
|
uint16_t s;
|
||
|
uint32_t i;
|
||
|
void *b;
|
||
|
char filename[32];
|
||
|
FILE *fp;
|
||
|
|
||
|
snprintf(filename, 32, "pmheapdump%02d.bin", n++);
|
||
|
fp = fopen(filename, "wb");
|
||
|
|
||
|
/* magic : PMDUMP for little endian or PMUDMP for big endian */
|
||
|
fwrite(&"PM", 1, 2, fp);
|
||
|
s = 0x5544;
|
||
|
fwrite(&s, sizeof(uint16_t), 1, fp);
|
||
|
fwrite(&"MP", 1, 2, fp);
|
||
|
|
||
|
/* pointer size */
|
||
|
s = sizeof(intptr_t);
|
||
|
fwrite(&s, sizeof(uint16_t), 1, fp);
|
||
|
|
||
|
/* dump version */
|
||
|
s = 1;
|
||
|
fwrite(&s, sizeof(uint16_t), 1, fp);
|
||
|
|
||
|
/* pmfeatures */
|
||
|
s = 0;
|
||
|
#ifdef USE_STRING_CACHE
|
||
|
s |= 1<<0;
|
||
|
#endif
|
||
|
#ifdef HAVE_DEFAULTARGS
|
||
|
s |= 1<<1;
|
||
|
#endif
|
||
|
#ifdef HAVE_CLOSURES
|
||
|
s |= 1<<2;
|
||
|
#endif
|
||
|
#ifdef HAVE_CLASSES
|
||
|
s |= 1<<3;
|
||
|
#endif
|
||
|
fwrite(&s, sizeof(uint16_t), 1, fp);
|
||
|
|
||
|
/* size of heap */
|
||
|
i = PM_HEAP_SIZE;
|
||
|
fwrite(&i, sizeof(uint32_t), 1, fp);
|
||
|
|
||
|
/* Write base address of heap */
|
||
|
b=&pmHeap.base;
|
||
|
fwrite((void*)(&b), sizeof(intptr_t), 1, fp);
|
||
|
|
||
|
/* Write contents of heap */
|
||
|
fwrite(&pmHeap.base, 1, PM_HEAP_SIZE, fp);
|
||
|
|
||
|
/* Write num roots*/
|
||
|
i = 10;
|
||
|
fwrite(&i, sizeof(uint32_t), 1, fp);
|
||
|
|
||
|
/* Write heap root ptrs */
|
||
|
fwrite((void *)&gVmGlobal.pnone, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.pfalse, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.ptrue, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.pzero, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.pone, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.pnegone, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.pcodeStr, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.builtins, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.nativeframe, sizeof(intptr_t), 1, fp);
|
||
|
fwrite((void *)&gVmGlobal.threadList, sizeof(intptr_t), 1, fp);
|
||
|
fclose(fp);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/* Removes the given chunk from the free list; leaves list in sorted order */
|
||
|
static PmReturn_t
|
||
|
heap_unlinkFromFreelist(pPmHeapDesc_t pchunk)
|
||
|
{
|
||
|
C_ASSERT(pchunk != C_NULL);
|
||
|
|
||
|
pmHeap.avail -= OBJ_GET_SIZE(pchunk);
|
||
|
|
||
|
if (pchunk->next != C_NULL)
|
||
|
{
|
||
|
pchunk->next->prev = pchunk->prev;
|
||
|
}
|
||
|
|
||
|
/* If pchunk was the first chunk in the free list, update the heap ptr */
|
||
|
if (pchunk->prev == C_NULL)
|
||
|
{
|
||
|
pmHeap.pfreelist = pchunk->next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pchunk->prev->next = pchunk->next;
|
||
|
}
|
||
|
|
||
|
return PM_RET_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Inserts in order a chunk into the free list. Caller adjusts heap state */
|
||
|
static PmReturn_t
|
||
|
heap_linkToFreelist(pPmHeapDesc_t pchunk)
|
||
|
{
|
||
|
uint16_t size;
|
||
|
pPmHeapDesc_t pscan;
|
||
|
|
||
|
/* Ensure the object is already free */
|
||
|
C_ASSERT(OBJ_GET_FREE(pchunk) != 0);
|
||
|
|
||
|
pmHeap.avail += OBJ_GET_SIZE(pchunk);
|
||
|
|
||
|
/* If free list is empty, add to head of list */
|
||
|
if (pmHeap.pfreelist == C_NULL)
|
||
|
{
|
||
|
pmHeap.pfreelist = pchunk;
|
||
|
pchunk->next = C_NULL;
|
||
|
pchunk->prev = C_NULL;
|
||
|
|
||
|
return PM_RET_OK;
|
||
|
}
|
||
|
|
||
|
/* Scan free list for insertion point */
|
||
|
pscan = pmHeap.pfreelist;
|
||
|
size = OBJ_GET_SIZE(pchunk);
|
||
|
while ((OBJ_GET_SIZE(pscan) < size) && (pscan->next != C_NULL))
|
||
|
{
|
||
|
pscan = pscan->next;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Insert chunk after the scan chunk (next is NULL).
|
||
|
* This is a slightly rare case where the last chunk in the free list
|
||
|
* is smaller than the chunk being freed.
|
||
|
*/
|
||
|
if (size > OBJ_GET_SIZE(pscan))
|
||
|
{
|
||
|
pchunk->next = pscan->next;
|
||
|
pscan->next = pchunk;
|
||
|
pchunk->prev = pscan;
|
||
|
}
|
||
|
|
||
|
/* Insert chunk before the scan chunk */
|
||
|
else
|
||
|
{
|
||
|
pchunk->next = pscan;
|
||
|
pchunk->prev = pscan->prev;
|
||
|
|
||
|
/* If chunk will be first item in free list */
|
||
|
if (pscan->prev == C_NULL)
|
||
|
{
|
||
|
pmHeap.pfreelist = pchunk;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pscan->prev->next = pchunk;
|
||
|
}
|
||
|
pscan->prev = pchunk;
|
||
|
}
|
||
|
|
||
|
return PM_RET_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Initializes the heap state variables
|
||
|
*/
|
||
|
PmReturn_t
|
||
|
heap_init(void)
|
||
|
{
|
||
|
pPmHeapDesc_t pchunk;
|
||
|
|
||
|
#if PM_HEAP_SIZE > 65535
|
||
|
uint32_t hs;
|
||
|
#else
|
||
|
uint16_t hs;
|
||
|
#endif
|
||
|
|
||
|
#if __DEBUG__
|
||
|
/* Fill the heap with a non-NULL value to bring out any heap bugs. */
|
||
|
sli_memset(pmHeap.base, 0xAA, sizeof(pmHeap.base));
|
||
|
#endif
|
||
|
|
||
|
/* Init heap globals */
|
||
|
pmHeap.pfreelist = C_NULL;
|
||
|
pmHeap.avail = 0;
|
||
|
#ifdef HAVE_GC
|
||
|
pmHeap.gcval = (uint8_t)0;
|
||
|
pmHeap.temp_root_index = (uint8_t)0;
|
||
|
heap_gcSetAuto(C_TRUE);
|
||
|
#endif /* HAVE_GC */
|
||
|
|
||
|
/* Create as many max-sized chunks as possible in the freelist */
|
||
|
for (pchunk = (pPmHeapDesc_t)pmHeap.base, hs = PM_HEAP_SIZE;
|
||
|
hs >= HEAP_MAX_FREE_CHUNK_SIZE; hs -= HEAP_MAX_FREE_CHUNK_SIZE)
|
||
|
{
|
||
|
OBJ_SET_FREE(pchunk, 1);
|
||
|
OBJ_SET_SIZE(pchunk, HEAP_MAX_FREE_CHUNK_SIZE);
|
||
|
heap_linkToFreelist(pchunk);
|
||
|
pchunk =
|
||
|
(pPmHeapDesc_t)((uint8_t *)pchunk + HEAP_MAX_FREE_CHUNK_SIZE);
|
||
|
}
|
||
|
|
||
|
/* Add any leftover memory to the freelist */
|
||
|
if (hs >= HEAP_MIN_CHUNK_SIZE)
|
||
|
{
|
||
|
/* Round down to a multiple of four */
|
||
|
hs = hs & ~3;
|
||
|
OBJ_SET_FREE(pchunk, 1);
|
||
|
OBJ_SET_SIZE(pchunk, hs);
|
||
|
heap_linkToFreelist(pchunk);
|
||
|
}
|
||
|
|
||
|
C_DEBUG_PRINT(VERBOSITY_LOW, "heap_init(), id=%p, s=%d\n",
|
||
|
pmHeap.base, pmHeap.avail);
|
||
|
|
||
|
string_cacheInit();
|
||
|
|
||
|
return PM_RET_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Obtains a chunk of memory from the free list
|
||
|
*
|
||
|
* Performs the Best Fit algorithm.
|
||
|
* Iterates through the freelist to see if a chunk of suitable size exists.
|
||
|
* Shaves a chunk to perfect size iff the remainder is greater than
|
||
|
* the minimum chunk size.
|
||
|
*
|
||
|
* @param size Requested chunk size
|
||
|
* @param r_pchunk Return ptr to chunk
|
||
|
* @return Return status
|
||
|
*/
|
||
|
static PmReturn_t
|
||
|
heap_getChunkImpl(uint16_t size, uint8_t **r_pchunk)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
pPmHeapDesc_t pchunk;
|
||
|
pPmHeapDesc_t premainderChunk;
|
||
|
|
||
|
C_ASSERT(r_pchunk != C_NULL);
|
||
|
|
||
|
/* Skip to the first chunk that can hold the requested size */
|
||
|
pchunk = pmHeap.pfreelist;
|
||
|
while ((pchunk != C_NULL) && (OBJ_GET_SIZE(pchunk) < size))
|
||
|
{
|
||
|
pchunk = pchunk->next;
|
||
|
}
|
||
|
|
||
|
/* No chunk of appropriate size was found, raise OutOfMemory exception */
|
||
|
if (pchunk == C_NULL)
|
||
|
{
|
||
|
*r_pchunk = C_NULL;
|
||
|
PM_RAISE(retval, PM_RET_EX_MEM);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Remove the chunk from the free list */
|
||
|
retval = heap_unlinkFromFreelist(pchunk);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Check if a chunk should be carved from what is available */
|
||
|
if (OBJ_GET_SIZE(pchunk) - size >= HEAP_MIN_CHUNK_SIZE)
|
||
|
{
|
||
|
/* Create the heap descriptor for the remainder chunk */
|
||
|
premainderChunk = (pPmHeapDesc_t)((uint8_t *)pchunk + size);
|
||
|
OBJ_SET_FREE(premainderChunk, 1);
|
||
|
OBJ_SET_SIZE(premainderChunk, OBJ_GET_SIZE(pchunk) - size);
|
||
|
|
||
|
/* Put the remainder chunk back in the free list */
|
||
|
retval = heap_linkToFreelist(premainderChunk);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Convert the chunk from a heap descriptor to an object descriptor */
|
||
|
OBJ_SET_SIZE(pchunk, 0);
|
||
|
OBJ_SET_FREE(pchunk, 0);
|
||
|
OBJ_SET_SIZE(pchunk, size);
|
||
|
|
||
|
C_DEBUG_PRINT(VERBOSITY_HIGH,
|
||
|
"heap_getChunkImpl()carved, id=%p, s=%d\n", pchunk,
|
||
|
size);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Set chunk's type to none (overwrites size field's high byte) */
|
||
|
OBJ_SET_TYPE((pPmObj_t)pchunk, OBJ_TYPE_NON);
|
||
|
OBJ_SET_FREE(pchunk, 0);
|
||
|
|
||
|
C_DEBUG_PRINT(VERBOSITY_HIGH,
|
||
|
"heap_getChunkImpl()exact, id=%p, s=%d\n", pchunk,
|
||
|
OBJ_GET_SIZE(pchunk));
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Set the chunk's GC mark so it will be collected during the next GC cycle
|
||
|
* if it is not reachable
|
||
|
*/
|
||
|
OBJ_SET_GCVAL(pchunk, pmHeap.gcval);
|
||
|
|
||
|
/* Return the chunk */
|
||
|
*r_pchunk = (uint8_t *)pchunk;
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Allocates chunk of memory.
|
||
|
* Filters out invalid sizes.
|
||
|
* Rounds the size up to the next multiple of 4.
|
||
|
* Obtains a chunk of at least the desired size.
|
||
|
*/
|
||
|
PmReturn_t
|
||
|
heap_getChunk(uint16_t requestedsize, uint8_t **r_pchunk)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
uint16_t adjustedsize;
|
||
|
|
||
|
/* Ensure size request is valid */
|
||
|
if (requestedsize > HEAP_MAX_LIVE_CHUNK_SIZE)
|
||
|
{
|
||
|
PM_RAISE(retval, PM_RET_EX_MEM);
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
else if (requestedsize < HEAP_MIN_CHUNK_SIZE)
|
||
|
{
|
||
|
requestedsize = HEAP_MIN_CHUNK_SIZE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Round up the size to a multiple of 4 bytes.
|
||
|
* This maintains alignment on 32-bit platforms (required).
|
||
|
*/
|
||
|
adjustedsize = ((requestedsize + 3) & ~3);
|
||
|
|
||
|
/* Attempt to get a chunk */
|
||
|
retval = heap_getChunkImpl(adjustedsize, r_pchunk);
|
||
|
|
||
|
#ifdef HAVE_GC
|
||
|
/* Perform GC if out of memory, gc is enabled and not in native session */
|
||
|
if ((retval == PM_RET_EX_MEM) && (pmHeap.auto_gc == C_TRUE)
|
||
|
&& (gVmGlobal.nativeframe.nf_active == C_FALSE))
|
||
|
{
|
||
|
retval = heap_gcRun();
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Attempt to get a chunk */
|
||
|
retval = heap_getChunkImpl(adjustedsize, r_pchunk);
|
||
|
}
|
||
|
#endif /* HAVE_GC */
|
||
|
|
||
|
/* Ensure that the pointer is 4-byte aligned */
|
||
|
if (retval == PM_RET_OK)
|
||
|
{
|
||
|
C_ASSERT(((intptr_t)*r_pchunk & 3) == 0);
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Releases chunk to the free list */
|
||
|
PmReturn_t
|
||
|
heap_freeChunk(pPmObj_t ptr)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
|
||
|
C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_freeChunk(), id=%p, s=%d\n",
|
||
|
ptr, OBJ_GET_SIZE(ptr));
|
||
|
|
||
|
/* Ensure the chunk falls within the heap */
|
||
|
C_ASSERT(((uint8_t *)ptr >= pmHeap.base)
|
||
|
&& ((uint8_t *)ptr < pmHeap.base + PM_HEAP_SIZE));
|
||
|
|
||
|
/* Insert the chunk into the freelist */
|
||
|
OBJ_SET_FREE(ptr, 1);
|
||
|
|
||
|
/* Clear type so that heap descriptor's size's upper byte is zero */
|
||
|
OBJ_SET_TYPE(ptr, 0);
|
||
|
retval = heap_linkToFreelist((pPmHeapDesc_t)ptr);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Returns, by reference, the number of bytes available in the heap */
|
||
|
#if PM_HEAP_SIZE > 65535
|
||
|
uint32_t
|
||
|
#else
|
||
|
uint16_t
|
||
|
#endif
|
||
|
heap_getAvail(void)
|
||
|
{
|
||
|
return pmHeap.avail;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef HAVE_GC
|
||
|
/*
|
||
|
* Marks the given object and the objects it references.
|
||
|
*
|
||
|
* @param pobj Any non-free heap object
|
||
|
* @return Return code
|
||
|
*/
|
||
|
static PmReturn_t
|
||
|
heap_gcMarkObj(pPmObj_t pobj)
|
||
|
{
|
||
|
PmReturn_t retval = PM_RET_OK;
|
||
|
int16_t i = 0;
|
||
|
int16_t n;
|
||
|
PmType_t type;
|
||
|
|
||
|
/* Return if ptr is null or object is already marked */
|
||
|
if (pobj == C_NULL)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
if (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* The pointer must be within the heap (native frame is special case) */
|
||
|
C_ASSERT((((uint8_t *)pobj >= &pmHeap.base[0])
|
||
|
&& ((uint8_t *)pobj <= &pmHeap.base[PM_HEAP_SIZE]))
|
||
|
|| ((uint8_t *)pobj == (uint8_t *)&gVmGlobal.nativeframe));
|
||
|
|
||
|
/* The object must not already be free */
|
||
|
C_ASSERT(OBJ_GET_FREE(pobj) == 0);
|
||
|
|
||
|
type = (PmType_t)OBJ_GET_TYPE(pobj);
|
||
|
switch (type)
|
||
|
{
|
||
|
/* Objects with no references to other objects */
|
||
|
case OBJ_TYPE_NON:
|
||
|
case OBJ_TYPE_INT:
|
||
|
case OBJ_TYPE_FLT:
|
||
|
case OBJ_TYPE_STR:
|
||
|
case OBJ_TYPE_NOB:
|
||
|
case OBJ_TYPE_BOOL:
|
||
|
case OBJ_TYPE_CIO:
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_TUP:
|
||
|
i = ((pPmTuple_t)pobj)->length;
|
||
|
|
||
|
/* Mark tuple head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark each obj in tuple */
|
||
|
while (--i >= 0)
|
||
|
{
|
||
|
retval = heap_gcMarkObj(((pPmTuple_t)pobj)->val[i]);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_LST:
|
||
|
|
||
|
/* Mark the list */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the seglist */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmList_t)pobj)->val);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_DIC:
|
||
|
/* Mark the dict head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the keys seglist */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_keys);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the vals seglist */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_vals);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_COB:
|
||
|
/* Mark the code obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the names tuple */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_names);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the consts tuple */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_consts);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* #122: Mark the code image if it is in RAM */
|
||
|
if (((pPmCo_t)pobj)->co_memspace == MEMSPACE_RAM)
|
||
|
{
|
||
|
retval = heap_gcMarkObj((pPmObj_t)
|
||
|
(((pPmCo_t)pobj)->co_codeimgaddr));
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
}
|
||
|
|
||
|
#ifdef HAVE_CLOSURES
|
||
|
/* #256: Add support for closures */
|
||
|
/* Mark the cellvars tuple */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_cellvars);
|
||
|
#endif /* HAVE_CLOSURES */
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_MOD:
|
||
|
case OBJ_TYPE_FXN:
|
||
|
/* Module and Func objs are implemented via the PmFunc_t */
|
||
|
/* Mark the func obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the code obj */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_co);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the attr dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_attrs);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the globals dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_globals);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
#ifdef HAVE_DEFAULTARGS
|
||
|
/* Mark the default args tuple */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_defaultargs);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
#endif /* HAVE_DEFAULTARGS */
|
||
|
|
||
|
#ifdef HAVE_CLOSURES
|
||
|
/* #256: Mark the closure tuple */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_closure);
|
||
|
#endif /* HAVE_CLOSURES */
|
||
|
break;
|
||
|
|
||
|
#ifdef HAVE_CLASSES
|
||
|
case OBJ_TYPE_CLI:
|
||
|
/* Mark the obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the class */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_class);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the attrs dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_attrs);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_MTH:
|
||
|
/* Mark the obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the instance */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_instance);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the func */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_func);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the attrs dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_attrs);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_CLO:
|
||
|
/* Mark the obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the attrs dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_attrs);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the base tuple */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_bases);
|
||
|
break;
|
||
|
#endif /* HAVE_CLASSES */
|
||
|
|
||
|
/*
|
||
|
* An obj in ram should not be of these types.
|
||
|
* Images arrive in RAM as string objects (image is array of bytes)
|
||
|
*/
|
||
|
case OBJ_TYPE_CIM:
|
||
|
case OBJ_TYPE_NIM:
|
||
|
PM_RAISE(retval, PM_RET_EX_SYS);
|
||
|
return retval;
|
||
|
|
||
|
case OBJ_TYPE_FRM:
|
||
|
{
|
||
|
pPmObj_t *ppobj2 = C_NULL;
|
||
|
|
||
|
/* Mark the frame obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the previous frame, if this isn't a generator's frame */
|
||
|
/* Issue #129: Fix iterator losing its object */
|
||
|
if ((((pPmFrame_t)pobj)->fo_func->f_co->co_flags & CO_GENERATOR) == 0)
|
||
|
{
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_back);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
}
|
||
|
|
||
|
/* Mark the fxn obj */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_func);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the blockstack */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)
|
||
|
((pPmFrame_t)pobj)->fo_blockstack);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the attrs dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_attrs);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the globals dict */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_globals);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark each obj in the locals list and the stack */
|
||
|
ppobj2 = ((pPmFrame_t)pobj)->fo_locals;
|
||
|
while (ppobj2 < ((pPmFrame_t)pobj)->fo_sp)
|
||
|
{
|
||
|
retval = heap_gcMarkObj(*ppobj2);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
ppobj2++;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case OBJ_TYPE_BLK:
|
||
|
/* Mark the block obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the next block in the stack */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmBlock_t)pobj)->next);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_SGL:
|
||
|
/* Mark the seglist obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the seglist's segments */
|
||
|
n = ((pSeglist_t)pobj)->sl_length;
|
||
|
pobj = (pPmObj_t)((pSeglist_t)pobj)->sl_rootseg;
|
||
|
for (i = 0; i < n; i++)
|
||
|
{
|
||
|
/* Mark the segment item */
|
||
|
retval = heap_gcMarkObj(((pSegment_t)pobj)->s_val[i % SEGLIST_OBJS_PER_SEG]);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the segment obj head */
|
||
|
if ((i % SEGLIST_OBJS_PER_SEG) == 0)
|
||
|
{
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
}
|
||
|
|
||
|
/* Point to the next segment */
|
||
|
else
|
||
|
if ((i % SEGLIST_OBJS_PER_SEG) == (SEGLIST_OBJS_PER_SEG - 1))
|
||
|
{
|
||
|
pobj = (pPmObj_t)((pSegment_t)pobj)->next;
|
||
|
if (pobj == C_NULL)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_SQI:
|
||
|
/* Mark the sequence iterator obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the sequence */
|
||
|
retval = heap_gcMarkObj(((pPmSeqIter_t)pobj)->si_sequence);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_THR:
|
||
|
/* Mark the thread obj head */
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the current frame */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmThread_t)pobj)->pframe);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_NFM:
|
||
|
/*
|
||
|
* Mark the obj desc. This doesn't really do much since the
|
||
|
* native frame is declared static (not from the heap), but this
|
||
|
* is here in case that ever changes
|
||
|
*/
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
/* Mark the native frame's remaining fields if active */
|
||
|
if (gVmGlobal.nativeframe.nf_active)
|
||
|
{
|
||
|
/* Mark the frame stack */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)
|
||
|
gVmGlobal.nativeframe.nf_back);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the function object */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)
|
||
|
gVmGlobal.nativeframe.nf_func);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the stack object */
|
||
|
retval = heap_gcMarkObj(gVmGlobal.nativeframe.nf_stack);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the args to the native func */
|
||
|
for (i = 0; i < NATIVE_GET_NUM_ARGS(); i++)
|
||
|
{
|
||
|
retval =
|
||
|
heap_gcMarkObj(gVmGlobal.nativeframe.nf_locals[i]);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
#ifdef HAVE_BYTEARRAY
|
||
|
case OBJ_TYPE_BYA:
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
|
||
|
retval = heap_gcMarkObj((pPmObj_t)((pPmBytearray_t)pobj)->val);
|
||
|
break;
|
||
|
|
||
|
case OBJ_TYPE_BYS:
|
||
|
OBJ_SET_GCVAL(pobj, pmHeap.gcval);
|
||
|
break;
|
||
|
#endif /* HAVE_BYTEARRAY */
|
||
|
|
||
|
default:
|
||
|
/* There should be no invalid types */
|
||
|
PM_RAISE(retval, PM_RET_EX_SYS);
|
||
|
break;
|
||
|
}
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Marks the root objects so they won't be collected during the sweep phase.
|
||
|
* Recursively marks all objects reachable from the roots.
|
||
|
*/
|
||
|
static PmReturn_t
|
||
|
heap_gcMarkRoots(void)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
uint8_t i;
|
||
|
|
||
|
/* Toggle the GC marking value so it differs from the last run */
|
||
|
pmHeap.gcval ^= 1;
|
||
|
|
||
|
/* Mark the constant objects */
|
||
|
retval = heap_gcMarkObj(PM_NONE);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
retval = heap_gcMarkObj(PM_FALSE);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
retval = heap_gcMarkObj(PM_TRUE);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
retval = heap_gcMarkObj(PM_ZERO);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
retval = heap_gcMarkObj(PM_ONE);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
retval = heap_gcMarkObj(PM_NEGONE);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
retval = heap_gcMarkObj(PM_CODE_STR);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the builtins dict */
|
||
|
retval = heap_gcMarkObj(PM_PBUILTINS);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the native frame if it is active */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)&gVmGlobal.nativeframe);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the thread list */
|
||
|
retval = heap_gcMarkObj((pPmObj_t)gVmGlobal.threadList);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Mark the temporary roots */
|
||
|
for (i = 0; i < pmHeap.temp_root_index; i++)
|
||
|
{
|
||
|
retval = heap_gcMarkObj(pmHeap.temp_roots[i]);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
#if USE_STRING_CACHE
|
||
|
/**
|
||
|
* Unlinks free objects from the string cache.
|
||
|
* This function must only be called by the GC after the heap has been marked
|
||
|
* and before the heap has been swept.
|
||
|
*
|
||
|
* This solves the problem where a string object would be collected
|
||
|
* but its chunk was still linked into the free list
|
||
|
*
|
||
|
* @param gcval The current value for chunks marked by the GC
|
||
|
*/
|
||
|
static PmReturn_t
|
||
|
heap_purgeStringCache(uint8_t gcval)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
pPmString_t *ppstrcache;
|
||
|
pPmString_t pstr;
|
||
|
|
||
|
/* Update string cache pointer if the first string objs are not marked */
|
||
|
retval = string_getCache(&ppstrcache);
|
||
|
if (ppstrcache == C_NULL)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
while ((*ppstrcache != C_NULL) && (OBJ_GET_GCVAL(*ppstrcache) != gcval))
|
||
|
{
|
||
|
*ppstrcache = (*ppstrcache)->next;
|
||
|
}
|
||
|
if (*ppstrcache == C_NULL)
|
||
|
{
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
/* Unlink remaining strings that are not marked */
|
||
|
for (pstr = *ppstrcache; pstr->next != C_NULL;)
|
||
|
{
|
||
|
/* Unlink consecutive non-marked strings */
|
||
|
while ((pstr->next != C_NULL) && (OBJ_GET_GCVAL(pstr->next) != gcval))
|
||
|
{
|
||
|
pstr->next = pstr->next->next;
|
||
|
}
|
||
|
|
||
|
/* If not at end of cache, string must be marked, skip it */
|
||
|
if (pstr->next != C_NULL)
|
||
|
{
|
||
|
pstr = pstr->next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return retval;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Reclaims any object that does not have a current mark.
|
||
|
* Puts it in the free list. Coalesces all contiguous free chunks.
|
||
|
*/
|
||
|
static PmReturn_t
|
||
|
heap_gcSweep(void)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
pPmObj_t pobj;
|
||
|
pPmHeapDesc_t pchunk;
|
||
|
uint16_t totalchunksize;
|
||
|
|
||
|
#if USE_STRING_CACHE
|
||
|
retval = heap_purgeStringCache(pmHeap.gcval);
|
||
|
#endif
|
||
|
|
||
|
/* Start at the base of the heap */
|
||
|
pobj = (pPmObj_t)pmHeap.base;
|
||
|
while ((uint8_t *)pobj < &pmHeap.base[PM_HEAP_SIZE])
|
||
|
{
|
||
|
/* Skip to the next unmarked or free chunk within the heap */
|
||
|
while (!OBJ_GET_FREE(pobj)
|
||
|
&& (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
|
||
|
&& ((uint8_t *)pobj < &pmHeap.base[PM_HEAP_SIZE]))
|
||
|
{
|
||
|
pobj = (pPmObj_t)((uint8_t *)pobj + OBJ_GET_SIZE(pobj));
|
||
|
}
|
||
|
|
||
|
/* Stop if reached the end of the heap */
|
||
|
if ((uint8_t *)pobj >= &pmHeap.base[PM_HEAP_SIZE])
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Accumulate the sizes of all consecutive unmarked or free chunks */
|
||
|
totalchunksize = 0;
|
||
|
|
||
|
/* Coalesce all contiguous free chunks */
|
||
|
pchunk = (pPmHeapDesc_t)pobj;
|
||
|
while (OBJ_GET_FREE(pchunk)
|
||
|
|| (!OBJ_GET_FREE(pchunk)
|
||
|
&& (OBJ_GET_GCVAL(pchunk) != pmHeap.gcval)))
|
||
|
{
|
||
|
if ((totalchunksize + OBJ_GET_SIZE(pchunk))
|
||
|
> HEAP_MAX_FREE_CHUNK_SIZE)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
totalchunksize = totalchunksize + OBJ_GET_SIZE(pchunk);
|
||
|
|
||
|
/*
|
||
|
* If the chunk is already free, unlink it because its size
|
||
|
* is about to change
|
||
|
*/
|
||
|
if (OBJ_GET_FREE(pchunk))
|
||
|
{
|
||
|
retval = heap_unlinkFromFreelist(pchunk);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
}
|
||
|
|
||
|
/* Otherwise free and reclaim the unmarked chunk */
|
||
|
else
|
||
|
{
|
||
|
OBJ_SET_TYPE(pchunk, 0);
|
||
|
OBJ_SET_FREE(pchunk, 1);
|
||
|
}
|
||
|
|
||
|
C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_gcSweep(), id=%p, s=%d\n",
|
||
|
pchunk, OBJ_GET_SIZE(pchunk));
|
||
|
|
||
|
/* Proceed to the next chunk */
|
||
|
pchunk = (pPmHeapDesc_t)
|
||
|
((uint8_t *)pchunk + OBJ_GET_SIZE(pchunk));
|
||
|
|
||
|
/* Stop if it's past the end of the heap */
|
||
|
if ((uint8_t *)pchunk >= &pmHeap.base[PM_HEAP_SIZE])
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Set the heap descriptor data */
|
||
|
OBJ_SET_FREE(pobj, 1);
|
||
|
OBJ_SET_SIZE(pobj, totalchunksize);
|
||
|
|
||
|
/* Insert chunk into free list */
|
||
|
retval = heap_linkToFreelist((pPmHeapDesc_t)pobj);
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
/* Continue to the next chunk */
|
||
|
pobj = (pPmObj_t)pchunk;
|
||
|
}
|
||
|
|
||
|
return PM_RET_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Runs the mark-sweep garbage collector */
|
||
|
PmReturn_t
|
||
|
heap_gcRun(void)
|
||
|
{
|
||
|
PmReturn_t retval;
|
||
|
|
||
|
/* #239: Fix GC when 2+ unlinked allocs occur */
|
||
|
/* This assertion fails when there are too many objects on the temporary
|
||
|
* root stack and a GC occurs; consider increasing PM_HEAP_NUM_TEMP_ROOTS
|
||
|
*/
|
||
|
C_ASSERT(pmHeap.temp_root_index < HEAP_NUM_TEMP_ROOTS);
|
||
|
|
||
|
C_DEBUG_PRINT(VERBOSITY_LOW, "heap_gcRun()\n");
|
||
|
/*heap_dump();*/
|
||
|
|
||
|
retval = heap_gcMarkRoots();
|
||
|
PM_RETURN_IF_ERROR(retval);
|
||
|
|
||
|
retval = heap_gcSweep();
|
||
|
/*heap_dump();*/
|
||
|
return retval;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Enables or disables automatic garbage collection */
|
||
|
PmReturn_t
|
||
|
heap_gcSetAuto(uint8_t auto_gc)
|
||
|
{
|
||
|
pmHeap.auto_gc = auto_gc;
|
||
|
return PM_RET_OK;
|
||
|
}
|
||
|
|
||
|
void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid)
|
||
|
{
|
||
|
if (pmHeap.temp_root_index < HEAP_NUM_TEMP_ROOTS)
|
||
|
{
|
||
|
*r_objid = pmHeap.temp_root_index;
|
||
|
pmHeap.temp_roots[pmHeap.temp_root_index] = pobj;
|
||
|
pmHeap.temp_root_index++;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
void heap_gcPopTempRoot(uint8_t objid)
|
||
|
{
|
||
|
pmHeap.temp_root_index = objid;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid) {}
|
||
|
void heap_gcPopTempRoot(uint8_t objid) {}
|
||
|
|
||
|
#endif /* HAVE_GC */
|