NAME ^

src/resources.c - Allocate and deallocate tracked resources

DESCRIPTION ^

Parrot Memory Management Code ^

*/

#include "parrot/parrot.h" #include "parrot/resources.h"

#define RECLAMATION_FACTOR 0.20 #define WE_WANT_EVER_GROWING_ALLOCATIONS 0

/* show allocated blocks on stderr */ #define RESOURCE_DEBUG 0 #define RESOURCE_DEBUG_SIZE 1000000

#define POOL_SIZE 65536 * 2

typedef void (*compact_f) (Interp *, Memory_Pool *);

/* HEADERIZER HFILE: include/parrot/resources.h */

/* HEADERIZER BEGIN: static */

PARROT_CANNOT_RETURN_NULL PARROT_WARN_UNUSED_RESULT static char * aligned_mem( NOTNULL(const Buffer *buffer), NOTNULL(char *mem) ) __attribute__nonnull__(1) __attribute__nonnull__(2);

PARROT_PURE_FUNCTION PARROT_WARN_UNUSED_RESULT static size_t aligned_size( NOTNULL(const Buffer *buffer), size_t len ) __attribute__nonnull__(1);

PARROT_CONST_FUNCTION PARROT_WARN_UNUSED_RESULT static size_t aligned_string_size( size_t len );

static void alloc_new_block( PARROT_INTERP, size_t size, NOTNULL(Memory_Pool *pool), NOTNULL(const char *why) ) __attribute__nonnull__(1) __attribute__nonnull__(3) __attribute__nonnull__(4);

PARROT_CANNOT_RETURN_NULL PARROT_WARN_UNUSED_RESULT static const char* buffer_location( PARROT_INTERP, NOTNULL(const PObj *b) ) __attribute__nonnull__(1) __attribute__nonnull__(2);

static void compact_pool( PARROT_INTERP, NOTNULL(Memory_Pool *pool) ) __attribute__nonnull__(1) __attribute__nonnull__(2);

static void debug_print_buf( PARROT_INTERP, NOTNULL(const PObj *b) ) __attribute__nonnull__(1) __attribute__nonnull__(2);

PARROT_MALLOC PARROT_CANNOT_RETURN_NULL static void * mem_allocate( PARROT_INTERP, size_t size, NOTNULL(Memory_Pool *pool) ) __attribute__nonnull__(1) __attribute__nonnull__(3);

static void merge_pools( NOTNULL(Memory_Pool *dest), NOTNULL(Memory_Pool *source) ) __attribute__nonnull__(1) __attribute__nonnull__(2);

PARROT_MALLOC PARROT_CANNOT_RETURN_NULL static Memory_Pool * new_memory_pool( size_t min_block, NULLOK(compact_f compact) );

/* HEADERIZER END: static */

/*

Allocate a new memory block. We allocate the larger of the requested size or the default size. The given text is used for debugging.

*/

static void alloc_new_block(PARROT_INTERP, size_t size, NOTNULL(Memory_Pool *pool), NOTNULL(const char *why)) { Memory_Block *new_block;

    const size_t alloc_size = (size > pool->minimum_block_size)
            ? size : pool->minimum_block_size;

#if RESOURCE_DEBUG fprintf(stderr, "new_block (%s) size %u -> %u\n", why, size, alloc_size); #else UNUSED(why) #endif

    /* Allocate a new block. Header info's on the front */
    new_block = (Memory_Block *)mem_internal_allocate_zeroed(
        sizeof (Memory_Block) + alloc_size);

    if (!new_block) {
        fprintf(stderr, "out of mem allocsize = %d\n", (int)alloc_size);
        exit(EXIT_FAILURE);
    }

    new_block->free  = alloc_size;
    new_block->size  = alloc_size;

    new_block->next  = NULL;
    new_block->start = (char *)new_block + sizeof (Memory_Block);
    new_block->top   = new_block->start;

    /* Note that we've allocated it */
    interp->arena_base->memory_allocated += alloc_size;

    /* If this is for a public pool, add it to the list */
    new_block->prev = pool->top_block;

    /* If we're not first, then tack us on the list */
    if (pool->top_block)
        pool->top_block->next = new_block;

    pool->top_block        = new_block;
    pool->total_allocated += alloc_size;
}

