[svn:parrot] r49061 - branches/string_gc_encapsulate/src/gc

bacek at svn.parrot.org bacek at svn.parrot.org
Thu Sep 16 13:53:54 UTC 2010


Author: bacek
Date: Thu Sep 16 13:53:54 2010
New Revision: 49061
URL: https://trac.parrot.org/parrot/changeset/49061

Log:
Move String GC related functions from alloc_resources into string_gc

Modified:
   branches/string_gc_encapsulate/src/gc/alloc_resources.c
   branches/string_gc_encapsulate/src/gc/gc_ms.c
   branches/string_gc_encapsulate/src/gc/gc_private.h
   branches/string_gc_encapsulate/src/gc/string_gc.c

Modified: branches/string_gc_encapsulate/src/gc/alloc_resources.c
==============================================================================
--- branches/string_gc_encapsulate/src/gc/alloc_resources.c	Thu Sep 16 13:53:33 2010	(r49060)
+++ branches/string_gc_encapsulate/src/gc/alloc_resources.c	Thu Sep 16 13:53:54 2010	(r49061)
@@ -66,47 +66,10 @@
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*dest_interp);
 
-static void free_memory_pool(ARGFREE(Variable_Size_Pool *pool));
-static void free_old_mem_blocks(
-     ARGMOD(Memory_Pools *mem_pools),
-    ARGMOD(Variable_Size_Pool *pool),
-    ARGMOD(Memory_Block *new_block),
-    UINTVAL total_size)
-        __attribute__nonnull__(1)
-        __attribute__nonnull__(2)
-        __attribute__nonnull__(3)
-        FUNC_MODIFIES(*mem_pools)
-        FUNC_MODIFIES(*pool)
-        FUNC_MODIFIES(*new_block);
-
 static void free_pool(ARGFREE(Fixed_Size_Pool *pool));
 static int is_block_almost_full(ARGIN(const Memory_Block *block))
         __attribute__nonnull__(1);
 
-static void move_buffer_callback(PARROT_INTERP,
-    ARGIN(Buffer *b),
-    ARGIN(void *data))
-        __attribute__nonnull__(1)
-        __attribute__nonnull__(2)
-        __attribute__nonnull__(3);
-
-PARROT_WARN_UNUSED_RESULT
-PARROT_CANNOT_RETURN_NULL
-static char * move_one_buffer(PARROT_INTERP,
-    ARGIN(Memory_Block *pool),
-    ARGMOD(Buffer *old_buf),
-    ARGMOD(char *new_pool_ptr))
-        __attribute__nonnull__(1)
-        __attribute__nonnull__(2)
-        __attribute__nonnull__(3)
-        __attribute__nonnull__(4)
-        FUNC_MODIFIES(*old_buf)
-        FUNC_MODIFIES(*new_pool_ptr);
-
-PARROT_CANNOT_RETURN_NULL
-static UINTVAL pad_pool_size(ARGIN(const Variable_Size_Pool *pool))
-        __attribute__nonnull__(1);
-
 static void Parrot_gc_merge_buffer_pools(PARROT_INTERP,
     ARGMOD(Memory_Pools *mem_pools),
     ARGMOD(Fixed_Size_Pool *dest),
@@ -153,25 +116,9 @@
 #define ASSERT_ARGS_fix_pmc_syncs __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(dest_interp) \
     , PARROT_ASSERT_ARG(pool))
-#define ASSERT_ARGS_free_memory_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
-#define ASSERT_ARGS_free_old_mem_blocks __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(mem_pools) \
-    , PARROT_ASSERT_ARG(pool) \
-    , PARROT_ASSERT_ARG(new_block))
 #define ASSERT_ARGS_free_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
 #define ASSERT_ARGS_is_block_almost_full __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(block))
-#define ASSERT_ARGS_move_buffer_callback __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(interp) \
-    , PARROT_ASSERT_ARG(b) \
-    , PARROT_ASSERT_ARG(data))
-#define ASSERT_ARGS_move_one_buffer __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(interp) \
-    , PARROT_ASSERT_ARG(pool) \
-    , PARROT_ASSERT_ARG(old_buf) \
-    , PARROT_ASSERT_ARG(new_pool_ptr))
-#define ASSERT_ARGS_pad_pool_size __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(pool))
 #define ASSERT_ARGS_Parrot_gc_merge_buffer_pools __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(mem_pools) \
@@ -246,343 +193,8 @@
 
 =back
 
