[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