/*

FUNCDOC: mem_allocate

Allocates memory for headers.

Alignment problems history:

See http://archive.develooper.com/perl6-internals%40perl.org/msg12310.html for details.

- return aligned pointer *if needed* - return strings et al at unaligned i.e. void* boundaries - remember alignment in a buffer header bit use this in compaction code - reduce alignment to a reasonable value i.e. MALLOC_ALIGNMENT aka 2*sizeof (size_t) or just 8 (TODO make a config hint)

Buffer memory layout:

                    +-----------------+
                    |  ref_count   |f |    # GC header
  obj->bufstart  -> +-----------------+
                    |  data           |
                    v                 v

 * if PObj_is_COWable is set, then we have
   - a ref_count, {inc,dec}remented by 2 always
   - the lo bit 'f' means 'is being forwarded" - what TAIL_flag was

 * if PObj_align_FLAG is set, obj->bufstart is aligned like discussed above
 * obj->buflen is the usable length excluding the optional GC part.

*/

PARROT_MALLOC PARROT_CANNOT_RETURN_NULL static void * mem_allocate(PARROT_INTERP, size_t size, NOTNULL(Memory_Pool *pool)) { void *return_val;

    /* we always should have one block at least */
    PARROT_ASSERT(pool->top_block);

    /* If not enough room, try to find some */
    if (pool->top_block->free < size) {
        /*
         * force a DOD run to get live flags set
         * for incremental M&S collection is run from there
         * TODO pass required allocation size to the DOD system,
         *      so that collection can be skipped if needed
         */
        if (!interp->arena_base->DOD_block_level) {
            Parrot_do_dod_run(interp, DOD_trace_stack_FLAG);
#if !PARROT_GC_IMS
            /* Compact the pool if allowed and worthwhile */
            if (pool->compact) {
                /* don't bother reclaiming if it's just chicken feed */
                if ((pool->possibly_reclaimable * pool->reclaim_factor +
                            pool->guaranteed_reclaimable) > size) {
                    (*pool->compact) (interp, pool);
                }

            }
#endif
        }
        if (pool->top_block->free < size) {
            if (pool->minimum_block_size < 65536 * 16)
                pool->minimum_block_size *= 2;
            /*
             * TODO - Big blocks
             *
             * Mark the block as big block (it has just one item)
             * And don't set big blocks as the top_block.
             */
            alloc_new_block(interp, size, pool, "compact failed");

            interp->arena_base->mem_allocs_since_last_collect++;

            if (pool->top_block->free < size) {
                fprintf(stderr, "out of mem\n");
                exit(EXIT_FAILURE);
            }
        }
    }

    /* TODO inline the fast path */
    return_val             = pool->top_block->top;
    pool->top_block->top  += size;
    pool->top_block->free -= size;

    return return_val;
}

#if RESOURCE_DEBUG PARROT_CANNOT_RETURN_NULL PARROT_WARN_UNUSED_RESULT static const char* buffer_location(PARROT_INTERP, NOTNULL(const PObj *b)) { int i; static char reg[10];

    parrot_context_t* const ctx = CONTEXT(interp->ctx);

    for (i = 0; i < ctx->n_regs_used[REGNO_STR]; ++i) {
        PObj * const obj = (PObj *) CTX_REG_STR(interp, ctx, i);
        if (obj == b) {
            sprintf(reg, "S%d", i);
            return reg;
        }
    }

    return "???";
}

static void debug_print_buf(PARROT_INTERP, NOTNULL(const PObj *b)) { fprintf(stderr, "found %p, len %d, flags 0x%08x at %s\n", b, (int)PObj_buflen(b), (uint)PObj_get_FLAGS(b), buffer_location(interp, b)); } #endif