-=head2 Compaction Code
-
-=over 4
-
-=item C<void compact_pool(PARROT_INTERP, Memory_Pools *mem_pools,
-Variable_Size_Pool *pool)>
-
-Compact the string buffer pool. Does not perform a GC scan, or mark items
-as being alive in any way.
-
-=cut
-
-*/
-
-void
-compact_pool(PARROT_INTERP,
-        ARGMOD(Memory_Pools *mem_pools),
-        ARGMOD(Variable_Size_Pool *pool))
-{
-    ASSERT_ARGS(compact_pool)
-    INTVAL        j;
-    UINTVAL       total_size;
-
-    Fixed_Size_Arena *cur_buffer_arena;
-
-    /* Contains new_block and cur_spot */
-    string_callback_data cb_data;
-
-
-    /* Bail if we're blocked */
-    if (mem_pools->gc_sweep_block_level)
-        return;
-
-    ++mem_pools->gc_sweep_block_level;
-
-    /* We're collecting */
-    mem_pools->mem_allocs_since_last_collect    = 0;
-    mem_pools->header_allocs_since_last_collect = 0;
-    ++mem_pools->gc_collect_runs;
-
-    /* Snag a block big enough for everything */
-    total_size = pad_pool_size(pool);
-
-    if (total_size == 0) {
-        free_old_mem_blocks(mem_pools, pool, pool->top_block, total_size);
-        --mem_pools->gc_sweep_block_level;
-        return;
-    }
-
-    Parrot_gc_str_alloc_new_block(mem_pools, total_size, pool, "inside compact");
-
-    cb_data.new_block = pool->top_block;
-
-    /* Start at the beginning */
-    cb_data.cur_spot  = cb_data.new_block->start;
-
-    /* Run through all the Buffer header pools and copy */
-    interp->gc_sys->iterate_live_strings(interp, move_buffer_callback, &cb_data);
-
-    /* Okay, we're done with the copy. Set the bits in the pool struct */
-    /* First, where we allocate next */
-    cb_data.new_block->top = cb_data.cur_spot;
-
-    PARROT_ASSERT(cb_data.new_block->size
-                  >=
-                  (size_t)cb_data.new_block->top - (size_t)cb_data.new_block->start);
-
-    /* How much is free. That's the total size minus the amount we used */
-    cb_data.new_block->free     = cb_data.new_block->size
-                                  - (cb_data.cur_spot - cb_data.new_block->start);
-    mem_pools->memory_collected += (cb_data.cur_spot - cb_data.new_block->start);
-    mem_pools->memory_used      += (cb_data.cur_spot - cb_data.new_block->start);
-
-    free_old_mem_blocks(mem_pools, pool, cb_data.new_block, total_size);
-
-    --mem_pools->gc_sweep_block_level;
-}
-
-/*
-=item C<static void move_buffer_callback(PARROT_INTERP, Buffer *b, void *data)>
-
-Callback for live STRING/Buffer for compating.
-
-=cut
-*/
-static void
-move_buffer_callback(PARROT_INTERP, ARGIN(Buffer *b), ARGIN(void *data))
-{
-    ASSERT_ARGS(move_buffer_callback)
-    string_callback_data *cb = (string_callback_data*)data;
-
-    if (Buffer_buflen(b) && PObj_is_movable_TESTALL(b)) {
-        Memory_Block *old_block = Buffer_pool(b);
-
-        if (!is_block_almost_full(old_block))
-            cb->cur_spot = move_one_buffer(interp, cb->new_block, b, cb->cur_spot);
-    }
-
-}
-
-/*
-
-=item C<static UINTVAL pad_pool_size(const Variable_Size_Pool *pool)>
-
-Calculate the size of the new pool. The currently used size equals the total
-size minus the reclaimable size. Add a minimum block to the current amount, so
-we can avoid having to allocate it in the future.
-
-Returns 0 if all blocks below the top block are almost full. In this case
-compacting is not needed.
-
-TODO - Big blocks
-
-Currently all available blocks are compacted into one new
-block with total_size. This is 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 to check liveness. OTOH if this compaction
-is running through all the buffer headers, there is no
-relation to the block.
-
-Moving the live bit into the buffer thus also solves this
-problem easily.
-
-=cut
-
-*/
-
-PARROT_CANNOT_RETURN_NULL
-static UINTVAL
-pad_pool_size(ARGIN(const Variable_Size_Pool *pool))
-{
-    ASSERT_ARGS(pad_pool_size)
-    Memory_Block *cur_block = pool->top_block->prev;
-
-    UINTVAL total_size   = 0;
-#if RESOURCE_DEBUG
-    size_t  total_blocks = 1;
-#endif
-
-    while (cur_block) {
-        if (!is_block_almost_full(cur_block))
-            total_size += cur_block->size - cur_block->freed - cur_block->free;
-        cur_block   = cur_block->prev;
-#if RESOURCE_DEBUG
-        ++total_blocks;
-#endif
-    }
-
-    if (total_size == 0)
-        return 0;
-
-    cur_block = pool->top_block;
-    if (!is_block_almost_full(cur_block))
-        total_size += cur_block->size - cur_block->freed - cur_block->free;
-
-    /* this makes for ever increasing allocations but fewer collect runs */
-#if WE_WANT_EVER_GROWING_ALLOCATIONS
-    total_size += pool->minimum_block_size;
-#endif
-
-#if RESOURCE_DEBUG
-    fprintf(stderr, "Total blocks: %d\n", total_blocks);
-#endif
-
-    return total_size;
-}
-
-/*
-
-=item C<static char * move_one_buffer(PARROT_INTERP, Memory_Block *pool, Buffer
-*old_buf, char *new_pool_ptr)>
-
-The compact_pool operation collects disjointed blocks of memory allocated on a
-given pool's free list into one large block of memory. Once the new larger
-memory block has been allocated, this function moves one buffer from the old
-memory block to the new memory block and marks that it has been moved.
-
-=cut
-
 */
 
