/* 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); /* 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; }
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; } }
/* 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 + 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); /* Increase heap available amount */ pmHeap.avail += OBJ_GET_SIZE(ptr); return retval; }
/* 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; }
/** * 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; }
/* * 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; }
/* * Reclaims any object that doesn't 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; uint16_t additionalheapsize; /* Start at the base of the heap */ pobj = (pPmObj_t)pmHeap.base; while ((uint8_t *)pobj < &pmHeap.base[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[HEAP_SIZE])) { pobj = (pPmObj_t)((uint8_t *)pobj + OBJ_GET_SIZE(pobj)); /*printf("Object is at addr <%x>\n",(uint32_t)pobj);*/ #if 0 printf("pobj=");obj_print(pobj,0);printf("; type=%x; size=%x\n",OBJ_GET_TYPE(pobj),OBJ_GET_SIZE(pobj)); #endif } /* Stop if reached the end of the heap */ if ((uint8_t *)pobj >= &pmHeap.base[HEAP_SIZE]) { break; } /* Accumulate the sizes of all consecutive unmarked or free chunks */ totalchunksize = 0; additionalheapsize = 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))) { 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); additionalheapsize += OBJ_GET_SIZE(pchunk); } C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_gcSweep(), id=%p, s=%d\n", pchunk, OBJ_GET_SIZE(pchunk)); #if 0 /* It's possible for 0-sized chunks to exist (which makes no sense) TODO:solve */ /* They result in infinite loops, so we must curtail them */ if (OBJ_GET_SIZE(pchunk) == 0) { break; } #endif /* 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[HEAP_SIZE]) { break; } } /* Adjust the heap stats */ pmHeap.avail += additionalheapsize; /* Set the heap descriptor data */ OBJ_SET_FREE(pobj, 1); OBJ_SET_SIZE(pobj, totalchunksize); #if 0 /* avoid loops by breaking on 0-size */ if (totalchunksize == 0) { break; } #endif /* 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; }