/*

Compaction Code ^

FUNCDOC: compact_pool

Compact the buffer pool.

*/

static void compact_pool(PARROT_INTERP, NOTNULL(Memory_Pool *pool)) { INTVAL j; UINTVAL object_size; UINTVAL total_size;

    Memory_Block *new_block;     /* A pointer to our working block */
    char         *cur_spot;      /* Where we're currently copying to */

    Small_Object_Arena *cur_buffer_arena;
    INTVAL             *ref_count  = NULL;
    Arenas * const      arena_base = interp->arena_base;

    /* Bail if we're blocked */
    if (arena_base->GC_block_level)
        return;

    ++arena_base->GC_block_level;

    if (interp->profile)
        Parrot_dod_profile_start(interp);

    /* We're collecting */
    arena_base->mem_allocs_since_last_collect    = 0;
    arena_base->header_allocs_since_last_collect = 0;
    arena_base->collect_runs++;

    /* total - reclaimable == currently used. Add a minimum block to the
     * current amount, so we can avoid having to allocate it in the future. */
    {
        Memory_Block *cur_block;

        total_size = 0;
        cur_block  = pool->top_block;

        while (cur_block) {
            /*
             * TODO - Big blocks
             *
             * Currently all available blocks are compacted into on new
             * block with total_size. This is more than suboptimal, if
             * the block has just one live item from a big allocation.
             *
             * But currently it's unknown if the buffer memory is alive
             * as the live bits are in Buffer headers. We have to run the
             * compaction loop below to check liveness. OTOH if this
             * compaction is running through all the buffer headers, there
             * is no relation to the block.
             *
             *
             * Moving the life bit into the buffer thus also solves this
             * problem easily.
             */
            total_size += cur_block->size - cur_block->free;
            cur_block   = cur_block->prev;
        }
    }
    /*
     * XXX for some reason the guarantee isn't correct
     *     TODO check why
     */

    /* total_size -= pool->guaranteed_reclaimable; */

    /* this makes for ever increasing allocations but fewer collect runs */
#if WE_WANT_EVER_GROWING_ALLOCATIONS
    total_size += pool->minimum_block_size;
#endif

    /* Snag a block big enough for everything */
    alloc_new_block(interp, total_size, pool, "inside compact");

    new_block = pool->top_block;

    /* Start at the beginning */
    cur_spot  = new_block->start;

    /* Run through all the Buffer header pools and copy */
    for (j = 0; j < (INTVAL)arena_base->num_sized; j++) {
        Small_Object_Pool * const header_pool = arena_base->sized_header_pools[j];

        if (header_pool == NULL)
            continue;

        object_size = header_pool->object_size;

        for (cur_buffer_arena = header_pool->last_Arena;
                NULL != cur_buffer_arena;
                cur_buffer_arena = cur_buffer_arena->prev) {
            Buffer *b;
            UINTVAL i;

            b = (Buffer *)ARENA_to_PObj(cur_buffer_arena->start_objects);

            for (i = 0; i < cur_buffer_arena->used; i++) {
                /* ! (on_free_list | constant | external | sysmem) */
                if (PObj_buflen(b) && PObj_is_movable_TESTALL(b)) {
                    ptrdiff_t offset = 0;
#if RESOURCE_DEBUG
                    if (PObj_buflen(b) >= RESOURCE_DEBUG_SIZE)
                        debug_print_buf(interp, b);
#endif

                    /* we can't perform the math all the time, because
                     * strstart might be in unallocated memory */
                    if (PObj_is_COWable_TEST(b)) {
                        ref_count = ((INTVAL*) PObj_bufstart(b)) - 1;

                        if (PObj_is_string_TEST(b)) {
                            offset = (ptrdiff_t)((STRING *)b)->strstart -
                                (ptrdiff_t)PObj_bufstart(b);
                        }
                    }

                    /* buffer has already been moved; just change the header */
                    if (PObj_COW_TEST(b) &&
                        (ref_count && *ref_count & Buffer_moved_FLAG)) {
                        /* Find out who else references our data */
                        Buffer * const hdr = *(Buffer **)(PObj_bufstart(b));

                        PARROT_ASSERT(PObj_is_COWable_TEST(b));

                        /* Make sure they know that we own it too */
                        PObj_COW_SET(hdr);

                        /* TODO incr ref_count, after fixing string too
                         * Now make sure we point to where the other guy does */
                        PObj_bufstart(b) = PObj_bufstart(hdr);

                        /* And if we're a string, update strstart */
                        /* Somewhat of a hack, but if we get per-pool
                         * collections, it should help ease the pain */
                        if (PObj_is_string_TEST(b)) {
                            ((STRING *)b)->strstart = (char *)PObj_bufstart(b) +
                                    offset;
                        }
                    }
                    else {
                        cur_spot = aligned_mem(b, cur_spot);

                        if (PObj_is_COWable_TEST(b)) {
                            INTVAL * const new_ref_count = ((INTVAL*) cur_spot) - 1;
                            *new_ref_count        = 2;
                        }

                        /* Copy our memory to the new pool */
                        memcpy(cur_spot, PObj_bufstart(b), PObj_buflen(b));

                        /* If we're COW */
                        if (PObj_COW_TEST(b)) {
                            PARROT_ASSERT(PObj_is_COWable_TEST(b));

                            /* Let the old buffer know how to find us */
                            *(Buffer **)(PObj_bufstart(b)) = b;

                            /* No guarantees that our data is still COW, so
                             * assume not, and let the above code fix-up */
                            PObj_COW_CLEAR(b);

                            /* Finally, let the tail know that we've moved, so
                             * that any other references can know to look for
                             * us and not re-copy */
                            *ref_count |= Buffer_moved_FLAG;
                        }
                        PObj_bufstart(b) = cur_spot;
                        if (PObj_is_string_TEST(b)) {
                            ((STRING *)b)->strstart = (char *)PObj_bufstart(b) +
                                    offset;
                        }
                        cur_spot += PObj_buflen(b);
                    }
                }
                b = (Buffer *)((char *)b + object_size);
            }
        }
    }

    /* Okay, we're done with the copy. Set the bits in the pool struct */
    /* First, where we allocate next */
    new_block->top = cur_spot;

    PARROT_ASSERT(new_block->size >= (size_t)new_block->top -
            (size_t)new_block->start);

    /* How much is free. That's the total size minus the amount we used */
    new_block->free = new_block->size - (new_block->top - new_block->start);

    arena_base->memory_collected += (new_block->top - new_block->start);

    /* Now we're done. We're already on the pool's free list, so let us be the
     * only one on the free list and free the rest */
    {
        Memory_Block *cur_block;

        PARROT_ASSERT(new_block == pool->top_block);
        cur_block = new_block->prev;
        while (cur_block) {
            Memory_Block * const next_block = cur_block->prev;
            /* Note that we don't have it any more */
            arena_base->memory_allocated -= cur_block->size;
            /* We know the pool body and pool header are a single chunk, so
             * this is enough to get rid of 'em both */
            mem_internal_free(cur_block);
            cur_block = next_block;
        }

        /* Set our new pool as the only pool */
        new_block->prev = NULL;
        pool->total_allocated = total_size;
    }

    pool->guaranteed_reclaimable = 0;
    pool->possibly_reclaimable = 0;
    if (interp->profile)
        Parrot_dod_profile_end(interp, PARROT_PROF_GC);
    --arena_base->GC_block_level;
}