-PARROT_WARN_UNUSED_RESULT
-PARROT_CANNOT_RETURN_NULL
-static char *
-move_one_buffer(PARROT_INTERP, ARGIN(Memory_Block *pool),
-        ARGMOD(Buffer *old_buf), ARGMOD(char *new_pool_ptr))
-{
-    ASSERT_ARGS(move_one_buffer)
-
-    INTVAL       *flags     = NULL;
-    ptrdiff_t     offset    = 0;
-    Memory_Block *old_block = NULL;
-#if RESOURCE_DEBUG
-    if (Buffer_buflen(old_buf) >= RESOURCE_DEBUG_SIZE)
-        debug_print_buf(interp, old_buf);
-#else
-    UNUSED(interp);
-#endif
-
-    /* we can't perform the math all the time, because
-        * strstart might be in unallocated memory */
-    if (PObj_is_COWable_TEST(old_buf)) {
-        flags = Buffer_bufflagsptr(old_buf);
-        old_block = Buffer_pool(old_buf);
-
-        if (PObj_is_string_TEST(old_buf)) {
-            offset = (ptrdiff_t)((STRING *)old_buf)->strstart -
-                (ptrdiff_t)Buffer_bufstart(old_buf);
-        }
-    }
-
-    /* buffer has already been moved; just change the header */
-    if (flags && (*flags & Buffer_shared_FLAG)
-              && (*flags & Buffer_moved_FLAG)) {
-        /* Find out who else references our data */
-        Buffer * const hdr = *((Buffer **)Buffer_bufstart(old_buf));
-
-        PARROT_ASSERT(PObj_is_COWable_TEST(old_buf));
-
-        /* Make sure they know that we own it too */
-        /* Set Buffer_shared_FLAG in new buffer */
-        *Buffer_bufflagsptr(hdr) |= Buffer_shared_FLAG;
-
-        /* Now make sure we point to where the other guy does */
-        Buffer_bufstart(old_buf) = Buffer_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(old_buf))
-            ((STRING *)old_buf)->strstart =
-                (char *)Buffer_bufstart(old_buf) + offset;
-    }
-    else {
-        new_pool_ptr = aligned_mem(old_buf, new_pool_ptr);
-
-        /* Copy our memory to the new pool */
-        memcpy(new_pool_ptr, Buffer_bufstart(old_buf),
-                                Buffer_buflen(old_buf));
-
-        /* If we're shared */
-        if (flags && (*flags & Buffer_shared_FLAG)) {
-            PARROT_ASSERT(PObj_is_COWable_TEST(old_buf));
-
-            /* Let the old buffer know how to find us */
-            *((Buffer **)Buffer_bufstart(old_buf)) = old_buf;
-
-            /* Finally, let the tail know that we've moved, so
-                * that any other references can know to look for
-                * us and not re-copy */
-            *flags |= Buffer_moved_FLAG;
-        }
-
-        Buffer_bufstart(old_buf) = new_pool_ptr;
-
-        /* Remember new pool inside */
-        *Buffer_poolptr(old_buf) = pool;
-
-        if (PObj_is_string_TEST(old_buf))
-            ((STRING *)old_buf)->strstart =
-                    (char *)Buffer_bufstart(old_buf) + offset;
-
-        new_pool_ptr += Buffer_buflen(old_buf);
-    }
-
-    return new_pool_ptr;
-}
-
-/*
-
-=item C<static void free_old_mem_blocks( Memory_Pools *mem_pools,
-Variable_Size_Pool *pool, Memory_Block *new_block, UINTVAL total_size)>
-
-The compact_pool operation collects disjointed blocks of memory allocated on a
-given pool's free list into one large block of memory, setting it as the new
-top block for the pool. Once that is done, and all items have been moved into
-the new block of memory, this function iterates through the old blocks and
-frees each one. It also performs the necessary housekeeping to record the
-freed memory blocks. At the end of this function, the pool will have only one
-block of memory on its free list.
-
-=cut
-
-*/
-
-static void
-free_old_mem_blocks(
-        ARGMOD(Memory_Pools *mem_pools),
-        ARGMOD(Variable_Size_Pool *pool),
-        ARGMOD(Memory_Block *new_block),
-        UINTVAL total_size)
-{
-    ASSERT_ARGS(free_old_mem_blocks)
-    Memory_Block *prev_block = new_block;
-    Memory_Block *cur_block  = new_block->prev;
-
-    PARROT_ASSERT(new_block == pool->top_block);
-
-    while (cur_block) {
-        Memory_Block * const next_block = cur_block->prev;
-
-        if (is_block_almost_full(cur_block)) {
-            /* Skip block */
-            prev_block = cur_block;
-            cur_block  = next_block;
-        }
-        else {
-            /* Note that we don't have it any more */
-            mem_pools->memory_allocated -= cur_block->size;
-            mem_pools->memory_used -= cur_block->size - cur_block->free;
-
-            /* 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;
-
-            /* Unlink it from list */
-            prev_block->prev = next_block;
-        }
-    }
-
-    /* Terminate list */
-    prev_block->prev = NULL;
-
-
-    /* ANR: I suspect this should be set to new_block->size, instead of passing
-     * in the raw value of total_size, because alloc_new_block pads the size of
-     * the new block under certain conditions. Leaving it unmodified for now,
-     * so this refactor has no functionality changes, only code cleanups.*/
-    pool->total_allocated        = total_size;
-    pool->guaranteed_reclaimable = 0;
-    pool->possibly_reclaimable   = 0;
-}
-
 /*
 
 =item C<static int is_block_almost_full(const Memory_Block *block)>
@@ -604,30 +216,6 @@
 
 /*
 
-=item C<char * aligned_mem(const Buffer *buffer, char *mem)>
-
-Returns a pointer to the aligned allocated storage for Buffer C<buffer>,
-which might not be the same as the pointer to C<buffer> because of
-memory alignment.
-
-=cut
-
-*/
-
-PARROT_CANNOT_RETURN_NULL
-PARROT_WARN_UNUSED_RESULT
-char *
-aligned_mem(SHIM(const Buffer *buffer), ARGIN(char *mem))
-{
-    ASSERT_ARGS(aligned_mem)
-    mem += sizeof (void *);
-    mem  = (char *)(((unsigned long)(mem + WORD_ALIGN_1)) & WORD_ALIGN_MASK);
-
-    return mem;
-}
-
-/*
-
 =back
 
 =head2 Parrot Re/Allocate Code
@@ -1018,54 +606,6 @@
 }
 
 
-/*
-
-=item C<static void free_memory_pool(Variable_Size_Pool *pool)>
-
-Frees a memory pool; helper function for C<Parrot_gc_destroy_memory_pools>.
-
-=cut
-
-*/
-
-static void
-free_memory_pool(ARGFREE(Variable_Size_Pool *pool))
-{
-    ASSERT_ARGS(free_memory_pool)
-
-    Memory_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);
-}
-
-
-/*
-
-=item C<void Parrot_gc_destroy_memory_pools(PARROT_INTERP, Memory_Pools
-*mem_pools)>
-
-Destroys the memory pool and the constant string pool. Loop through both
-pools and destroy all memory blocks contained in them. Once all the
-blocks are freed, free the pools themselves.
-
-=cut
-
-*/
-
-void
-Parrot_gc_destroy_memory_pools(SHIM_INTERP, ARGMOD(Memory_Pools *mem_pools))
-{
-    ASSERT_ARGS(Parrot_gc_destroy_memory_pools)
-
-    free_memory_pool(mem_pools->string_gc.constant_string_pool);
-    free_memory_pool(mem_pools->string_gc.memory_pool);
-}
 
 /*
 

Modified: branches/string_gc_encapsulate/src/gc/gc_ms.c
==============================================================================
--- branches/string_gc_encapsulate/src/gc/gc_ms.c	Thu Sep 16 13:53:33 2010	(r49060)
+++ branches/string_gc_encapsulate/src/gc/gc_ms.c	Thu Sep 16 13:53:54 2010	(r49061)
@@ -457,7 +457,7 @@
     Parrot_gc_destroy_header_pools(interp, interp->mem_pools);
 
     /* memory pools in resources */
