davide carboni / Mbed 2 deprecated pymite_http_get

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers heap.c Source File

heap.c

Go to the documentation of this file.
00001 /*
00002 # This file is Copyright 2003, 2006, 2007, 2009 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 
00020 /**
00021  * \file
00022  * \brief VM Heap
00023  *
00024  * VM heap operations.
00025  * All of PyMite's dynamic memory is obtained from this heap.
00026  * The heap provides dynamic memory on demand.
00027  */
00028 
00029 
00030 #include "pm.h"
00031 
00032 
00033 /** Checks for heap size definition. */
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 
00041 /**
00042  * The maximum size a live chunk can be (a live chunk is one that is in use).
00043  * The live chunk size is limited by the size field in the *object* descriptor.
00044  * That field is nine bits with two assumed least significant bits (zeros):
00045  * (0x1FF << 2) == 2044
00046  */
00047 #define HEAP_MAX_LIVE_CHUNK_SIZE 2044
00048 
00049 /**
00050  * The maximum size a free chunk can be (a free chunk is one that is not in use).
00051  * The free chunk size is limited by the size field in the *heap* descriptor.
00052  * That field is fourteen bits with two assumed least significant bits (zeros):
00053  * (0x3FFF << 2) == 65532
00054  */
00055 #define HEAP_MAX_FREE_CHUNK_SIZE 65532
00056 
00057 /** The minimum size a chunk can be (rounded up to a multiple of 4) */
00058 #define HEAP_MIN_CHUNK_SIZE ((sizeof(PmHeapDesc_t) + 3) & ~3)
00059 
00060 
00061 /**
00062  * Gets the GC's mark bit for the object.
00063  * This MUST NOT be called on objects that are free.
00064  */
00065 #define OBJ_GET_GCVAL(pobj) ((((pPmObj_t)pobj)->od >> OD_MARK_SHIFT) & 1)
00066 
00067 /**
00068  * Sets the GC's mark bit for the object
00069  * This MUST NOT be called on objects that are free.
00070  */
00071 #ifdef HAVE_GC
00072 #define OBJ_SET_GCVAL(pobj, gcval) \
00073     do \
00074     { \
00075         ((pPmObj_t)pobj)->od = (gcval) ? ((pPmObj_t)pobj)->od | OD_MARK_BIT \
00076                                        : ((pPmObj_t)pobj)->od & ~OD_MARK_BIT;\
00077     } \
00078     while (0)
00079 #else
00080 #define OBJ_SET_GCVAL(pobj, gcval)
00081 #endif /* HAVE_GC */
00082 
00083 
00084 /**
00085  * The following is a diagram of the heap descriptor at the head of the chunk:
00086  *
00087  *                MSb          LSb
00088  *                7 6 5 4 3 2 1 0
00089  *      pchunk-> +-+-+-+-+-+-+-+-+
00090  *               |     S[9:2]    |     S := Size of the chunk (2 LSbs dropped)
00091  *               +-+-+-----------+     F := Chunk free bit (not in use)
00092  *               |F|R| S[15:10]  |     R := Bit reserved for future use
00093  *               +-+-+-----------+
00094  *               |     P(L)      |     P := hd_prev: Pointer to previous node
00095  *               |     P(H)      |     N := hd_next: Pointer to next node
00096  *               |     N(L)      |
00097  *               |     N(H)      |     Theoretical min size == 6
00098  *               +---------------+     Effective min size == 8
00099  *               | unused space  |     (12 on 32-bit MCUs)
00100  *               ...           ...
00101  *               | end chunk     |
00102  *               +---------------+
00103  */
00104 typedef struct PmHeapDesc_s
00105 {
00106     /** Heap descriptor */
00107     uint16_t hd;
00108 
00109     /** Ptr to prev heap chunk */
00110     struct PmHeapDesc_s *prev;
00111 
00112     /** Ptr to next heap chunk */
00113     struct PmHeapDesc_s *next;
00114 } PmHeapDesc_t,
00115  *pPmHeapDesc_t;
00116 
00117 typedef struct PmHeap_s
00118 {
00119     /*
00120      * WARNING: Leave 'base' field at the top of struct to increase chance
00121      * of alignment when compiler doesn't recognize the aligned attribute
00122      * which is specific to GCC
00123      */
00124     /** Global declaration of heap. */
00125     uint8_t base[PM_HEAP_SIZE];
00126 
00127     /** Ptr to list of free chunks; sorted smallest to largest. */
00128     pPmHeapDesc_t pfreelist;
00129 
00130     /** The amount of heap space available in free list */
00131 #if PM_HEAP_SIZE > 65535
00132     uint32_t avail;
00133 #else
00134     uint16_t avail;
00135 #endif
00136 
00137 #ifdef HAVE_GC
00138     /** Garbage collection mark value */
00139     uint8_t gcval;
00140 
00141     /** Boolean to indicate if GC should run automatically */
00142     uint8_t auto_gc;
00143 #endif                          /* HAVE_GC */
00144 
00145 } PmHeap_t,
00146  *pPmHeap_t;
00147 
00148 
00149 /** The PyMite heap */
00150 static PmHeap_t pmHeap;
00151 
00152 
00153 #if 0
00154 static void
00155 heap_gcPrintFreelist(void)
00156 {
00157     pPmHeapDesc_t pchunk = pmHeap.pfreelist;
00158 
00159     printf("DEBUG: pmHeap.avail = %d\n", pmHeap.avail);
00160     printf("DEBUG: freelist:\n");
00161     while (pchunk != C_NULL)
00162     {
00163         printf("DEBUG:     free chunk (%d bytes) @ 0x%0x\n",
00164                OBJ_GET_SIZE(pchunk), (int)pchunk);
00165         pchunk = pchunk->next;
00166     }
00167 }
00168 #endif
00169 
00170 
00171 #if 0
00172 /** DEBUG: dumps the heap and roots list to a file */
00173 static void
00174 heap_dump(void)
00175 {
00176     static int n = 0;
00177     int i;
00178     char filename[32];
00179     FILE *fp;
00180 
00181     snprintf(filename, 32, "pmheapdump%02d.bin", n++);
00182     fp = fopen(filename, "wb");
00183 
00184     /* Write size of heap */
00185     i = PM_HEAP_SIZE;
00186     fwrite(&i, sizeof(int), 1, fp);
00187 
00188     /* Write base address of heap */
00189     i = (int)&pmHeap.base;
00190     fwrite(&i, sizeof(int *), 1, fp);
00191 
00192     /* Write contents of heap */
00193     fwrite(&pmHeap.base, 1, PM_HEAP_SIZE, fp);
00194 
00195     /* Write num roots*/
00196     i = 10;
00197     fwrite(&i, sizeof(int), 1, fp);
00198 
00199     /* Write heap root ptrs */
00200     fwrite((void *)&gVmGlobal.pnone, sizeof(intptr_t), 1, fp);
00201     fwrite((void *)&gVmGlobal.pfalse, sizeof(intptr_t), 1, fp);
00202     fwrite((void *)&gVmGlobal.ptrue, sizeof(intptr_t), 1, fp);
00203     fwrite((void *)&gVmGlobal.pzero, sizeof(intptr_t), 1, fp);
00204     fwrite((void *)&gVmGlobal.pone, sizeof(intptr_t), 1, fp);
00205     fwrite((void *)&gVmGlobal.pnegone, sizeof(intptr_t), 1, fp);
00206     fwrite((void *)&gVmGlobal.pcodeStr, sizeof(intptr_t), 1, fp);
00207     fwrite((void *)&gVmGlobal.builtins, sizeof(intptr_t), 1, fp);
00208     fwrite((void *)&gVmGlobal.nativeframe, sizeof(intptr_t), 1, fp);
00209     fwrite((void *)&gVmGlobal.threadList, sizeof(intptr_t), 1, fp);
00210     fclose(fp);
00211 }
00212 #endif
00213 
00214 
00215 /* Removes the given chunk from the free list; leaves list in sorted order */
00216 static PmReturn_t
00217 heap_unlinkFromFreelist(pPmHeapDesc_t pchunk)
00218 {
00219     C_ASSERT(pchunk != C_NULL);
00220 
00221     pmHeap.avail -= OBJ_GET_SIZE(pchunk);
00222 
00223     if (pchunk->next != C_NULL)
00224     {
00225         pchunk->next->prev = pchunk->prev;
00226     }
00227 
00228     /* If pchunk was the first chunk in the free list, update the heap ptr */
00229     if (pchunk->prev == C_NULL)
00230     {
00231         pmHeap.pfreelist = pchunk->next;
00232     }
00233     else
00234     {
00235         pchunk->prev->next = pchunk->next;
00236     }
00237 
00238     return PM_RET_OK;
00239 }
00240 
00241 
00242 /* Inserts in order a chunk into the free list.  Caller adjusts heap state */
00243 static PmReturn_t
00244 heap_linkToFreelist(pPmHeapDesc_t pchunk)
00245 {
00246     uint16_t size;
00247     pPmHeapDesc_t pscan;
00248 
00249     /* Ensure the object is already free */
00250     C_ASSERT(OBJ_GET_FREE(pchunk) != 0);
00251 
00252     pmHeap.avail += OBJ_GET_SIZE(pchunk);
00253 
00254     /* If free list is empty, add to head of list */
00255     if (pmHeap.pfreelist == C_NULL)
00256     {
00257         pmHeap.pfreelist = pchunk;
00258         pchunk->next = C_NULL;
00259         pchunk->prev = C_NULL;
00260 
00261         return PM_RET_OK;
00262     }
00263 
00264     /* Scan free list for insertion point */
00265     pscan = pmHeap.pfreelist;
00266     size = OBJ_GET_SIZE(pchunk);
00267     while ((OBJ_GET_SIZE(pscan) < size) && (pscan->next != C_NULL))
00268     {
00269         pscan = pscan->next;
00270     }
00271 
00272     /*
00273      * Insert chunk after the scan chunk (next is NULL).
00274      * This is a slightly rare case where the last chunk in the free list
00275      * is smaller than the chunk being freed.
00276      */
00277     if (size > OBJ_GET_SIZE(pscan))
00278     {
00279         pchunk->next = pscan->next;
00280         pscan->next = pchunk;
00281         pchunk->prev = pscan;
00282     }
00283 
00284     /* Insert chunk before the scan chunk */
00285     else
00286     {
00287         pchunk->next = pscan;
00288         pchunk->prev = pscan->prev;
00289 
00290         /* If chunk will be first item in free list */
00291         if (pscan->prev == C_NULL)
00292         {
00293             pmHeap.pfreelist = pchunk;
00294         }
00295         else
00296         {
00297             pscan->prev->next = pchunk;
00298         }
00299         pscan->prev = pchunk;
00300     }
00301 
00302     return PM_RET_OK;
00303 }
00304 
00305 
00306 /*
00307  * Initializes the heap state variables
00308  */
00309 PmReturn_t
00310 heap_init(void)
00311 {
00312     pPmHeapDesc_t pchunk;
00313 
00314 #if PM_HEAP_SIZE > 65535
00315     uint32_t hs;
00316 #else
00317     uint16_t hs;
00318 #endif
00319 
00320     /* Init heap globals */
00321     pmHeap.pfreelist = C_NULL;
00322     pmHeap.avail = 0;
00323 #ifdef HAVE_GC
00324     pmHeap.gcval = (uint8_t)0;
00325     heap_gcSetAuto(C_TRUE);
00326 #endif /* HAVE_GC */
00327 
00328     /* Create as many max-sized chunks as possible in the freelist */
00329     for (pchunk = (pPmHeapDesc_t)pmHeap.base, hs = PM_HEAP_SIZE;
00330          hs >= HEAP_MAX_FREE_CHUNK_SIZE; hs -= HEAP_MAX_FREE_CHUNK_SIZE)
00331     {
00332         OBJ_SET_FREE(pchunk, 1);
00333         OBJ_SET_SIZE(pchunk, HEAP_MAX_FREE_CHUNK_SIZE);
00334         heap_linkToFreelist(pchunk);
00335         pchunk =
00336             (pPmHeapDesc_t)((uint8_t *)pchunk + HEAP_MAX_FREE_CHUNK_SIZE);
00337     }
00338 
00339     /* Add any leftover memory to the freelist */
00340     if (hs >= HEAP_MIN_CHUNK_SIZE)
00341     {
00342         /* Round down to a multiple of four */
00343         hs = hs & ~3;
00344         OBJ_SET_FREE(pchunk, 1);
00345         OBJ_SET_SIZE(pchunk, hs);
00346         heap_linkToFreelist(pchunk);
00347     }
00348 
00349     C_DEBUG_PRINT(VERBOSITY_LOW, "heap_init(), id=%p, s=%d\n",
00350                   pmHeap.base, pmHeap.avail);
00351 
00352     string_cacheInit();
00353 
00354     return PM_RET_OK;
00355 }
00356 
00357 
00358 /**
00359  * Obtains a chunk of memory from the free list
00360  *
00361  * Performs the Best Fit algorithm.
00362  * Iterates through the freelist to see if a chunk of suitable size exists.
00363  * Shaves a chunk to perfect size iff the remainder is greater than
00364  * the minimum chunk size.
00365  *
00366  * @param size Requested chunk size
00367  * @param r_pchunk Return ptr to chunk
00368  * @return Return status
00369  */
00370 static PmReturn_t
00371 heap_getChunkImpl(uint16_t size, uint8_t **r_pchunk)
00372 {
00373     PmReturn_t retval;
00374     pPmHeapDesc_t pchunk;
00375     pPmHeapDesc_t premainderChunk;
00376 
00377     C_ASSERT(r_pchunk != C_NULL);
00378 
00379     /* Skip to the first chunk that can hold the requested size */
00380     pchunk = pmHeap.pfreelist;
00381     while ((pchunk != C_NULL) && (OBJ_GET_SIZE(pchunk) < size))
00382     {
00383         pchunk = pchunk->next;
00384     }
00385 
00386     /* No chunk of appropriate size was found, raise OutOfMemory exception */
00387     if (pchunk == C_NULL)
00388     {
00389         *r_pchunk = C_NULL;
00390         PM_RAISE(retval, PM_RET_EX_MEM);
00391         return retval;
00392     }
00393 
00394     /* Remove the chunk from the free list */
00395     retval = heap_unlinkFromFreelist(pchunk);
00396     PM_RETURN_IF_ERROR(retval);
00397 
00398     /* Check if a chunk should be carved from what is available */
00399     if (OBJ_GET_SIZE(pchunk) - size >= HEAP_MIN_CHUNK_SIZE)
00400     {
00401         /* Create the heap descriptor for the remainder chunk */
00402         premainderChunk = (pPmHeapDesc_t)((uint8_t *)pchunk + size);
00403         OBJ_SET_FREE(premainderChunk, 1);
00404         OBJ_SET_SIZE(premainderChunk, OBJ_GET_SIZE(pchunk) - size);
00405 
00406         /* Put the remainder chunk back in the free list */
00407         retval = heap_linkToFreelist(premainderChunk);
00408         PM_RETURN_IF_ERROR(retval);
00409 
00410         /* Convert the chunk from a heap descriptor to an object descriptor */
00411         OBJ_SET_SIZE(pchunk, 0);
00412         OBJ_SET_FREE(pchunk, 0);
00413         OBJ_SET_SIZE(pchunk, size);
00414 
00415         C_DEBUG_PRINT(VERBOSITY_HIGH,
00416                       "heap_getChunkImpl()carved, id=%p, s=%d\n", pchunk,
00417                       size);
00418     }
00419     else
00420     {
00421         /* Set chunk's type to none (overwrites size field's high byte) */
00422         OBJ_SET_TYPE((pPmObj_t)pchunk, OBJ_TYPE_NON);
00423         OBJ_SET_FREE(pchunk, 0);
00424 
00425         C_DEBUG_PRINT(VERBOSITY_HIGH,
00426                       "heap_getChunkImpl()exact, id=%p, s=%d\n", pchunk,
00427                       OBJ_GET_SIZE(pchunk));
00428     }
00429 
00430     /*
00431      * Set the chunk's GC mark so it will be collected during the next GC cycle
00432      * if it is not reachable
00433      */
00434     OBJ_SET_GCVAL(pchunk, pmHeap.gcval);
00435 
00436     /* Return the chunk */
00437     *r_pchunk = (uint8_t *)pchunk;
00438 
00439     return retval;
00440 }
00441 
00442 
00443 /*
00444  * Allocates chunk of memory.
00445  * Filters out invalid sizes.
00446  * Rounds the size up to the next multiple of 4.
00447  * Obtains a chunk of at least the desired size.
00448  */
00449 PmReturn_t
00450 heap_getChunk(uint16_t requestedsize, uint8_t **r_pchunk)
00451 {
00452     PmReturn_t retval;
00453     uint16_t adjustedsize;
00454 
00455     /* Ensure size request is valid */
00456     if (requestedsize > HEAP_MAX_LIVE_CHUNK_SIZE)
00457     {
00458         PM_RAISE(retval, PM_RET_EX_MEM);
00459         return retval;
00460     }
00461 
00462     else if (requestedsize < HEAP_MIN_CHUNK_SIZE)
00463     {
00464         requestedsize = HEAP_MIN_CHUNK_SIZE;
00465     }
00466 
00467     /*
00468      * Round up the size to a multiple of 4 bytes.
00469      * This maintains alignment on 32-bit platforms (required).
00470      */
00471     adjustedsize = ((requestedsize + 3) & ~3);
00472 
00473     /* Attempt to get a chunk */
00474     retval = heap_getChunkImpl(adjustedsize, r_pchunk);
00475 
00476 #ifdef HAVE_GC
00477     /* Perform GC if out of memory, gc is enabled and not in native session */
00478     if ((retval == PM_RET_EX_MEM) && (pmHeap.auto_gc == C_TRUE)
00479         && (gVmGlobal.nativeframe.nf_active == C_FALSE))
00480     {
00481         retval = heap_gcRun();
00482         PM_RETURN_IF_ERROR(retval);
00483 
00484         /* Attempt to get a chunk */
00485         retval = heap_getChunkImpl(adjustedsize, r_pchunk);
00486     }
00487 #endif /* HAVE_GC */
00488 
00489     /* Ensure that the pointer is 4-byte aligned */
00490     if (retval == PM_RET_OK)
00491     {
00492         C_ASSERT(((intptr_t)*r_pchunk & 3) == 0);
00493     }
00494 
00495     return retval;
00496 }
00497 
00498 
00499 /* Releases chunk to the free list */
00500 PmReturn_t
00501 heap_freeChunk(pPmObj_t ptr)
00502 {
00503     PmReturn_t retval;
00504 
00505     C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_freeChunk(), id=%p, s=%d\n",
00506                   ptr, OBJ_GET_SIZE(ptr));
00507 
00508     /* Ensure the chunk falls within the heap */
00509     C_ASSERT(((uint8_t *)ptr >= pmHeap.base)
00510              && ((uint8_t *)ptr < pmHeap.base + PM_HEAP_SIZE));
00511 
00512     /* Insert the chunk into the freelist */
00513     OBJ_SET_FREE(ptr, 1);
00514 
00515     /* Clear type so that heap descriptor's size's upper byte is zero */
00516     OBJ_SET_TYPE(ptr, 0);
00517     retval = heap_linkToFreelist((pPmHeapDesc_t)ptr);
00518     PM_RETURN_IF_ERROR(retval);
00519 
00520     return retval;
00521 }
00522 
00523 
00524 /* Returns, by reference, the number of bytes available in the heap */
00525 #if PM_HEAP_SIZE > 65535
00526 uint32_t
00527 #else
00528 uint16_t
00529 #endif
00530 heap_getAvail (void)
00531 {
00532     return pmHeap.avail;
00533 }
00534 
00535 
00536 #ifdef HAVE_GC
00537 /*
00538  * Marks the given object and the objects it references.
00539  *
00540  * @param   pobj Any non-free heap object
00541  * @return  Return code
00542  */
00543 static PmReturn_t
00544 heap_gcMarkObj(pPmObj_t pobj)
00545 {
00546     PmReturn_t retval = PM_RET_OK;
00547     int16_t i = 0;
00548     PmType_t type;
00549 
00550     /* Return if ptr is null or object is already marked */
00551     if ((pobj == C_NULL) || (OBJ_GET_GCVAL(pobj) == pmHeap.gcval))
00552     {
00553         return retval;
00554     }
00555 
00556     /* The pointer must be within the heap (native frame is special case) */
00557     C_ASSERT((((uint8_t *)pobj >= &pmHeap.base[0])
00558               && ((uint8_t *)pobj <= &pmHeap.base[PM_HEAP_SIZE]))
00559              || ((uint8_t *)pobj == (uint8_t *)&gVmGlobal.nativeframe));
00560 
00561     /* The object must not already be free */
00562     C_ASSERT(OBJ_GET_FREE(pobj) == 0);
00563 
00564     type = (PmType_t)OBJ_GET_TYPE(pobj);
00565     switch (type)
00566     {
00567             /* Objects with no references to other objects */
00568         case OBJ_TYPE_NON:
00569         case OBJ_TYPE_INT:
00570         case OBJ_TYPE_FLT:
00571         case OBJ_TYPE_STR:
00572         case OBJ_TYPE_NOB:
00573         case OBJ_TYPE_BOOL:
00574         case OBJ_TYPE_CIO:
00575             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00576             break;
00577 
00578         case OBJ_TYPE_TUP:
00579             i = ((pPmTuple_t)pobj)->length;
00580 
00581             /* Mark tuple head */
00582             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00583 
00584             /* Mark each obj in tuple */
00585             while (--i >= 0)
00586             {
00587                 retval = heap_gcMarkObj(((pPmTuple_t)pobj)->val[i]);
00588                 PM_RETURN_IF_ERROR(retval);
00589             }
00590             break;
00591 
00592         case OBJ_TYPE_LST:
00593 
00594             /* Mark the list */
00595             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00596 
00597             /* Mark the seglist */
00598             retval = heap_gcMarkObj((pPmObj_t)((pPmList_t)pobj)->val);
00599             break;
00600 
00601         case OBJ_TYPE_DIC:
00602             /* Mark the dict head */
00603             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00604 
00605             /* Mark the keys seglist */
00606             retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_keys);
00607             PM_RETURN_IF_ERROR(retval);
00608 
00609             /* Mark the vals seglist */
00610             retval = heap_gcMarkObj((pPmObj_t)((pPmDict_t)pobj)->d_vals);
00611             break;
00612 
00613         case OBJ_TYPE_COB:
00614             /* Mark the code obj head */
00615             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00616 
00617             /* Mark the names tuple */
00618             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_names);
00619             PM_RETURN_IF_ERROR(retval);
00620 
00621             /* Mark the consts tuple */
00622             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_consts);
00623             PM_RETURN_IF_ERROR(retval);
00624 
00625             /* #122: Mark the code image if it is in RAM */
00626             if (((pPmCo_t)pobj)->co_memspace == MEMSPACE_RAM)
00627             {
00628                 retval = heap_gcMarkObj((pPmObj_t)
00629                                         (((pPmCo_t)pobj)->co_codeimgaddr));
00630             }
00631 
00632 #ifdef HAVE_CLOSURES
00633             /* #256: Add support for closures */
00634             /* Mark the cellvars tuple */
00635             retval = heap_gcMarkObj((pPmObj_t)((pPmCo_t)pobj)->co_cellvars);
00636 #endif /* HAVE_CLOSURES */
00637             break;
00638 
00639         case OBJ_TYPE_MOD:
00640         case OBJ_TYPE_FXN:
00641             /* Module and Func objs are implemented via the PmFunc_t */
00642             /* Mark the func obj head */
00643             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00644 
00645             /* Mark the code obj */
00646             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_co);
00647             PM_RETURN_IF_ERROR(retval);
00648 
00649             /* Mark the attr dict */
00650             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_attrs);
00651             PM_RETURN_IF_ERROR(retval);
00652 
00653 #ifdef HAVE_DEFAULTARGS
00654             /* Mark the default args tuple */
00655             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_defaultargs);
00656 #endif /* HAVE_DEFAULTARGS */
00657 
00658 #ifdef HAVE_CLOSURES
00659             /* #256: Mark the closure tuple */
00660             retval = heap_gcMarkObj((pPmObj_t)((pPmFunc_t)pobj)->f_closure);
00661 #endif /* HAVE_CLOSURES */
00662             break;
00663 
00664 #ifdef HAVE_CLASSES
00665         case OBJ_TYPE_CLI:
00666             /* Mark the obj head */
00667             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00668 
00669             /* Mark the class */
00670             retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_class);
00671             PM_RETURN_IF_ERROR(retval);
00672 
00673             /* Mark the attrs dict */
00674             retval = heap_gcMarkObj((pPmObj_t)((pPmInstance_t)pobj)->cli_attrs);
00675             PM_RETURN_IF_ERROR(retval);
00676             break;
00677 
00678         case OBJ_TYPE_MTH:
00679             /* Mark the obj head */
00680             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00681 
00682             /* Mark the instance */
00683             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_instance);
00684             PM_RETURN_IF_ERROR(retval);
00685 
00686             /* Mark the func */
00687             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_func);
00688             PM_RETURN_IF_ERROR(retval);
00689 
00690             /* Mark the attrs dict */
00691             retval = heap_gcMarkObj((pPmObj_t)((pPmMethod_t)pobj)->m_attrs);
00692             PM_RETURN_IF_ERROR(retval);
00693             break;
00694 
00695         case OBJ_TYPE_CLO:
00696             /* Mark the obj head */
00697             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00698 
00699             /* Mark the attrs dict */
00700             retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_attrs);
00701             PM_RETURN_IF_ERROR(retval);
00702 
00703             /* Mark the base tuple */
00704             retval = heap_gcMarkObj((pPmObj_t)((pPmClass_t)pobj)->cl_bases);
00705             break;
00706 #endif /* HAVE_CLASSES */
00707 
00708             /*
00709              * An obj in ram should not be of these types.
00710              * Images arrive in RAM as string objects (image is array of bytes)
00711              */
00712         case OBJ_TYPE_CIM:
00713         case OBJ_TYPE_NIM:
00714             PM_RAISE(retval, PM_RET_EX_SYS);
00715             return retval;
00716 
00717         case OBJ_TYPE_FRM:
00718         {
00719             pPmObj_t *ppobj2 = C_NULL;
00720 
00721             /* Mark the frame obj head */
00722             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00723 
00724             /* Mark the previous frame */
00725             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_back);
00726             PM_RETURN_IF_ERROR(retval);
00727 
00728             /* Mark the fxn obj */
00729             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_func);
00730             PM_RETURN_IF_ERROR(retval);
00731 
00732             /* Mark the blockstack */
00733             retval = heap_gcMarkObj((pPmObj_t)
00734                                     ((pPmFrame_t)pobj)->fo_blockstack);
00735             PM_RETURN_IF_ERROR(retval);
00736 
00737             /* Mark the attrs dict */
00738             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_attrs);
00739             PM_RETURN_IF_ERROR(retval);
00740 
00741             /* Mark the globals dict */
00742             retval = heap_gcMarkObj((pPmObj_t)((pPmFrame_t)pobj)->fo_globals);
00743             PM_RETURN_IF_ERROR(retval);
00744 
00745             /* Mark each obj in the locals list and the stack */
00746             ppobj2 = ((pPmFrame_t)pobj)->fo_locals;
00747             while (ppobj2 < ((pPmFrame_t)pobj)->fo_sp)
00748             {
00749                 retval = heap_gcMarkObj(*ppobj2);
00750                 PM_RETURN_IF_ERROR(retval);
00751                 ppobj2++;
00752             }
00753             break;
00754         }
00755 
00756         case OBJ_TYPE_BLK:
00757             /* Mark the block obj head */
00758             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00759 
00760             /* Mark the next block in the stack */
00761             retval = heap_gcMarkObj((pPmObj_t)((pPmBlock_t)pobj)->next);
00762             break;
00763 
00764         case OBJ_TYPE_SEG:
00765             /* Mark the segment obj head */
00766             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00767 
00768             /* Mark each obj in the segment */
00769             for (i = 0; i < SEGLIST_OBJS_PER_SEG; i++)
00770             {
00771                 retval = heap_gcMarkObj(((pSegment_t)pobj)->s_val[i]);
00772                 PM_RETURN_IF_ERROR(retval);
00773             }
00774 
00775             /* Mark the next segment */
00776             retval = heap_gcMarkObj((pPmObj_t)((pSegment_t)pobj)->next);
00777             break;
00778 
00779         case OBJ_TYPE_SGL:
00780             /* Mark the seglist obj head */
00781             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00782 
00783             /* Mark the root segment */
00784             retval = heap_gcMarkObj((pPmObj_t)((pSeglist_t)pobj)->sl_rootseg);
00785             break;
00786 
00787         case OBJ_TYPE_SQI:
00788             /* Mark the sequence iterator obj head */
00789             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00790 
00791             /* Mark the sequence */
00792             retval = heap_gcMarkObj(((pPmSeqIter_t)pobj)->si_sequence);
00793             break;
00794 
00795         case OBJ_TYPE_THR:
00796             /* Mark the thread obj head */
00797             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00798 
00799             /* Mark the current frame */
00800             retval = heap_gcMarkObj((pPmObj_t)((pPmThread_t)pobj)->pframe);
00801             break;
00802 
00803         case OBJ_TYPE_NFM:
00804             /*
00805              * Mark the obj desc.  This doesn't really do much since the
00806              * native frame is declared static (not from the heap), but this
00807              * is here in case that ever changes
00808              */
00809             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00810 
00811             /* Mark the native frame's remaining fields if active */
00812             if (gVmGlobal.nativeframe.nf_active)
00813             {
00814                 /* Mark the frame stack */
00815                 retval = heap_gcMarkObj((pPmObj_t)
00816                                         gVmGlobal.nativeframe.nf_back);
00817                 PM_RETURN_IF_ERROR(retval);
00818 
00819                 /* Mark the function object */
00820                 retval = heap_gcMarkObj((pPmObj_t)
00821                                         gVmGlobal.nativeframe.nf_func);
00822                 PM_RETURN_IF_ERROR(retval);
00823 
00824                 /* Mark the stack object */
00825                 retval = heap_gcMarkObj(gVmGlobal.nativeframe.nf_stack);
00826                 PM_RETURN_IF_ERROR(retval);
00827 
00828                 /* Mark the args to the native func */
00829                 for (i = 0; i < NATIVE_GET_NUM_ARGS(); i++)
00830                 {
00831                     retval =
00832                         heap_gcMarkObj(gVmGlobal.nativeframe.nf_locals[i]);
00833                     PM_RETURN_IF_ERROR(retval);
00834                 }
00835             }
00836             break;
00837 
00838 #ifdef HAVE_BYTEARRAY
00839         case OBJ_TYPE_BYA:
00840             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00841 
00842             retval = heap_gcMarkObj((pPmObj_t)((pPmBytearray_t)pobj)->val);
00843             break;
00844 
00845         case OBJ_TYPE_BYS:
00846             OBJ_SET_GCVAL(pobj, pmHeap.gcval);
00847             break;
00848 #endif /* HAVE_BYTEARRAY */
00849 
00850         default:
00851             /* There should be no invalid types */
00852             PM_RAISE(retval, PM_RET_EX_SYS);
00853             break;
00854     }
00855     return retval;
00856 }
00857 
00858 
00859 /*
00860  * Marks the root objects so they won't be collected during the sweep phase.
00861  * Recursively marks all objects reachable from the roots.
00862  */
00863 static PmReturn_t
00864 heap_gcMarkRoots(void)
00865 {
00866     PmReturn_t retval;
00867 
00868     /* Toggle the GC marking value so it differs from the last run */
00869     pmHeap.gcval ^= 1;
00870 
00871     /* Mark the constant objects */
00872     retval = heap_gcMarkObj(PM_NONE);
00873     PM_RETURN_IF_ERROR(retval);
00874     retval = heap_gcMarkObj(PM_FALSE);
00875     PM_RETURN_IF_ERROR(retval);
00876     retval = heap_gcMarkObj(PM_TRUE);
00877     PM_RETURN_IF_ERROR(retval);
00878     retval = heap_gcMarkObj(PM_ZERO);
00879     PM_RETURN_IF_ERROR(retval);
00880     retval = heap_gcMarkObj(PM_ONE);
00881     PM_RETURN_IF_ERROR(retval);
00882     retval = heap_gcMarkObj(PM_NEGONE);
00883     PM_RETURN_IF_ERROR(retval);
00884     retval = heap_gcMarkObj(PM_CODE_STR);
00885     PM_RETURN_IF_ERROR(retval);
00886 
00887     /* Mark the builtins dict */
00888     retval = heap_gcMarkObj(PM_PBUILTINS);
00889     PM_RETURN_IF_ERROR(retval);
00890 
00891     /* Mark the native frame if it is active */
00892     retval = heap_gcMarkObj((pPmObj_t)&gVmGlobal.nativeframe);
00893     PM_RETURN_IF_ERROR(retval);
00894 
00895     /* Mark the thread list */
00896     retval = heap_gcMarkObj((pPmObj_t)gVmGlobal.threadList);
00897 
00898     return retval;
00899 }
00900 
00901 
00902 #if USE_STRING_CACHE
00903 /**
00904  * Unlinks free objects from the string cache.
00905  * This function must only be called by the GC after the heap has been marked
00906  * and before the heap has been swept.
00907  *
00908  * This solves the problem where a string object would be collected
00909  * but its chunk was still linked into the free list
00910  *
00911  * @param gcval The current value for chunks marked by the GC
00912  */
00913 static PmReturn_t
00914 heap_purgeStringCache(uint8_t gcval)
00915 {
00916     PmReturn_t retval;
00917     pPmString_t *ppstrcache;
00918     pPmString_t pstr;
00919 
00920     /* Update string cache pointer if the first string objs are not marked */
00921     retval = string_getCache(&ppstrcache);
00922     if (ppstrcache == C_NULL)
00923     {
00924         return retval;
00925     }
00926     while ((*ppstrcache != C_NULL) && (OBJ_GET_GCVAL(*ppstrcache) != gcval))
00927     {
00928         *ppstrcache = (*ppstrcache)->next;
00929     }
00930     if (*ppstrcache == C_NULL)
00931     {
00932         return retval;
00933     }
00934 
00935     /* Unlink remaining strings that are not marked */
00936     for (pstr = *ppstrcache; pstr->next != C_NULL;)
00937     {
00938         /* Unlink consecutive non-marked strings */
00939         while ((pstr->next != C_NULL) && (OBJ_GET_GCVAL(pstr->next) != gcval))
00940         {
00941             pstr->next = pstr->next->next;
00942         }
00943 
00944         /* If not at end of cache, string must be marked, skip it */
00945         if (pstr->next != C_NULL)
00946         {
00947             pstr = pstr->next;
00948         }
00949     }
00950 
00951     return retval;
00952 }
00953 #endif
00954 
00955 
00956 /*
00957  * Reclaims any object that does not have a current mark.
00958  * Puts it in the free list.  Coalesces all contiguous free chunks.
00959  */
00960 static PmReturn_t
00961 heap_gcSweep(void)
00962 {
00963     PmReturn_t retval;
00964     pPmObj_t pobj;
00965     pPmHeapDesc_t pchunk;
00966     uint16_t totalchunksize;
00967 
00968 #if USE_STRING_CACHE
00969     retval = heap_purgeStringCache(pmHeap.gcval);
00970 #endif
00971 
00972     /* Start at the base of the heap */
00973     pobj = (pPmObj_t)pmHeap.base;
00974     while ((uint8_t *)pobj < &pmHeap.base[PM_HEAP_SIZE])
00975     {
00976         /* Skip to the next unmarked or free chunk within the heap */
00977         while (!OBJ_GET_FREE(pobj)
00978                && (OBJ_GET_GCVAL(pobj) == pmHeap.gcval)
00979                && ((uint8_t *)pobj < &pmHeap.base[PM_HEAP_SIZE]))
00980         {
00981             pobj = (pPmObj_t)((uint8_t *)pobj + OBJ_GET_SIZE(pobj));
00982         }
00983 
00984         /* Stop if reached the end of the heap */
00985         if ((uint8_t *)pobj >= &pmHeap.base[PM_HEAP_SIZE])
00986         {
00987             break;
00988         }
00989 
00990         /* Accumulate the sizes of all consecutive unmarked or free chunks */
00991         totalchunksize = 0;
00992 
00993         /* Coalesce all contiguous free chunks */
00994         pchunk = (pPmHeapDesc_t)pobj;
00995         while (OBJ_GET_FREE(pchunk)
00996                || (!OBJ_GET_FREE(pchunk)
00997                    && (OBJ_GET_GCVAL(pchunk) != pmHeap.gcval)))
00998         {
00999             if ((totalchunksize + OBJ_GET_SIZE(pchunk))
01000                 > HEAP_MAX_FREE_CHUNK_SIZE)
01001             {
01002                 break;
01003             }
01004             totalchunksize = totalchunksize + OBJ_GET_SIZE(pchunk);
01005 
01006             /*
01007              * If the chunk is already free, unlink it because its size
01008              * is about to change
01009              */
01010             if (OBJ_GET_FREE(pchunk))
01011             {
01012                 retval = heap_unlinkFromFreelist(pchunk);
01013                 PM_RETURN_IF_ERROR(retval);
01014             }
01015 
01016             /* Otherwise free and reclaim the unmarked chunk */
01017             else
01018             {
01019                 OBJ_SET_TYPE(pchunk, 0);
01020                 OBJ_SET_FREE(pchunk, 1);
01021             }
01022 
01023             C_DEBUG_PRINT(VERBOSITY_HIGH, "heap_gcSweep(), id=%p, s=%d\n",
01024                           pchunk, OBJ_GET_SIZE(pchunk));
01025 
01026             /* Proceed to the next chunk */
01027             pchunk = (pPmHeapDesc_t)
01028                 ((uint8_t *)pchunk + OBJ_GET_SIZE(pchunk));
01029 
01030             /* Stop if it's past the end of the heap */
01031             if ((uint8_t *)pchunk >= &pmHeap.base[PM_HEAP_SIZE])
01032             {
01033                 break;
01034             }
01035         }
01036 
01037         /* Set the heap descriptor data */
01038         OBJ_SET_FREE(pobj, 1);
01039         OBJ_SET_SIZE(pobj, totalchunksize);
01040 
01041         /* Insert chunk into free list */
01042         retval = heap_linkToFreelist((pPmHeapDesc_t)pobj);
01043         PM_RETURN_IF_ERROR(retval);
01044 
01045         /* Continue to the next chunk */
01046         pobj = (pPmObj_t)pchunk;
01047     }
01048 
01049     return PM_RET_OK;
01050 }
01051 
01052 
01053 /* Runs the mark-sweep garbage collector */
01054 PmReturn_t
01055 heap_gcRun(void)
01056 {
01057     PmReturn_t retval;
01058 
01059     C_DEBUG_PRINT(VERBOSITY_LOW, "heap_gcRun()\n");
01060     /*heap_dump();*/
01061 
01062     retval = heap_gcMarkRoots();
01063     PM_RETURN_IF_ERROR(retval);
01064 
01065     retval = heap_gcSweep();
01066     /*heap_dump();*/
01067     return retval;
01068 }
01069 
01070 
01071 /* Enables or disables automatic garbage collection */
01072 PmReturn_t
01073 heap_gcSetAuto(uint8_t auto_gc)
01074 {
01075     pmHeap.auto_gc = auto_gc;
01076     return PM_RET_OK;
01077 }
01078 #endif /* HAVE_GC */