/*

FUNCDOC: Go do a GC run. This only scans the string pools and compacts them, it doesn't check for string liveness.

*/

void Parrot_go_collect(PARROT_INTERP) { compact_pool(interp, interp->arena_base->memory_pool); }

PARROT_PURE_FUNCTION PARROT_WARN_UNUSED_RESULT static size_t aligned_size(NOTNULL(const Buffer *buffer), size_t len) { if (PObj_is_COWable_TEST(buffer)) len += sizeof (void*); if (PObj_aligned_TEST(buffer)) len = (len + BUFFER_ALIGN_1) & BUFFER_ALIGN_MASK; else len = (len + WORD_ALIGN_1) & WORD_ALIGN_MASK; return len; }

PARROT_CANNOT_RETURN_NULL PARROT_WARN_UNUSED_RESULT static char * aligned_mem(NOTNULL(const Buffer *buffer), NOTNULL(char *mem)) { if (PObj_is_COWable_TEST(buffer)) mem += sizeof (void*); if (PObj_aligned_TEST(buffer)) mem = (char*)(((unsigned long)(mem + BUFFER_ALIGN_1)) & BUFFER_ALIGN_MASK); else mem = (char*)(((unsigned long)(mem + WORD_ALIGN_1)) & WORD_ALIGN_MASK);

    return mem;
}