-    Parrot_gc_destroy_memory_pools(interp, interp->mem_pools);
+    Parrot_gc_str_finalize(interp, &interp->mem_pools->string_gc);
 
     /* mem subsystem is dead now */
     mem_internal_free(interp->mem_pools);

Modified: branches/string_gc_encapsulate/src/gc/gc_private.h
==============================================================================
--- branches/string_gc_encapsulate/src/gc/gc_private.h	Thu Sep 16 13:53:33 2010	(r49060)
+++ branches/string_gc_encapsulate/src/gc/gc_private.h	Thu Sep 16 13:53:54 2010	(r49061)
@@ -508,11 +508,6 @@
 /* HEADERIZER BEGIN: src/gc/alloc_resources.c */
 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
 
-PARROT_CANNOT_RETURN_NULL
-PARROT_WARN_UNUSED_RESULT
-char * aligned_mem(SHIM(const Buffer *buffer), ARGIN(char *mem))
-        __attribute__nonnull__(2);
-
 void check_buffer_ptr(
     ARGMOD(Buffer * pobj),
     ARGMOD(Variable_Size_Pool * pool))
@@ -521,15 +516,6 @@
         FUNC_MODIFIES(* pobj)
         FUNC_MODIFIES(* pool);
 
-void compact_pool(PARROT_INTERP,
-    ARGMOD(Memory_Pools *mem_pools),
-    ARGMOD(Variable_Size_Pool *pool))
-        __attribute__nonnull__(1)
-        __attribute__nonnull__(2)
-        __attribute__nonnull__(3)
-        FUNC_MODIFIES(*mem_pools)
-        FUNC_MODIFIES(*pool);
-
 void merge_pools(
     ARGMOD(Variable_Size_Pool *dest),
     ARGMOD(Variable_Size_Pool *source))
@@ -544,11 +530,6 @@
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*mem_pools);
 
-void Parrot_gc_destroy_memory_pools(SHIM_INTERP,
-    ARGMOD(Memory_Pools *mem_pools))
-        __attribute__nonnull__(2)
-        FUNC_MODIFIES(*mem_pools);
-
 void Parrot_gc_merge_memory_pools(
     ARGMOD(Interp *dest_interp),
     ARGMOD(Memory_Pools *dest_arena),
@@ -559,15 +540,9 @@
         FUNC_MODIFIES(*dest_interp)
         FUNC_MODIFIES(*dest_arena);
 
-#define ASSERT_ARGS_aligned_mem __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(mem))
 #define ASSERT_ARGS_check_buffer_ptr __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(pobj) \
     , PARROT_ASSERT_ARG(pool))
-#define ASSERT_ARGS_compact_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(interp) \
-    , PARROT_ASSERT_ARG(mem_pools) \
-    , PARROT_ASSERT_ARG(pool))
 #define ASSERT_ARGS_merge_pools __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(dest) \
     , PARROT_ASSERT_ARG(source))
@@ -575,9 +550,6 @@
      __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(mem_pools))
-#define ASSERT_ARGS_Parrot_gc_destroy_memory_pools \
-     __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
-       PARROT_ASSERT_ARG(mem_pools))
 #define ASSERT_ARGS_Parrot_gc_merge_memory_pools __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(dest_interp) \
     , PARROT_ASSERT_ARG(dest_arena) \
