heap.c

Go to the documentation of this file.
00001 /*
00002 # This file is Copyright 2003, 2006, 2007, 2009, 2010 Dean Hall.
00003 #
00004 # This file is part of the PyMite VM.
00005 # The PyMite VM is free software: you can redistribute it and/or modify
00006 # it under the terms of the GNU GENERAL PUBLIC LICENSE Version 2.
00007 #
00008 # The PyMite VM is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00011 # A copy of the GNU GENERAL PUBLIC LICENSE Version 2
00012 # is seen in the file COPYING in this directory.
00013 */
00014 
00015 
00016 #undef __FILE_ID__
00017 #define __FILE_ID__ 0x06
00018 
00019 
00030 #include "pm.h"
00031 
00032 
00034 #ifndef PM_HEAP_SIZE
00035 #warning PM_HEAP_SIZE not defined in src/platform/<yourplatform>/pmfeatures.h
00036 #elif PM_HEAP_SIZE & 3
00037 #error PM_HEAP_SIZE is not a multiple of four
00038 #endif
00039 
00040 
00042 #define HEAP_NUM_TEMP_ROOTS 24
00043 
00050 #define HEAP_MAX_LIVE_CHUNK_SIZE 2044
00051 
00058 #define HEAP_MAX_FREE_CHUNK_SIZE 65532
00059 
00061 #define HEAP_MIN_CHUNK_SIZE ((sizeof(PmHeapDesc_t) + 3) & ~3)
00062 
00063 
00068 #define OBJ_GET_GCVAL(pobj) ((((pPmObj_t)pobj)->od >> OD_MARK_SHIFT) & 1)
00069 
00074 #ifdef HAVE_GC
00075 #define OBJ_SET_GCVAL(pobj, gcval) \
00076     do \
00077     { \
00078         ((pPmObj_t)pobj)->od = (gcval) ? ((pPmObj_t)pobj)->od | OD_MARK_BIT \
00079                                        : ((pPmObj_t)pobj)->od & ~OD_MARK_BIT;\
00080     } \
00081     while (0)
00082 #else
00083 #define OBJ_SET_GCVAL(pobj, gcval)
00084 #endif /* HAVE_GC */
00085 
00086 
00108 typedef struct PmHeapDesc_s
00109 {
00111     uint16_t hd;
00112 
00114     struct PmHeapDesc_s *prev;
00115 
00117     struct PmHeapDesc_s *next;
00118 } PmHeapDesc_t,
00119  *pPmHeapDesc_t;
00120 
00121 typedef struct PmHeap_s
00122 {
00123     /*
00124      * WARNING: Leave 'base' field at the top of struct to increase chance
00125      * of alignment when compiler doesn't recognize the aligned attribute
00126      * which is specific to GCC
00127      */
00129     uint8_t base[PM_HEAP_SIZE];
00130 
00132     pPmHeapDesc_t pfreelist;
00133 
00135 #if PM_HEAP_SIZE > 65535
00136     uint32_t avail;
00137 #else
00138     uint16_t avail;
00139 #endif
00140 
00141 #ifdef HAVE_GC
00142 
00143     uint8_t gcval;
00144 
00146     uint8_t auto_gc;
00147 
00148     /* #239: Fix GC when 2+ unlinked allocs occur */
00150     pPmObj_t temp_roots[HEAP_NUM_TEMP_ROOTS];
00151 
00152     uint8_t temp_root_index;
00153 #endif                          /* HAVE_GC */
00154 
00155 } PmHeap_t,
00156  *pPmHeap_t;
00157 
00158 
00160 static PmHeap_t pmHeap PM_PLAT_HEAP_ATTR;
00161 
00162 
00163 #if 0
00164 static void
00165 heap_gcPrintFreelist(void)
00166 {
00167     pPmHeapDesc_t pchunk = pmHeap.pfreelist;
00168 
00169     printf("DEBUG: pmHeap.avail = %d\n", pmHeap.avail);
00170     printf("DEBUG: freelist:\n");
00171     while (pchunk != C_NULL)
00172     {
00173         printf("DEBUG:     free chunk (%d bytes) @ 0x%0x\n",
00174                OBJ_GET_SIZE(pchunk), (int)pchunk);
00175         pchunk = pchunk->next;
00176     }
00177 }
00178 #endif
00179 
00180 
00181 #if 0
00182 
00183 static void
00184 heap_dump(void)
00185 {
00186     static int n = 0;
00187     uint16_t s;
00188     uint32_t i;
00189     void *b;
00190     char filename[32];
00191     FILE *fp;
00192 
00193     snprintf(filename, 32, "pmheapdump%02d.bin", n++);
00194     fp = fopen(filename, "wb");
00195 
00196     /* magic : PMDUMP for little endian or PMUDMP for big endian */
00197     fwrite(&"PM", 1, 2, fp);
00198     s = 0x5544;
00199     fwrite(&s, sizeof(uint16_t), 1, fp);
00200     fwrite(&"MP", 1, 2, fp);
00201 
00202     /* pointer size */
00203     s = sizeof(intptr_t);
00204     fwrite(&s, sizeof(uint16_t), 1, fp);
00205 
00206     /* dump version */
00207     s = 1;
00208     fwrite(&s, sizeof(uint16_t), 1, fp);    
00209 
00210     /* pmfeatures */
00211     s = 0;
00212 #ifdef USE_STRING_CACHE
00213     s |= 1<<0;
00214 #endif
00215 #ifdef HAVE_DEFAULTARGS
00216     s |= 1<<1;
00217 #endif
00218 #ifdef HAVE_CLOSURES
00219     s |= 1<<2;
00220 #endif
00221 #ifdef HAVE_CLASSES
00222     s |= 1<<3;
00223 #endif
00224     fwrite(&s, sizeof(uint16_t), 1, fp);
00225 
00226     /* size of heap */
00227     i = PM_HEAP_SIZE;
00228     fwrite(&i, sizeof(uint32_t), 1, fp);
00229 
00230     /* Write base address of heap */
00231     b=&pmHeap.base;
00232     fwrite((void*)(&b), sizeof(intptr_t), 1, fp);
00233 
00234     /* Write contents of heap */
00235     fwrite(&pmHeap.base, 1, PM_HEAP_SIZE, fp);
00236 
00237     /* Write num roots*/
00238     i = 10;
00239     fwrite(&i, sizeof(uint32_t), 1, fp);
00240 
00241     /* Write heap root ptrs */
00242     fwrite((void *)&gVmGlobal.pnone, sizeof(intptr_t), 1, fp);
00243     fwrite((void *)&gVmGlobal.pfalse, sizeof(intptr_t), 1, fp);
00244     fwrite((void *)&gVmGlobal.ptrue, sizeof(intptr_t), 1, fp);
00245     fwrite((void *)&gVmGlobal.pzero, sizeof(intptr_t), 1, fp);
00246     fwrite((void *)&gVmGlobal.pone, sizeof(intptr_t), 1, fp);
00247     fwrite((void *)&gVmGlobal.pnegone, sizeof(intptr_t), 1, fp);
00248     fwrite((void *)&gVmGlobal.pcodeStr, sizeof(intptr_t), 1, fp);
00249     fwrite((void *)&gVmGlobal.builtins, sizeof(intptr_t), 1, fp);
00250     fwrite((void *)&gVmGlobal.nativeframe, sizeof(intptr_t), 1, fp);
00251     fwrite((void *)&gVmGlobal.threadList, sizeof(intptr_t), 1, fp);
00252     fclose(fp);
00253 }
00254 #endif
00255 
00256 
00257 /* Removes the given chunk from the free list; leaves list in sorted order */
00258 static PmReturn_t
00259 heap_unlinkFromFreelist(pPmHeapDesc_t pchunk)
00260 {
00261     C_ASSERT(pchunk != C_NULL);
00262 
00263     pmHeap.avail -= OBJ_GET_SIZE(pchunk);
00264 
00265     if (pchunk->next != C_NULL)
00266     {
00267         pchunk->next->prev = pchunk->prev;
00268     }
00269 
00270     /* If pchunk was the first chunk in the free list, update the heap ptr */
00271     if (pchunk->prev == C_NULL)
00272     {
00273         pmHeap.pfreelist = pchunk->next;
00274     }
00275     else
00276     {
00277         pchunk->prev->next = pchunk->next;
00278     }
00279 
00280     return PM_RET_OK;
00281 }
00282 
00283 
00284 /* Inserts in order a chunk into the free list.  Caller adjusts heap state */
00285 static PmReturn_t
00286 heap_linkToFreelist(pPmHeapDesc_t pchunk)
00287 {
00288     uint16_t size;
00289     pPmHeapDesc_t pscan;
00290 
00291     /* Ensure the object is already free */
00292     C_ASSERT(OBJ_GET_FREE(pchunk) != 0);
00293 
00294     pmHeap.avail += OBJ_GET_SIZE(pchunk);
00295 
00296     /* If free list is empty, add to head of list */
00297     if (pmHeap.pfreelist == C_NULL)
00298     {
00299         pmHeap.pfreelist = pchunk;
00300         pchunk->next = C_NULL;
00301         pchunk->prev = C_NULL;
00302 
00303         return PM_RET_OK;
00304     }
00305 
00306     /* Scan free list for insertion point */
00307     pscan = pmHeap.pfreelist;
00308     size = OBJ_GET_SIZE(pchunk);
00309     while ((OBJ_GET_SIZE(pscan) < size) && (pscan->next != C_NULL))
00310     {
00311         pscan = pscan->next;
00312     }
00313 
00314     /*
00315      * Insert chunk after the scan chunk (next is NULL).
00316      * This is a slightly rare case where the last chunk in the free list
00317      * is smaller than the chunk being freed.
00318      */
00319     if (size > OBJ_GET_SIZE(pscan))
00320     {
00321         pchunk->next = pscan->next;
00322         pscan->next = pchunk;
00323         pchunk->prev = pscan;
00324     }
00325 
00326     /* Insert chunk before the scan chunk */
00327     else
00328     {
00329         pchunk->next = pscan;
00330         pchunk->prev = pscan->prev;
00331 
00332         /* If chunk will be first item in free list */
00333         if (pscan->prev == C_NULL)
00334         {
00335             pmHeap.pfreelist = pchunk;
00336         }
00337         else
00338         {
00339             pscan->prev->next = pchunk;
00340         }
00341         pscan->prev = pchunk;
00342     }
00343 
00344     return PM_RET_OK;
00345 }
00346 
00347 
00348 /*
00349  * Initializes the heap state variables
00350  */
00351 PmReturn_t
00352 heap_init(void)
00353 {
00354     pPmHeapDesc_t pchunk;
00355 
00356 #if PM_HEAP_SIZE > 65535
00357     uint32_t hs;
00358 #else
00359     uint16_t hs;
00360 #endif
00361 
00362 #if __DEBUG__
00363     /* Fill the heap with a non-NULL value to bring out any heap bugs. */
00364     sli_memset(pmHeap.base, 0xAA, sizeof(pmHeap.base));
00365 #endif
00366 
00367     /* Init heap globals */
00368     pmHeap.pfreelist = C_NULL;
00369     pmHeap.avail = 0;
00370 #ifdef HAVE_GC
00371     pmHeap.gcval = (uint8_t)0;
00372     pmHeap.temp_root_index = (uint8_t)0;
00373     heap_gcSetAuto(C_TRUE);
00374 #endif /* HAVE_GC */
00375 
00376     /* Create as many max-sized chunks as possible in the freelist */
00377     for (pchunk = (pPmHeapDesc_t)pmHeap.base, hs = PM_HEAP_SIZE;
00378          hs >= HEAP_MAX_FREE_CHUNK_SIZE; hs -= HEAP_MAX_FREE_CHUNK_SIZE)
00379     {
00380         OBJ_SET_FREE(pchunk, 1);
00381         OBJ_SET_SIZE(pchunk, HEAP_MAX_FREE_CHUNK_SIZE);
00382         heap_linkToFreelist(pchunk);
00383         pchunk =
00384             (pPmHeapDesc_t)((uint8_t *)pchunk + HEAP_MAX_FREE_CHUNK_SIZE);
00385     }
00386 
00387     /* Add any leftover memory to the freelist */
00388     if (hs >= HEAP_MIN_CHUNK_SIZE)
00389     {
00390         /* Round down to a multiple of four */
00391         hs = hs & ~3;
00392         OBJ_SET_FREE(pchunk, 1);
00393         OBJ_SET_SIZE(pchunk, hs);
00394         heap_linkToFreelist(pchunk);
00395     }
00396 
00397     C_DEBUG_PRINT(VERBOSITY_LOW, "heap_init(), id=%p, s=%d\n",
00398                   pmHeap.base, pmHeap.avail);
00399 
00400     string_cacheInit();
00401 
00402     return PM_RET_OK;
00403 }
00404 
00405 
00418 static PmReturn_t
00419 heap_getChunkImpl(uint16_t size, uint8_t **r_pchunk)
00420 {
00421     PmReturn_t retval;
00422     pPmHeapDesc_t pchunk;
00423     pPmHeapDesc_t premainderChunk;
00424 
00425     C_ASSERT(r_pchunk != C_NULL);
00426 
00427     /* Skip to the first chunk that can hold the requested size */
00428     pchunk = pmHeap.pfreelist;
00429     while ((pchunk != C_NULL) && (OBJ_GET_SIZE(pchunk) < size))
00430     {
00431         pchunk = pchunk->next;
00432     }
00433 
00434     /* No chunk of appropriate size was found, raise OutOfMemory exception */
00435     if (pchunk == C_NULL)
00436     {
00437         *r_pchunk = C_NULL;
00438         PM_RAISE(retval, PM_RET_EX_MEM);
00439         return retval;
00440     }
00441 
00442     /* Remove the chunk from the free list */
00443     retval = heap_unlinkFromFreelist(pchunk);
00444     PM_RETURN_IF_ERROR(retval);
00445 
00446     /* Check if a chunk should be carved from what is available */
00447     if (OBJ_GET_SIZE(pchunk) - size >= HEAP_MIN_CHUNK_SIZE)
00448     {
00449         /* Create the heap descriptor for the remainder chunk */
00450         premainderChunk = (pPmHeapDesc_t)((uint8_t *)pchunk + size);
00451         OBJ_SET_FREE(premainderChunk, 1);
00452         OBJ_SET_SIZE(premainderChunk, OBJ_GET_SIZE(pchunk) - size);
00453 
00454         /* Put the remainder chunk back in the free list */
00455         retval = heap_linkToFreelist(premainderChunk);
00456         PM_RETURN_IF_ERROR(retval);
00457 
00458         /* Convert the chunk from a heap descriptor to an object descriptor */
00459         OBJ_SET_SIZE(pchunk, 0);
00460         OBJ_SET_FREE(pchunk, 0);
00461         OBJ_SET_SIZE(pchunk, size);
00462 
00463         C_DEBUG_PRINT(VERBOSITY_HIGH,
00464                       "heap_getChunkImpl()carved, id=%p, s=%d\n", pchunk,
00465                       size);
00466     }
00467     else
00468     {
00469         /* Set chunk's type to none (overwrites size field's high byte) */
00470         OBJ_SET_TYPE((pPmObj_t)pchunk, OBJ_TYPE_NON);
00471         OBJ_SET_FREE(pchunk, 0);
00472 
00473         C_DEBUG_PRINT(VERBOSITY_HIGH,
00474                       "heap_getChunkImpl()exact, id=%p, s=%d\n", pchunk,
00475                       OBJ_GET_SIZE(pchunk));
00476     }
00477 
00478     /*
00479      * Set the chunk's GC mark so it will be collected during the next GC cycle
00480      * if it is not reachable
00481      */
00482     OBJ_SET_GCVAL(pchunk, pmHeap.gcval);
00483 
00484     /* Return the chunk */
00485     *r_pchunk = (uint8_t *)pchunk;
00486 
00487     return retval;
00488 }
00489 
00490 
00491 /*
00492  * Allocates chunk of memory.
00493  * Filters out invalid sizes.
00494  * Rounds the size up to the next multiple of 4.
00495  * Obtains a chunk of at least the desired size.
00496  */
00497 PmReturn_t
00498 heap_getChunk(uint16_t requestedsize, uint8_t **r_pchunk)
00499 {
00500     PmReturn_t retval;
00501     uint16_t adjustedsize;
00502 
00503     /* Ensure size request is valid */
00504     if (requestedsize > HEAP_MAX_LIVE_CHUNK_SIZE)
00505     {
00506         PM_RAISE(retval, PM_RET_EX_MEM);
00507         return retval;
00508     }
00509 
00510     else if (requestedsize < HEAP_MIN_CHUNK_SIZE)
00511     {
00512         requestedsize = HEAP_MIN_CHUNK_SIZE;
00513     }
00514 
00515     /*
00516      * Round up the size to a multiple of 4 bytes.
00517      * This maintains alignment on 32-bit platforms (required).
00518      */
00519     adjustedsize = ((requestedsize + 3) & ~3);
00520 
00521     /* Attempt to get a chunk */
00522     retval = heap_getChunkImpl(adjustedsize, r_pchunk);
00523 
00524 #ifdef HAVE_GC
00525     /* Perform GC if out of memory, gc is enabled and not in native session */
00526     if ((retval == PM_RET_EX_MEM) && (pmHeap.auto_gc == C_TRUE)
00527         && (gVmGlobal.nativeframe.nf_active == C_FALSE))
00528     {
00529         retval = heap_gcRun();
00530         PM_RETURN_IF_ERROR(retval);
00531 
00532         /* Attempt to get a chunk */
00533         retval = heap_getChunkImpl(adjustedsize, r_pchunk);
00534     }
00535 #endif /* HAVE_GC */
00536 
00537     /* Ensure that the pointer is 4-byte aligned */
00538     if (retval == PM_RET_OK)
00539     {
00540         C_ASSERT(((intptr_t)*r_pchunk & 3) == 0);
00541     }
00542 
00543     return retval;
00544 }
00545 
00546 
00547 /* Releases chunk to the free list */
00548 PmReturn_t
00549 heap_freeChunk(pPmObj_t ptr)
00550 {
00551     PmReturn_t retval;
00552 
00553     C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_freeChunk(), id=%p, s=%d\n",
00554                   ptr, OBJ_GET_SIZE(ptr));
00555 
00556     /* Ensure the chunk falls within the heap */
00557     C_ASSERT(((uint8_t *)ptr >= pmHeap.base)
00558              && ((uint8_t *)ptr < pmHeap.base + PM_HEAP_SIZE));
00559 
00560     /* Insert the chunk into the freelist */
00561     OBJ_SET_FREE(ptr, 1);
00562 
00563     /* Clear type so that heap descriptor's size's upper byte is zero */
00564     OBJ_SET_TYPE(ptr, 0);
00565     retval = heap_linkToFreelist((pPmHeapDesc_t)ptr);
00566     PM_RETURN_IF_ERROR(retval);
00567 
00568     return retval;
00569 }
00570 
00571 
00572 /* Returns, by reference, the number of bytes available in the heap */
00573 #if PM_HEAP_SIZE > 65535
00574 uint32_t
00575 #else
00576 uint16_t
00577 #endif
00578 heap_getAvail(void)
00579 {
00580     return pmHeap.avail;
00581 }
00582 
00583 
00584 #ifdef HAVE_GC
00585 /*
00586  * Marks the given object and the objects it references.
00587  *
00588  * @param   pobj Any non-free heap object
00589  * @return  Return code
00590  */
00591 static PmReturn_t
00592 heap_gcMarkObj(pPmObj_t pobj)
00593 {
00594     PmReturn_t retval = PM_RET_OK;
00595     int16_t i = 0;
00596     int16_t n;
00597     PmType_t type;
00598 
00599     /* Return if ptr is null or object is already marked */
00600     if (pobj == C_NULL)
00601     {
00602         return retval;
00603     }
00604     if (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
00605     {
00606         return retval;
00607     }
00608 
00609     /* The pointer must be within the heap (native frame is special case) */
00610     C_ASSERT((((uint8_t *)pobj >= &pmHeap.base[0])
00611               && ((uint8_t *)pobj <= &pmHeap.base[PM_HEAP_SIZE]))
00612              || ((uint8_t *)pobj == (uint8_t *)&gVmGlobal.nativeframe));
00613 
00614     /* The object must not already be free */
00615     C_ASSERT(OBJ_GET_FREE(pobj) == 0);
00616 
00617     type = (PmType_t)OBJ_GET_TYPE(pobj);
00618     switch (type)
00619     {
00620             /* Objects with no references to other objects */
00621         case OBJ_TYPE_NON:
00622         case OBJ_TYPE_INT:
00623         case OBJ_TYPE_FLT:
00624         case OBJ_TYPE_STR:
00625         case OBJ_TYPE_NOB:
00626         case OBJ_TYPE_BOOL:
00627         case OBJ_TYPE_CIO:
00628             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00629             break;
00630 
00631         case OBJ_TYPE_TUP:
00632             i = ((pPmTuple_t)pobj)->length;
00633 
00634             /* Mark tuple head */
00635             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00636 
00637             /* Mark each obj in tuple */
00638             while (--i >= 0)
00639             {
00640                 retval = heap_gcMarkObj(((pPmTuple_t)pobj)->val[i]);
00641                 PM_RETURN_IF_ERROR(retval);
00642             }
00643             break;
00644 
00645         case OBJ_TYPE_LST:
00646 
00647             /* Mark the list */
00648             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00649 
00650             /* Mark the seglist */
00651             retval = heap_gcMarkObj((pPmObj_t)((pPmList_t)pobj)->val);
00652             break;
00653 
00654         case OBJ_TYPE_DIC:
00655             /* Mark the dict head */
00656             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00657 
00658             /* Mark the keys seglist */
00659             retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_keys);
00660             PM_RETURN_IF_ERROR(retval);
00661 
00662             /* Mark the vals seglist */
00663             retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_vals);
00664             break;
00665 
00666         case OBJ_TYPE_COB:
00667             /* Mark the code obj head */
00668             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00669 
00670             /* Mark the names tuple */
00671             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_names);
00672             PM_RETURN_IF_ERROR(retval);
00673 
00674             /* Mark the consts tuple */
00675             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_consts);
00676             PM_RETURN_IF_ERROR(retval);
00677 
00678             /* #122: Mark the code image if it is in RAM */
00679             if (((pPmCo_t)pobj)->co_memspace == MEMSPACE_RAM)
00680             {
00681                 retval = heap_gcMarkObj((pPmObj_t)
00682                                         (((pPmCo_t)pobj)->co_codeimgaddr));
00683                 PM_RETURN_IF_ERROR(retval);
00684             }
00685 
00686 #ifdef HAVE_CLOSURES
00687             /* #256: Add support for closures */
00688             /* Mark the cellvars tuple */
00689             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_cellvars);
00690 #endif /* HAVE_CLOSURES */
00691             break;
00692 
00693         case OBJ_TYPE_MOD:
00694         case OBJ_TYPE_FXN:
00695             /* Module and Func objs are implemented via the PmFunc_t */
00696             /* Mark the func obj head */
00697             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00698 
00699             /* Mark the code obj */
00700             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_co);
00701             PM_RETURN_IF_ERROR(retval);
00702 
00703             /* Mark the attr dict */
00704             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_attrs);
00705             PM_RETURN_IF_ERROR(retval);
00706 
00707             /* Mark the globals dict */
00708             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_globals);
00709             PM_RETURN_IF_ERROR(retval);
00710 
00711 #ifdef HAVE_DEFAULTARGS
00712             /* Mark the default args tuple */
00713             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_defaultargs);
00714             PM_RETURN_IF_ERROR(retval);
00715 #endif /* HAVE_DEFAULTARGS */
00716 
00717 #ifdef HAVE_CLOSURES
00718             /* #256: Mark the closure tuple */
00719             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_closure);
00720 #endif /* HAVE_CLOSURES */
00721             break;
00722 
00723 #ifdef HAVE_CLASSES
00724         case OBJ_TYPE_CLI:
00725             /* Mark the obj head */
00726             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00727 
00728             /* Mark the class */
00729             retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_class);
00730             PM_RETURN_IF_ERROR(retval);
00731 
00732             /* Mark the attrs dict */
00733             retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_attrs);
00734             break;
00735 
00736         case OBJ_TYPE_MTH:
00737             /* Mark the obj head */
00738             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00739 
00740             /* Mark the instance */
00741             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_instance);
00742             PM_RETURN_IF_ERROR(retval);
00743 
00744             /* Mark the func */
00745             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_func);
00746             PM_RETURN_IF_ERROR(retval);
00747 
00748             /* Mark the attrs dict */
00749             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_attrs);
00750             break;
00751 
00752         case OBJ_TYPE_CLO:
00753             /* Mark the obj head */
00754             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00755 
00756             /* Mark the attrs dict */
00757             retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_attrs);
00758             PM_RETURN_IF_ERROR(retval);
00759 
00760             /* Mark the base tuple */
00761             retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_bases);
00762             break;
00763 #endif /* HAVE_CLASSES */
00764 
00765             /*
00766              * An obj in ram should not be of these types.
00767              * Images arrive in RAM as string objects (image is array of bytes)
00768              */
00769         case OBJ_TYPE_CIM:
00770         case OBJ_TYPE_NIM:
00771             PM_RAISE(retval, PM_RET_EX_SYS);
00772             return retval;
00773 
00774         case OBJ_TYPE_FRM:
00775         {
00776             pPmObj_t *ppobj2 = C_NULL;
00777 
00778             /* Mark the frame obj head */
00779             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00780 
00781             /* Mark the previous frame, if this isn't a generator's frame */
00782             /* Issue #129: Fix iterator losing its object */
00783             if ((((pPmFrame_t)pobj)->fo_func->f_co->co_flags & CO_GENERATOR) == 0)
00784             {
00785                 retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_back);
00786                 PM_RETURN_IF_ERROR(retval);
00787             }
00788 
00789             /* Mark the fxn obj */
00790             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_func);
00791             PM_RETURN_IF_ERROR(retval);
00792 
00793             /* Mark the blockstack */
00794             retval = heap_gcMarkObj((pPmObj_t)
00795                                     ((pPmFrame_t)pobj)->fo_blockstack);
00796             PM_RETURN_IF_ERROR(retval);
00797 
00798             /* Mark the attrs dict */
00799             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_attrs);
00800             PM_RETURN_IF_ERROR(retval);
00801 
00802             /* Mark the globals dict */
00803             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_globals);
00804             PM_RETURN_IF_ERROR(retval);
00805 
00806             /* Mark each obj in the locals list and the stack */
00807             ppobj2 = ((pPmFrame_t)pobj)->fo_locals;
00808             while (ppobj2 < ((pPmFrame_t)pobj)->fo_sp)
00809             {
00810                 retval = heap_gcMarkObj(*ppobj2);
00811                 PM_RETURN_IF_ERROR(retval);
00812                 ppobj2++;
00813             }
00814             break;
00815         }
00816 
00817         case OBJ_TYPE_BLK:
00818             /* Mark the block obj head */
00819             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00820 
00821             /* Mark the next block in the stack */
00822             retval = heap_gcMarkObj((pPmObj_t)((pPmBlock_t)pobj)->next);
00823             break;
00824 
00825         case OBJ_TYPE_SGL:
00826             /* Mark the seglist obj head */
00827             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00828 
00829             /* Mark the seglist's segments */
00830             n = ((pSeglist_t)pobj)->sl_length;
00831             pobj = (pPmObj_t)((pSeglist_t)pobj)->sl_rootseg;
00832             for (i = 0; i < n; i++)
00833             {
00834                 /* Mark the segment item */
00835                 retval = heap_gcMarkObj(((pSegment_t)pobj)->s_val[i % SEGLIST_OBJS_PER_SEG]);
00836                 PM_RETURN_IF_ERROR(retval);
00837 
00838                 /* Mark the segment obj head */
00839                 if ((i % SEGLIST_OBJS_PER_SEG) == 0)
00840                 {
00841                     OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00842                 }
00843 
00844                 /* Point to the next segment */
00845                 else
00846                 if ((i % SEGLIST_OBJS_PER_SEG) == (SEGLIST_OBJS_PER_SEG - 1))
00847                 {
00848                     pobj = (pPmObj_t)((pSegment_t)pobj)->next;
00849                     if (pobj == C_NULL)
00850                     {
00851                         break;
00852                     }
00853                 }
00854             }
00855             break;
00856 
00857         case OBJ_TYPE_SQI:
00858             /* Mark the sequence iterator obj head */
00859             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00860 
00861             /* Mark the sequence */
00862             retval = heap_gcMarkObj(((pPmSeqIter_t)pobj)->si_sequence);
00863             break;
00864 
00865         case OBJ_TYPE_THR:
00866             /* Mark the thread obj head */
00867             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00868 
00869             /* Mark the current frame */
00870             retval = heap_gcMarkObj((pPmObj_t)((pPmThread_t)pobj)->pframe);
00871             break;
00872 
00873         case OBJ_TYPE_NFM:
00874             /*
00875              * Mark the obj desc.  This doesn't really do much since the
00876              * native frame is declared static (not from the heap), but this
00877              * is here in case that ever changes
00878              */
00879             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00880 
00881             /* Mark the native frame's remaining fields if active */
00882             if (gVmGlobal.nativeframe.nf_active)
00883             {
00884                 /* Mark the frame stack */
00885                 retval = heap_gcMarkObj((pPmObj_t)
00886                                         gVmGlobal.nativeframe.nf_back);
00887                 PM_RETURN_IF_ERROR(retval);
00888 
00889                 /* Mark the function object */
00890                 retval = heap_gcMarkObj((pPmObj_t)
00891                                         gVmGlobal.nativeframe.nf_func);
00892                 PM_RETURN_IF_ERROR(retval);
00893 
00894                 /* Mark the stack object */
00895                 retval = heap_gcMarkObj(gVmGlobal.nativeframe.nf_stack);
00896                 PM_RETURN_IF_ERROR(retval);
00897 
00898                 /* Mark the args to the native func */
00899                 for (i = 0; i < NATIVE_GET_NUM_ARGS(); i++)
00900                 {
00901                     retval =
00902                         heap_gcMarkObj(gVmGlobal.nativeframe.nf_locals[i]);
00903                     PM_RETURN_IF_ERROR(retval);
00904                 }
00905             }
00906             break;
00907 
00908 #ifdef HAVE_BYTEARRAY
00909         case OBJ_TYPE_BYA:
00910             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00911 
00912             retval = heap_gcMarkObj((pPmObj_t)((pPmBytearray_t)pobj)->val);
00913             break;
00914 
00915         case OBJ_TYPE_BYS:
00916             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00917             break;
00918 #endif /* HAVE_BYTEARRAY */
00919 
00920         default:
00921             /* There should be no invalid types */
00922             PM_RAISE(retval, PM_RET_EX_SYS);
00923             break;
00924     }
00925     return retval;
00926 }
00927 
00928 
00929 /*
00930  * Marks the root objects so they won't be collected during the sweep phase.
00931  * Recursively marks all objects reachable from the roots.
00932  */
00933 static PmReturn_t
00934 heap_gcMarkRoots(void)
00935 {
00936     PmReturn_t retval;
00937     uint8_t i;
00938 
00939     /* Toggle the GC marking value so it differs from the last run */
00940     pmHeap.gcval ^= 1;
00941 
00942     /* Mark the constant objects */
00943     retval = heap_gcMarkObj(PM_NONE);
00944     PM_RETURN_IF_ERROR(retval);
00945     retval = heap_gcMarkObj(PM_FALSE);
00946     PM_RETURN_IF_ERROR(retval);
00947     retval = heap_gcMarkObj(PM_TRUE);
00948     PM_RETURN_IF_ERROR(retval);
00949     retval = heap_gcMarkObj(PM_ZERO);
00950     PM_RETURN_IF_ERROR(retval);
00951     retval = heap_gcMarkObj(PM_ONE);
00952     PM_RETURN_IF_ERROR(retval);
00953     retval = heap_gcMarkObj(PM_NEGONE);
00954     PM_RETURN_IF_ERROR(retval);
00955     retval = heap_gcMarkObj(PM_CODE_STR);
00956     PM_RETURN_IF_ERROR(retval);
00957 
00958     /* Mark the builtins dict */
00959     retval = heap_gcMarkObj(PM_PBUILTINS);
00960     PM_RETURN_IF_ERROR(retval);
00961 
00962     /* Mark the native frame if it is active */
00963     retval = heap_gcMarkObj((pPmObj_t)&gVmGlobal.nativeframe);
00964     PM_RETURN_IF_ERROR(retval);
00965 
00966     /* Mark the thread list */
00967     retval = heap_gcMarkObj((pPmObj_t)gVmGlobal.threadList);
00968     PM_RETURN_IF_ERROR(retval);
00969 
00970     /* Mark the temporary roots */
00971     for (i = 0; i < pmHeap.temp_root_index; i++)
00972     {
00973         retval = heap_gcMarkObj(pmHeap.temp_roots[i]);
00974         PM_RETURN_IF_ERROR(retval);
00975     }
00976 
00977     return retval;
00978 }
00979 
00980 
00981 #if USE_STRING_CACHE
00982 
00992 static PmReturn_t
00993 heap_purgeStringCache(uint8_t gcval)
00994 {
00995     PmReturn_t retval;
00996     pPmString_t *ppstrcache;
00997     pPmString_t pstr;
00998 
00999     /* Update string cache pointer if the first string objs are not marked */
01000     retval = string_getCache(&ppstrcache);
01001     if (ppstrcache == C_NULL)
01002     {
01003         return retval;
01004     }
01005     while ((*ppstrcache != C_NULL) && (OBJ_GET_GCVAL(*ppstrcache) != gcval))
01006     {
01007         *ppstrcache = (*ppstrcache)->next;
01008     }
01009     if (*ppstrcache == C_NULL)
01010     {
01011         return retval;
01012     }
01013 
01014     /* Unlink remaining strings that are not marked */
01015     for (pstr = *ppstrcache; pstr->next != C_NULL;)
01016     {
01017         /* Unlink consecutive non-marked strings */
01018         while ((pstr->next != C_NULL) && (OBJ_GET_GCVAL(pstr->next) != gcval))
01019         {
01020             pstr->next = pstr->next->next;
01021         }
01022 
01023         /* If not at end of cache, string must be marked, skip it */
01024         if (pstr->next != C_NULL)
01025         {
01026             pstr = pstr->next;
01027         }
01028     }
01029 
01030     return retval;
01031 }
01032 #endif
01033 
01034 
01035 /*
01036  * Reclaims any object that does not have a current mark.
01037  * Puts it in the free list.  Coalesces all contiguous free chunks.
01038  */
01039 static PmReturn_t
01040 heap_gcSweep(void)
01041 {
01042     PmReturn_t retval;
01043     pPmObj_t pobj;
01044     pPmHeapDesc_t pchunk;
01045     uint16_t totalchunksize;
01046 
01047 #if USE_STRING_CACHE
01048     retval = heap_purgeStringCache(pmHeap.gcval);
01049 #endif
01050 
01051     /* Start at the base of the heap */
01052     pobj = (pPmObj_t)pmHeap.base;
01053     while ((uint8_t *)pobj < &pmHeap.base[PM_HEAP_SIZE])
01054     {
01055         /* Skip to the next unmarked or free chunk within the heap */
01056         while (!OBJ_GET_FREE(pobj)
01057                && (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
01058                && ((uint8_t *)pobj < &pmHeap.base[PM_HEAP_SIZE]))
01059         {
01060             pobj = (pPmObj_t)((uint8_t *)pobj + OBJ_GET_SIZE(pobj));
01061         }
01062 
01063         /* Stop if reached the end of the heap */
01064         if ((uint8_t *)pobj >= &pmHeap.base[PM_HEAP_SIZE])
01065         {
01066             break;
01067         }
01068 
01069         /* Accumulate the sizes of all consecutive unmarked or free chunks */
01070         totalchunksize = 0;
01071 
01072         /* Coalesce all contiguous free chunks */
01073         pchunk = (pPmHeapDesc_t)pobj;
01074         while (OBJ_GET_FREE(pchunk)
01075                || (!OBJ_GET_FREE(pchunk)
01076                    && (OBJ_GET_GCVAL(pchunk) != pmHeap.gcval)))
01077         {
01078             if ((totalchunksize + OBJ_GET_SIZE(pchunk))
01079                 > HEAP_MAX_FREE_CHUNK_SIZE)
01080             {
01081                 break;
01082             }
01083             totalchunksize = totalchunksize + OBJ_GET_SIZE(pchunk);
01084 
01085             /*
01086              * If the chunk is already free, unlink it because its size
01087              * is about to change
01088              */
01089             if (OBJ_GET_FREE(pchunk))
01090             {
01091                 retval = heap_unlinkFromFreelist(pchunk);
01092                 PM_RETURN_IF_ERROR(retval);
01093             }
01094 
01095             /* Otherwise free and reclaim the unmarked chunk */
01096             else
01097             {
01098                 OBJ_SET_TYPE(pchunk, 0);
01099                 OBJ_SET_FREE(pchunk, 1);
01100             }
01101 
01102             C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_gcSweep(), id=%p, s=%d\n",
01103                           pchunk, OBJ_GET_SIZE(pchunk));
01104 
01105             /* Proceed to the next chunk */
01106             pchunk = (pPmHeapDesc_t)
01107                 ((uint8_t *)pchunk + OBJ_GET_SIZE(pchunk));
01108 
01109             /* Stop if it's past the end of the heap */
01110             if ((uint8_t *)pchunk >= &pmHeap.base[PM_HEAP_SIZE])
01111             {
01112                 break;
01113             }
01114         }
01115 
01116         /* Set the heap descriptor data */
01117         OBJ_SET_FREE(pobj, 1);
01118         OBJ_SET_SIZE(pobj, totalchunksize);
01119 
01120         /* Insert chunk into free list */
01121         retval = heap_linkToFreelist((pPmHeapDesc_t)pobj);
01122         PM_RETURN_IF_ERROR(retval);
01123 
01124         /* Continue to the next chunk */
01125         pobj = (pPmObj_t)pchunk;
01126     }
01127 
01128     return PM_RET_OK;
01129 }
01130 
01131 
01132 /* Runs the mark-sweep garbage collector */
01133 PmReturn_t
01134 heap_gcRun(void)
01135 {
01136     PmReturn_t retval;
01137 
01138     /* #239: Fix GC when 2+ unlinked allocs occur */
01139     /* This assertion fails when there are too many objects on the temporary
01140      * root stack and a GC occurs; consider increasing PM_HEAP_NUM_TEMP_ROOTS
01141      */
01142     C_ASSERT(pmHeap.temp_root_index < HEAP_NUM_TEMP_ROOTS);
01143 
01144     C_DEBUG_PRINT(VERBOSITY_LOW, "heap_gcRun()\n");
01145     /*heap_dump();*/
01146 
01147     retval = heap_gcMarkRoots();
01148     PM_RETURN_IF_ERROR(retval);
01149 
01150     retval = heap_gcSweep();
01151     /*heap_dump();*/
01152     return retval;
01153 }
01154 
01155 
01156 /* Enables or disables automatic garbage collection */
01157 PmReturn_t
01158 heap_gcSetAuto(uint8_t auto_gc)
01159 {
01160     pmHeap.auto_gc = auto_gc;
01161     return PM_RET_OK;
01162 }
01163 
01164 void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid)
01165 {
01166     if (pmHeap.temp_root_index < HEAP_NUM_TEMP_ROOTS)
01167     {
01168         *r_objid = pmHeap.temp_root_index;
01169         pmHeap.temp_roots[pmHeap.temp_root_index] = pobj;
01170         pmHeap.temp_root_index++;
01171     }
01172     return;
01173 }
01174 
01175 
01176 void heap_gcPopTempRoot(uint8_t objid)
01177 {
01178     pmHeap.temp_root_index = objid;
01179 }
01180 
01181 #else
01182 
01183 void heap_gcPushTempRoot(pPmObj_t pobj, uint8_t *r_objid) {}
01184 void heap_gcPopTempRoot(uint8_t objid) {}
01185 
01186 #endif /* HAVE_GC */

Generated on Mon Oct 18 07:40:47 2010 for Python-on-a-chip by  doxygen 1.5.9