PARROT_CONST_FUNCTION PARROT_WARN_UNUSED_RESULT static size_t aligned_string_size(size_t len) /* XXX Looks like we can lose buffer here */ { len += sizeof (void*); len = (len + WORD_ALIGN_1) & WORD_ALIGN_MASK; return len; }

PARROT_WARN_UNUSED_RESULT int Parrot_in_memory_pool(PARROT_INTERP, NOTNULL(void *bufstart)) { Memory_Pool * const pool = interp->arena_base->memory_pool; Memory_Block * cur_block = pool->top_block;

    while (cur_block) {
        if ((char *)bufstart >= cur_block->start &&
            (char *) bufstart < cur_block->start + cur_block->size) {
            return 1;
        }
        cur_block = cur_block->prev;
    }
    return 0;
}

/*

Parrot Re/Allocate Code ^

FUNCDOC: Parrot_reallocate

Reallocate the Buffer's buffer memory to the given size. The allocated buffer will not shrink. If the buffer was allocated with Parrot_allocate_aligned the new buffer will also be aligned. As with all reallocation, the new buffer might have moved and the additional memory is not cleared.

*/

void Parrot_reallocate(PARROT_INTERP, NOTNULL(Buffer *buffer), size_t tosize) { size_t copysize; char *mem; Memory_Pool * const pool = interp->arena_base->memory_pool; size_t new_size, needed, old_size;

    /*
     * we don't shrink buffers
     */
    if (tosize <= PObj_buflen(buffer))
        return;

    /*
     * same as below but barely used and tested - only 3 list related
     * tests do use true reallocation
     *
     * list.c, which does _reallocate, has 2 reallocations
     * normally, which play ping pong with buffers.
     * The normal case is therefore always to allocate a new block
     */
    new_size = aligned_size(buffer, tosize);
    old_size = aligned_size(buffer, PObj_buflen(buffer));
    needed = new_size - old_size;
    if ( (pool->top_block->free >= needed) &&
            (pool->top_block->top == (char*)PObj_bufstart(buffer) + old_size) ) {
        pool->top_block->free -= needed;
        pool->top_block->top  += needed;
        PObj_buflen(buffer) = tosize;
        return;
    }
    copysize = PObj_buflen(buffer);
    if (!PObj_COW_TEST(buffer)) {
        pool->guaranteed_reclaimable += copysize;
    }
    pool->possibly_reclaimable += copysize;
    mem = (char *)mem_allocate(interp, new_size, pool);
    mem = aligned_mem(buffer, mem);

    /* We shouldn't ever have a 0 from size, but we do. If we can track down
     * those bugs, this can be removed which would make things cheaper */
    if (copysize) {
        memcpy(mem, PObj_bufstart(buffer), copysize);
    }
    PObj_bufstart(buffer) = mem;
    if (PObj_is_COWable_TEST(buffer))
        new_size -= sizeof (void*);
    PObj_buflen(buffer) = new_size;
}

/*

FUNCDOC: Parrot_reallocate_string

Reallocate the STRING's buffer memory to the given size. The allocated buffer will not shrink. This function sets also str->strstart to the new buffer location, str->bufused is not changed.

*/