@@ -722,6 +694,10 @@
         __attribute__nonnull__(1)
         __attribute__nonnull__(2);
 
+void Parrot_gc_str_finalize(SHIM_INTERP, ARGMOD(String_GC *gc))
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*gc);
+
 void Parrot_gc_str_free_buffer_storage(SHIM_INTERP,
     ARGIN(String_GC *gc),
     ARGMOD(Buffer *b))
@@ -769,6 +745,8 @@
 #define ASSERT_ARGS_Parrot_gc_str_compact_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(gc))
+#define ASSERT_ARGS_Parrot_gc_str_finalize __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(gc))
 #define ASSERT_ARGS_Parrot_gc_str_free_buffer_storage \
      __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(gc) \

Modified: branches/string_gc_encapsulate/src/gc/string_gc.c
==============================================================================
--- branches/string_gc_encapsulate/src/gc/string_gc.c	Thu Sep 16 13:53:33 2010	(r49060)
+++ branches/string_gc_encapsulate/src/gc/string_gc.c	Thu Sep 16 13:53:54 2010	(r49061)
@@ -30,12 +30,23 @@
 #define RESOURCE_DEBUG_SIZE 1000000
 
 #define RECLAMATION_FACTOR 0.20
+#define WE_WANT_EVER_GROWING_ALLOCATIONS 0
+
+typedef struct string_callback_data {
+    Memory_Block *new_block;     /* A pointer to our working block */
+    char         *cur_spot;      /* Where we're currently copying to */
+} string_callback_data;
 
 /* HEADERIZER HFILE: src/gc/gc_private.h */
 
 /* HEADERIZER BEGIN: static */
 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
 
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+static char * aligned_mem(SHIM(const Buffer *buffer), ARGIN(char *mem))
+        __attribute__nonnull__(2);
+
 static void alloc_new_block(
      ARGMOD(Memory_Pools *mem_pools),
     size_t size,
@@ -47,6 +58,41 @@
         FUNC_MODIFIES(*mem_pools)
         FUNC_MODIFIES(*pool);
 
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+static const char * buffer_location(PARROT_INTERP, ARGIN(const Buffer *b))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2);
+
+static void compact_pool(PARROT_INTERP,
+    ARGMOD(Memory_Pools *mem_pools),
+    ARGMOD(Variable_Size_Pool *pool))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        __attribute__nonnull__(3)
+        FUNC_MODIFIES(*mem_pools)
+        FUNC_MODIFIES(*pool);
+
+static void debug_print_buf(PARROT_INTERP, ARGIN(const Buffer *b))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2);
+
+static void free_memory_pool(ARGFREE(Variable_Size_Pool *pool));
+static void free_old_mem_blocks(
+     ARGMOD(Memory_Pools *mem_pools),
+    ARGMOD(Variable_Size_Pool *pool),
+    ARGMOD(Memory_Block *new_block),
+    UINTVAL total_size)
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        __attribute__nonnull__(3)
+        FUNC_MODIFIES(*mem_pools)
+        FUNC_MODIFIES(*pool)
+        FUNC_MODIFIES(*new_block);
+
+static int is_block_almost_full(ARGIN(const Memory_Block *block))
+        __attribute__nonnull__(1);
+
 PARROT_MALLOC
 PARROT_CANNOT_RETURN_NULL
 static void * mem_allocate(PARROT_INTERP,
@@ -59,6 +105,26 @@
         FUNC_MODIFIES(*mem_pools)
         FUNC_MODIFIES(*pool);
 
+static void move_buffer_callback(PARROT_INTERP,
+    ARGIN(Buffer *b),
+    ARGIN(void *data))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        __attribute__nonnull__(3);
+
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+static char * move_one_buffer(PARROT_INTERP,
+    ARGIN(Memory_Block *pool),
+    ARGMOD(Buffer *old_buf),
+    ARGMOD(char *new_pool_ptr))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        __attribute__nonnull__(3)
+        __attribute__nonnull__(4)
+        FUNC_MODIFIES(*old_buf)
+        FUNC_MODIFIES(*new_pool_ptr);
+
 PARROT_WARN_UNUSED_RESULT
 PARROT_MALLOC
 PARROT_CANNOT_RETURN_NULL
@@ -66,15 +132,49 @@
     size_t min_block,
     NULLOK(compact_f compact));
 
+PARROT_CANNOT_RETURN_NULL
+static UINTVAL pad_pool_size(ARGIN(const Variable_Size_Pool *pool))
+        __attribute__nonnull__(1);
+
+#define ASSERT_ARGS_aligned_mem __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(mem))
 #define ASSERT_ARGS_alloc_new_block __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(mem_pools) \
     , PARROT_ASSERT_ARG(pool) \
     , PARROT_ASSERT_ARG(why))
+#define ASSERT_ARGS_buffer_location __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(b))
+#define ASSERT_ARGS_compact_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(mem_pools) \
+    , PARROT_ASSERT_ARG(pool))
+#define ASSERT_ARGS_debug_print_buf __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(b))
+#define ASSERT_ARGS_free_memory_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
+#define ASSERT_ARGS_free_old_mem_blocks __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(mem_pools) \
+    , PARROT_ASSERT_ARG(pool) \
+    , PARROT_ASSERT_ARG(new_block))
+#define ASSERT_ARGS_is_block_almost_full __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(block))
 #define ASSERT_ARGS_mem_allocate __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(mem_pools) \
     , PARROT_ASSERT_ARG(pool))