void Parrot_reallocate_string(PARROT_INTERP, NOTNULL(STRING *str), size_t tosize) { size_t copysize; char *mem, *oldmem; size_t new_size, needed, old_size;

    Memory_Pool * const pool =
        PObj_constant_TEST(str)
            ? interp->arena_base->constant_string_pool
            : interp->arena_base->memory_pool;
    /*
     * if the requested size is smaller then buflen, we are done
     */
    if (tosize <= PObj_buflen(str))
        return;

    /*
     * first check, if we can reallocate:
     * - if the passed strings buffer is the last string in the pool and
     * - if there is enough size, we can just move the pool's top pointer
     */
    new_size = aligned_string_size(tosize);
    old_size = aligned_string_size(PObj_buflen(str));
    needed = new_size - old_size;
    if (pool->top_block->free >= needed &&
            pool->top_block->top == (char*)PObj_bufstart(str) +
            old_size) {
        pool->top_block->free -= needed;
        pool->top_block->top  += needed;
        PObj_buflen(str) = new_size - sizeof (void*);
        return;
    }
    PARROT_ASSERT(str->bufused <= tosize);
    /* only copy used memory, not total string buffer */
    copysize = str->bufused;

    if (!PObj_COW_TEST(str)) {
        pool->guaranteed_reclaimable += PObj_buflen(str);
    }
    pool->possibly_reclaimable += PObj_buflen(str);

    mem = (char *)mem_allocate(interp, new_size, pool);
    mem += sizeof (void*);

    /* copy mem from strstart, *not* bufstart */
    oldmem             = str->strstart;
    PObj_bufstart(str) = (void *)mem;
    str->strstart      = mem;
    PObj_buflen(str)   = new_size - sizeof (void*);

    /* We shouldn't ever have a 0 from size, but we do. If we can track down
     * those bugs, this can be removed which would make things cheaper */
    if (copysize) {
        memcpy(mem, oldmem, copysize);
    }
}

/*

FUNCDOC: Parrot_allocate

Allocate buffer memory for the given Buffer pointer. The size has to be a multiple of the word size. PObj_buflen will be set to exactly the given size.

*/

void Parrot_allocate(PARROT_INTERP, NOTNULL(Buffer *buffer), size_t size) { PObj_buflen(buffer) = 0; PObj_bufstart(buffer) = NULL; PARROT_ASSERT((size & WORD_ALIGN_1) == 0); PObj_bufstart(buffer) = mem_allocate(interp, size, interp->arena_base->memory_pool); PObj_buflen(buffer) = size; }

/*

FUNCDOC: Parrot_allocate_aligned

Like above, except the size will be rounded up and the address of the buffer will have the same alignment as a pointer returned by malloc(3) suitable to hold e.g. a FLOATVAL array.

*/

void Parrot_allocate_aligned(PARROT_INTERP, NOTNULL(Buffer *buffer), size_t size) { size_t new_size; char *mem;

    PObj_buflen(buffer) = 0;
    PObj_bufstart(buffer) = NULL;
    new_size = aligned_size(buffer, size);
    mem = (char *)mem_allocate(interp, new_size,
        interp->arena_base->memory_pool);
    mem = aligned_mem(buffer, mem);
    PObj_bufstart(buffer) = mem;
    if (PObj_is_COWable_TEST(buffer))
        new_size -= sizeof (void*);
    PObj_buflen(buffer) = new_size;
}

/*

FUNCDOC: Parrot_allocate_string

Allocate the STRING's buffer memory to the given size. The allocated buffer maybe slightly bigger than the given size. This function sets also str->strstart to the new buffer location, str->bufused is not changed.

*/