+#define ASSERT_ARGS_move_buffer_callback __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(b) \
+    , PARROT_ASSERT_ARG(data))
+#define ASSERT_ARGS_move_one_buffer __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(pool) \
+    , PARROT_ASSERT_ARG(old_buf) \
+    , PARROT_ASSERT_ARG(new_pool_ptr))
 #define ASSERT_ARGS_new_memory_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = (0)
+#define ASSERT_ARGS_pad_pool_size __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(pool))
 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
 /* HEADERIZER END: static */
 
@@ -104,6 +204,29 @@
     alloc_new_block(interp->mem_pools, POOL_SIZE, gc->constant_string_pool, "init");
 }
 
+/*
+
+=item C<void Parrot_gc_destroy_memory_pools(PARROT_INTERP, Memory_Pools
+*mem_pools)>
+
+Destroys the memory pool and the constant string pool. Loop through both
+pools and destroy all memory blocks contained in them. Once all the
+blocks are freed, free the pools themselves.
+
+=cut
+
+*/
+
+void
+Parrot_gc_str_finalize(SHIM_INTERP, ARGMOD(String_GC *gc))
+{
+    ASSERT_ARGS(Parrot_gc_str_finalize)
+
+    free_memory_pool(gc->constant_string_pool);
+    free_memory_pool(gc->memory_pool);
+}
+
+
 
 void
 Parrot_gc_str_alloc_new_block(
@@ -601,6 +724,470 @@
 
 /*
 
+=item C<static char * aligned_mem(const Buffer *buffer, char *mem)>
+
+Returns a pointer to the aligned allocated storage for Buffer C<buffer>,
+which might not be the same as the pointer to C<buffer> because of
+memory alignment.
+
+=cut
+
+*/
+
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+static char *
+aligned_mem(SHIM(const Buffer *buffer), ARGIN(char *mem))
+{
+    ASSERT_ARGS(aligned_mem)
+    mem += sizeof (void *);
+    mem  = (char *)(((unsigned long)(mem + WORD_ALIGN_1)) & WORD_ALIGN_MASK);
+
+    return mem;
+}
+
+/*
+
+=item C<static const char * buffer_location(PARROT_INTERP, const Buffer *b)>
+
+Recturns a constant string representing the location of the given
+Buffer C<b> in one of the PMC registers. If the PMC is not located
+in one of the PMC registers of the current context, returns the
+string C<"???">.
+
+=cut
+
+*/
+
+#if RESOURCE_DEBUG
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+static const char *
+buffer_location(PARROT_INTERP, ARGIN(const Buffer *b))
+{
+    ASSERT_ARGS(buffer_location)
+    Parrot_Context * const ctx = CONTEXT(interp);
+    static char reg[10];
+    UINTVAL i;
+
+    for (i = 0; i < ctx->n_regs_used[REGNO_STR]; ++i) {
+        PObj * const obj = (PObj *)Parrot_pcc_get_STRING_reg(interp, ctx, i);
+        if ((PObj *)obj == b) {
+            sprintf(reg, "S%d", (int)i);
+            return reg;
+        }
+    }
+
+    return "???";
+}
+
+/*
+
+=item C<static void debug_print_buf(PARROT_INTERP, const Buffer *b)>
+
+Prints a debug statement with information about the given PObj C<b>.
+=cut
+
+*/
+
+static void
+debug_print_buf(PARROT_INTERP, ARGIN(const Buffer *b))
+{
+    ASSERT_ARGS(debug_print_buf)
+    fprintf(stderr, "found %p, len %d, flags 0x%08x at %s\n",
+            b, (int)Buffer_buflen(b), (uint)PObj_get_FLAGS(b),
+            buffer_location(interp, b));
+}
+#endif
+
+/*
+
+=back
+
+=head2 Compaction Code
+
+=over 4
+
+=item C<static void compact_pool(PARROT_INTERP, Memory_Pools *mem_pools,
+Variable_Size_Pool *pool)>
+
+Compact the string buffer pool. Does not perform a GC scan, or mark items
+as being alive in any way.
+
+=cut
+
+*/
+
+static void
+compact_pool(PARROT_INTERP,
+        ARGMOD(Memory_Pools *mem_pools),
+        ARGMOD(Variable_Size_Pool *pool))
+{
+    ASSERT_ARGS(compact_pool)
+    INTVAL        j;
+    UINTVAL       total_size;
+
+    Fixed_Size_Arena *cur_buffer_arena;
+
+    /* Contains new_block and cur_spot */
+    string_callback_data cb_data;
+
+
+    /* Bail if we're blocked */
+    if (mem_pools->gc_sweep_block_level)
+        return;
+
+    ++mem_pools->gc_sweep_block_level;
+
+    /* We're collecting */
+    mem_pools->mem_allocs_since_last_collect    = 0;
+    mem_pools->header_allocs_since_last_collect = 0;
+    ++mem_pools->gc_collect_runs;
+
+    /* Snag a block big enough for everything */
+    total_size = pad_pool_size(pool);
+
+    if (total_size == 0) {
+        free_old_mem_blocks(mem_pools, pool, pool->top_block, total_size);
+        --mem_pools->gc_sweep_block_level;
+        return;
+    }
+
+    Parrot_gc_str_alloc_new_block(mem_pools, total_size, pool, "inside compact");
+
+    cb_data.new_block = pool->top_block;
+
+    /* Start at the beginning */
+    cb_data.cur_spot  = cb_data.new_block->start;
+
+    /* Run through all the Buffer header pools and copy */
+    interp->gc_sys->iterate_live_strings(interp, move_buffer_callback, &cb_data);
+
+    /* Okay, we're done with the copy. Set the bits in the pool struct */
+    /* First, where we allocate next */
+    cb_data.new_block->top = cb_data.cur_spot;
+
+    PARROT_ASSERT(cb_data.new_block->size
+                  >=
+                  (size_t)cb_data.new_block->top - (size_t)cb_data.new_block->start);
+
+    /* How much is free. That's the total size minus the amount we used */
+    cb_data.new_block->free     = cb_data.new_block->size
+                                  - (cb_data.cur_spot - cb_data.new_block->start);
+    mem_pools->memory_collected += (cb_data.cur_spot - cb_data.new_block->start);
+    mem_pools->memory_used      += (cb_data.cur_spot - cb_data.new_block->start);
+
+    free_old_mem_blocks(mem_pools, pool, cb_data.new_block, total_size);
+
+    --mem_pools->gc_sweep_block_level;
+}
+
+/*
+=item C<static void move_buffer_callback(PARROT_INTERP, Buffer *b, void *data)>
+
+Callback for live STRING/Buffer for compating.
+
+=cut
+*/
+static void
+move_buffer_callback(PARROT_INTERP, ARGIN(Buffer *b), ARGIN(void *data))
+{
+    ASSERT_ARGS(move_buffer_callback)
+    string_callback_data *cb = (string_callback_data*)data;
+
+    if (Buffer_buflen(b) && PObj_is_movable_TESTALL(b)) {
+        Memory_Block *old_block = Buffer_pool(b);
+
+        if (!is_block_almost_full(old_block))
+            cb->cur_spot = move_one_buffer(interp, cb->new_block, b, cb->cur_spot);
+    }
+
+}
+
+/*
+
+=item C<static UINTVAL pad_pool_size(const Variable_Size_Pool *pool)>
+
+Calculate the size of the new pool. The currently used size equals the total
+size minus the reclaimable size. Add a minimum block to the current amount, so
+we can avoid having to allocate it in the future.
+
+Returns 0 if all blocks below the top block are almost full. In this case
+compacting is not needed.
+
+TODO - Big blocks
+
+Currently all available blocks are compacted into one new
+block with total_size. This is 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 to check liveness. OTOH if this compaction
+is running through all the buffer headers, there is no
+relation to the block.
+
+Moving the live bit into the buffer thus also solves this
+problem easily.
+
+=cut
+
+*/
+
+PARROT_CANNOT_RETURN_NULL
+static UINTVAL
+pad_pool_size(ARGIN(const Variable_Size_Pool *pool))
+{
+    ASSERT_ARGS(pad_pool_size)
+    Memory_Block *cur_block = pool->top_block->prev;
+
+    UINTVAL total_size   = 0;
+#if RESOURCE_DEBUG
+    size_t  total_blocks = 1;
+#endif
+
+    while (cur_block) {
+        if (!is_block_almost_full(cur_block))
+            total_size += cur_block->size - cur_block->freed - cur_block->free;
+        cur_block   = cur_block->prev;
+#if RESOURCE_DEBUG
+        ++total_blocks;
+#endif
+    }
+
+    if (total_size == 0)
+        return 0;
+
+    cur_block = pool->top_block;
+    if (!is_block_almost_full(cur_block))
+        total_size += cur_block->size - cur_block->freed - cur_block->free;
+
+    /* this makes for ever increasing allocations but fewer collect runs */
+#if WE_WANT_EVER_GROWING_ALLOCATIONS
+    total_size += pool->minimum_block_size;
+#endif
+
+#if RESOURCE_DEBUG
+    fprintf(stderr, "Total blocks: %d\n", total_blocks);
+#endif
+
+    return total_size;
+}
+
+/*
+
+=item C<static char * move_one_buffer(PARROT_INTERP, Memory_Block *pool, Buffer
+*old_buf, char *new_pool_ptr)>
+
+The compact_pool operation collects disjointed blocks of memory allocated on a
+given pool's free list into one large block of memory. Once the new larger
+memory block has been allocated, this function moves one buffer from the old
+memory block to the new memory block and marks that it has been moved.
+
+=cut
+
+*/
+
+PARROT_WARN_UNUSED_RESULT
+PARROT_CANNOT_RETURN_NULL
+static char *
+move_one_buffer(PARROT_INTERP, ARGIN(Memory_Block *pool),
+        ARGMOD(Buffer *old_buf), ARGMOD(char *new_pool_ptr))
+{
+    ASSERT_ARGS(move_one_buffer)
+
+    INTVAL       *flags     = NULL;
+    ptrdiff_t     offset    = 0;
+    Memory_Block *old_block = NULL;
+#if RESOURCE_DEBUG
+    if (Buffer_buflen(old_buf) >= RESOURCE_DEBUG_SIZE)
+        debug_print_buf(interp, old_buf);
+#else
+    UNUSED(interp);
+#endif
+
+    /* we can't perform the math all the time, because
+        * strstart might be in unallocated memory */
+    if (PObj_is_COWable_TEST(old_buf)) {
+        flags = Buffer_bufflagsptr(old_buf);
+        old_block = Buffer_pool(old_buf);
+
+        if (PObj_is_string_TEST(old_buf)) {
+            offset = (ptrdiff_t)((STRING *)old_buf)->strstart -
+                (ptrdiff_t)Buffer_bufstart(old_buf);
+        }
+    }
+
+    /* buffer has already been moved; just change the header */
+    if (flags && (*flags & Buffer_shared_FLAG)
+              && (*flags & Buffer_moved_FLAG)) {
+        /* Find out who else references our data */
+        Buffer * const hdr = *((Buffer **)Buffer_bufstart(old_buf));
+
+        PARROT_ASSERT(PObj_is_COWable_TEST(old_buf));
+
+        /* Make sure they know that we own it too */
+        /* Set Buffer_shared_FLAG in new buffer */
+        *Buffer_bufflagsptr(hdr) |= Buffer_shared_FLAG;
+
+        /* Now make sure we point to where the other guy does */
+        Buffer_bufstart(old_buf) = Buffer_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(old_buf))
+            ((STRING *)old_buf)->strstart =
+                (char *)Buffer_bufstart(old_buf) + offset;
+    }
+    else {
+        new_pool_ptr = aligned_mem(old_buf, new_pool_ptr);
+
+        /* Copy our memory to the new pool */
+        memcpy(new_pool_ptr, Buffer_bufstart(old_buf),
+                                Buffer_buflen(old_buf));
+
+        /* If we're shared */
+        if (flags && (*flags & Buffer_shared_FLAG)) {
+            PARROT_ASSERT(PObj_is_COWable_TEST(old_buf));
+
+            /* Let the old buffer know how to find us */
+            *((Buffer **)Buffer_bufstart(old_buf)) = old_buf;
+
+            /* Finally, let the tail know that we've moved, so
+                * that any other references can know to look for
+                * us and not re-copy */
+            *flags |= Buffer_moved_FLAG;
+        }
+
+        Buffer_bufstart(old_buf) = new_pool_ptr;
+
+        /* Remember new pool inside */
+        *Buffer_poolptr(old_buf) = pool;
+
+        if (PObj_is_string_TEST(old_buf))
+            ((STRING *)old_buf)->strstart =
+                    (char *)Buffer_bufstart(old_buf) + offset;
+
+        new_pool_ptr += Buffer_buflen(old_buf);
+    }
+
+    return new_pool_ptr;
+}
+
+/*
+
+=item C<static void free_old_mem_blocks( Memory_Pools *mem_pools,
+Variable_Size_Pool *pool, Memory_Block *new_block, UINTVAL total_size)>
+
+The compact_pool operation collects disjointed blocks of memory allocated on a
+given pool's free list into one large block of memory, setting it as the new
+top block for the pool. Once that is done, and all items have been moved into
+the new block of memory, this function iterates through the old blocks and
+frees each one. It also performs the necessary housekeeping to record the
+freed memory blocks. At the end of this function, the pool will have only one
+block of memory on its free list.
+
+=cut
+
+*/
+
+static void
+free_old_mem_blocks(
+        ARGMOD(Memory_Pools *mem_pools),
+        ARGMOD(Variable_Size_Pool *pool),
+        ARGMOD(Memory_Block *new_block),
+        UINTVAL total_size)
+{
+    ASSERT_ARGS(free_old_mem_blocks)
+    Memory_Block *prev_block = new_block;
+    Memory_Block *cur_block  = new_block->prev;
+
+    PARROT_ASSERT(new_block == pool->top_block);
+
+    while (cur_block) {
+        Memory_Block * const next_block = cur_block->prev;
+
+        if (is_block_almost_full(cur_block)) {
+            /* Skip block */
+            prev_block = cur_block;
+            cur_block  = next_block;
+        }
+        else {
+            /* Note that we don't have it any more */
+            mem_pools->memory_allocated -= cur_block->size;
+            mem_pools->memory_used -= cur_block->size - cur_block->free;
+
+            /* 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;
+
+            /* Unlink it from list */
+            prev_block->prev = next_block;
+        }
+    }
+
+    /* Terminate list */
+    prev_block->prev = NULL;
+
+
+    /* ANR: I suspect this should be set to new_block->size, instead of passing
+     * in the raw value of total_size, because alloc_new_block pads the size of
+     * the new block under certain conditions. Leaving it unmodified for now,
+     * so this refactor has no functionality changes, only code cleanups.*/
+    pool->total_allocated        = total_size;
+    pool->guaranteed_reclaimable = 0;
+    pool->possibly_reclaimable   = 0;
+}
+
+/*
+
+=item C<static int is_block_almost_full(const Memory_Block *block)>
+
+Tests if the block is almost full and should be skipped during compacting.
+
+Returns true if less that 20% of block is available
+
+=cut
+
+*/
+
+static int
+is_block_almost_full(ARGIN(const Memory_Block *block))
+{
+    ASSERT_ARGS(is_block_almost_full)
+    return 5 * (block->free + block->freed) < block->size;
+}
+
+/*
+
+=item C<static void free_memory_pool(Variable_Size_Pool *pool)>
+
+Frees a memory pool; helper function for C<Parrot_gc_destroy_memory_pools>.
+
+=cut
+
+*/
+
+static void
+free_memory_pool(ARGFREE(Variable_Size_Pool *pool))
+{
+    ASSERT_ARGS(free_memory_pool)
+
+    Memory_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);
+}
+
+/*
+
 =back
 
 =head1 SEE ALSO


More information about the parrot-commits mailing list