void Parrot_allocate_string(PARROT_INTERP, NOTNULL(STRING *str), size_t size) { size_t new_size; Memory_Pool *pool; char *mem;

    PObj_buflen(str)   = 0;
    PObj_bufstart(str) = NULL;

    /* there's no sense in allocating zero memory, when the overhead of
     * allocating a string is one pointer; this can fill the pools in an
     * uncompactable way.  See RT #42320.
     */
    if (size == 0)
        return;

    pool     = PObj_constant_TEST(str)
                ? interp->arena_base->constant_string_pool
                : interp->arena_base->memory_pool;

    new_size = aligned_string_size(size);
    mem      = (char *)mem_allocate(interp, new_size, pool);
    mem     += sizeof (void*);

    PObj_bufstart(str) =  str->strstart = mem;
    PObj_buflen(str)   = new_size - sizeof (void*);
}

/*

FUNCDOC: new_memory_pool

Create a new memory pool.

*/

PARROT_MALLOC PARROT_CANNOT_RETURN_NULL static Memory_Pool * new_memory_pool(size_t min_block, NULLOK(compact_f compact)) { Memory_Pool * const pool = mem_internal_allocate_typed(Memory_Pool);

    pool->top_block              = NULL;
    pool->compact                = compact;
    pool->minimum_block_size     = min_block;
    pool->total_allocated        = 0;
    pool->guaranteed_reclaimable = 0;
    pool->possibly_reclaimable   = 0;
    pool->reclaim_factor         = RECLAMATION_FACTOR;

    return pool;
}

/*

FUNCDOC: Parrot_initialize_memory_pools

Initialize the managed memory pools.

*/

void Parrot_initialize_memory_pools(PARROT_INTERP) { Arenas * const arena_base = interp->arena_base;

    arena_base->memory_pool   = new_memory_pool(POOL_SIZE, &compact_pool);
    alloc_new_block(interp, POOL_SIZE, arena_base->memory_pool, "init");

    /* Constant strings - not compacted */
    arena_base->constant_string_pool = new_memory_pool(POOL_SIZE, NULL);

    alloc_new_block(interp, POOL_SIZE, arena_base->constant_string_pool,"init");
}

/*

FUNCDOC: Parrot_destroy_memory_pools

Destroys the memory pools.

*/

void Parrot_destroy_memory_pools(PARROT_INTERP) { int i;

    for (i = 0; i < 2; i++) {
        Memory_Pool * const pool = i ?
                interp->arena_base->constant_string_pool :
                interp->arena_base->memory_pool;
        Memory_Block *cur_block;

        cur_block = pool->top_block;

        while (cur_block) {
            Memory_Block * const next_block = cur_block->prev;
            mem_internal_free(cur_block);
            cur_block = next_block;
        }

        mem_internal_free(pool);
    }
}

static void merge_pools(NOTNULL(Memory_Pool *dest), NOTNULL(Memory_Pool *source)) { Memory_Block *cur_block;

    cur_block = source->top_block;

    while (cur_block) {
        Memory_Block * const next_block = cur_block->prev;

        if (cur_block->free == cur_block->size) {
            mem_internal_free(cur_block);
        }
        else {
            cur_block->next        = NULL;
            cur_block->prev        = dest->top_block;

            dest->top_block        = cur_block;
            dest->total_allocated += cur_block->size;
        }
        cur_block = next_block;
    }

    dest->guaranteed_reclaimable += source->guaranteed_reclaimable;
    dest->possibly_reclaimable   += dest->possibly_reclaimable;

    source->top_block              = NULL;
    source->total_allocated        = 0;
    source->possibly_reclaimable   = 0;
    source->guaranteed_reclaimable = 0;
}

/*

FUNCDOC: Parrot_merge_memory_pools

Merge the memory pools of source_interp into dest_interp.

*/

void Parrot_merge_memory_pools(NOTNULL(Interp *dest_interp), NOTNULL(Interp *source_interp)) { merge_pools(dest_interp->arena_base->constant_string_pool, source_interp->arena_base->constant_string_pool);

    merge_pools(dest_interp->arena_base->memory_pool,
                source_interp->arena_base->memory_pool);
}

/*

SEE ALSO ^

include/parrot/resources.h, src/memory.c.

HISTORY ^

Initial version by Dan on 2001.10.2.

*/

/* * Local variables: * c-file-style: "parrot" * End: * vim: expandtab shiftwidth=4: */


parrot