[svn:parrot] r39004 - in branches/gc_internals: . src/gc
whiteknight at svn.parrot.org
whiteknight at svn.parrot.org
Thu May 21 01:41:49 UTC 2009
Author: whiteknight
Date: Thu May 21 01:41:48 2009
New Revision: 39004
URL: https://trac.parrot.org/parrot/changeset/39004
Log:
[gc_internals] move resources.c -> alloc_resources.c
Added:
branches/gc_internals/src/gc/alloc_resources.c (props changed)
- copied unchanged from r39001, branches/gc_internals/src/gc/resources.c
Deleted:
branches/gc_internals/src/gc/resources.c
Modified:
branches/gc_internals/MANIFEST
Modified: branches/gc_internals/MANIFEST
==============================================================================
--- branches/gc_internals/MANIFEST Thu May 21 01:39:56 2009 (r39003)
+++ branches/gc_internals/MANIFEST Thu May 21 01:41:48 2009 (r39004)
@@ -1265,7 +1265,7 @@
src/gc/pools.c []
src/gc/alloc_register.c []
src/gc/res_lea.c []
-src/gc/resources.c []
+src/gc/alloc_resources.c []
src/gc/system.c []
src/global.c []
src/global_setup.c []
Copied: branches/gc_internals/src/gc/alloc_resources.c (from r39001, branches/gc_internals/src/gc/resources.c)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ branches/gc_internals/src/gc/alloc_resources.c Thu May 21 01:41:48 2009 (r39004, copy of r39001, branches/gc_internals/src/gc/resources.c)
@@ -0,0 +1,750 @@
+/*
+Copyright (C) 2001-2009, Parrot Foundation.
+$Id$
+
+=head1 NAME
+
+src/gc/resources.c - Allocate and deallocate tracked resources
+
+=head1 DESCRIPTION
+
+Functions to manage non-PObj memory, including strings and buffers.
+
+=head2 Parrot Memory Management Code
+
+=over 4
+
+=cut
+
+*/
+
+#include "parrot/parrot.h"
+#include "gc_private.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: src/gc/gc_private.h */
+
+/* HEADERIZER BEGIN: static */
+/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
+
+static void alloc_new_block(PARROT_INTERP,
+ size_t size,
+ ARGMOD(Memory_Pool *pool),
+ ARGIN(const char *why))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(3)
+ __attribute__nonnull__(4)
+ FUNC_MODIFIES(*pool);
+
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+static const char* buffer_location(PARROT_INTERP, ARGIN(const PObj *b))
+ __attribute__nonnull__(1)
+ __attribute__nonnull__(2);
+
+static void debug_print_buf(PARROT_INTERP, ARGIN(const PObj *b))
+ __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));
+
+#define ASSERT_ARGS_alloc_new_block __attribute__unused__ int _ASSERT_ARGS_CHECK = \
+ PARROT_ASSERT_ARG(interp) \
+ || 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_debug_print_buf __attribute__unused__ int _ASSERT_ARGS_CHECK = \
+ PARROT_ASSERT_ARG(interp) \
+ || PARROT_ASSERT_ARG(b)
+#define ASSERT_ARGS_new_memory_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = 0
+/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
+/* HEADERIZER END: static */
+
+
+/*
+
+=item C<static void alloc_new_block(PARROT_INTERP, size_t size, Memory_Pool
+*pool, const char *why)>
+
+Allocate a new memory block. We allocate either the requested size or the
+default size, whichever is larger. Add the new block to the given memory
+pool. The given C<char *why> text is used for debugging.
+
+=cut
+
+*/
+
+static void
+alloc_new_block(PARROT_INTERP, size_t size, ARGMOD(Memory_Pool *pool),
+ ARGIN(const char *why))
+{
+ ASSERT_ARGS(alloc_new_block)
+ 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;
+}
+
+/*
+
+=item C<void * mem_allocate(PARROT_INTERP, size_t size, Memory_Pool *pool)>
+
+Allocates memory for headers.
+
+Alignment problems history:
+
+See L<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.
+
+=cut
+
+*/
+
+PARROT_MALLOC
+PARROT_CANNOT_RETURN_NULL
+void *
+mem_allocate(PARROT_INTERP, size_t size, ARGMOD(Memory_Pool *pool))
+{
+ ASSERT_ARGS(mem_allocate)
+ 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 GC mark run to get live flags set
+ * for incremental M&S collection is run from there
+ * but only if there may be something worth collecting!
+ * TODO pass required allocation size to the GC system,
+ * so that collection can be skipped if needed
+ */
+ if (!interp->arena_base->gc_mark_block_level
+ && interp->arena_base->mem_allocs_since_last_collect) {
+ Parrot_gc_mark_and_sweep(interp, GC_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;
+}
+
+/*
+
+=item C<static const char* buffer_location(PARROT_INTERP, const PObj *b)>
+
+Recturns a constant string representing the location of the given
+PObj 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 PObj *b))
+{
+ ASSERT_ARGS(buffer_location)
+ int i;
+ static char reg[10];
+
+ Parrot_Context* const ctx = CONTEXT(interp);
+
+ 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 "???";
+}
+
+/*
+
+=item C<static void debug_print_buf(PARROT_INTERP, const PObj *b)>
+
+Prints a debug statement with information about the given PObj C<b>.
+=cut
+
+*/
+
+static void
+debug_print_buf(PARROT_INTERP, ARGIN(const PObj *b))
+{
+ ASSERT_ARGS(debug_print_buf)
+ 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
+
+/*
+
+=back
+
+=head2 Compaction Code
+
+=over 4
+
+=item C<void compact_pool(PARROT_INTERP, Memory_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_Pool *pool))
+{
+ ASSERT_ARGS(compact_pool)
+ INTVAL j;
+ 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;
+ Arenas * const arena_base = interp->arena_base;
+
+ /* Bail if we're blocked */
+ if (arena_base->gc_sweep_block_level)
+ return;
+
+ ++arena_base->gc_sweep_block_level;
+
+ if (interp->profile)
+ Parrot_gc_profile_start(interp);
+
+ /* We're collecting */
+ arena_base->mem_allocs_since_last_collect = 0;
+ arena_base->header_allocs_since_last_collect = 0;
+ arena_base->gc_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 = pool->top_block;
+
+ total_size = 0;
+
+ 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 = (INTVAL)arena_base->num_sized - 1; j >= 0; --j) {
+ Small_Object_Pool * const header_pool = arena_base->sized_header_pools[j];
+ UINTVAL object_size;
+
+ if (!header_pool)
+ continue;
+
+ object_size = header_pool->object_size;
+
+ for (cur_buffer_arena = header_pool->last_Arena;
+ cur_buffer_arena;
+ cur_buffer_arena = cur_buffer_arena->prev) {
+ Buffer *b = (Buffer *)ARENA_to_PObj(cur_buffer_arena->start_objects);
+ UINTVAL i;
+
+ for (i = cur_buffer_arena->used; i; --i) {
+ INTVAL *ref_count = NULL;
+
+ /* ! (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 = PObj_bufrefcountptr(b);
+
+ 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 */
+ if (ref_count)
+ *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 = new_block->prev;
+
+ PARROT_ASSERT(new_block == pool->top_block);
+
+ 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_gc_profile_end(interp, PARROT_PROF_GC);
+
+ --arena_base->gc_sweep_block_level;
+}
+
+/*
+
+=item C<size_t aligned_size(const Buffer *buffer, size_t len)>
+
+Determines the size of Buffer C<buffer> which has nominal length C<len>.
+The actual size in RAM of the Buffer might be different because of
+alignment issues.
+
+=cut
+
+*/
+
+PARROT_PURE_FUNCTION
+PARROT_WARN_UNUSED_RESULT
+size_t
+aligned_size(ARGIN(const Buffer *buffer), size_t len)
+{
+ ASSERT_ARGS(aligned_size)
+ 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;
+}
+
+/*
+
+=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(ARGIN(const Buffer *buffer), ARGIN(char *mem))
+{
+ ASSERT_ARGS(aligned_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;
+}
+
+/*
+
+=item C<size_t aligned_string_size(size_t len)>
+
+Determines the size of a string of length C<len> in RAM, accounting for
+alignment.
+
+=cut
+
+*/
+
+/* XXX Looks like we can lose buffer here */
+PARROT_CONST_FUNCTION
+PARROT_WARN_UNUSED_RESULT
+size_t
+aligned_string_size(size_t len)
+{
+ ASSERT_ARGS(aligned_string_size)
+ len += sizeof (void*);
+ len = (len + WORD_ALIGN_1) & WORD_ALIGN_MASK;
+ return len;
+}
+
+/*
+
+=back
+
+=head2 Parrot Re/Allocate Code
+
+=over 4
+
+=item C<static Memory_Pool * new_memory_pool(size_t min_block, compact_f
+compact)>
+
+Allocate a new C<Memory_Pool> structures, and set some initial values.
+return a pointer to the new pool.
+
+=cut
+
+*/
+
+PARROT_MALLOC
+PARROT_CANNOT_RETURN_NULL
+static Memory_Pool *
+new_memory_pool(size_t min_block, NULLOK(compact_f compact))
+{
+ ASSERT_ARGS(new_memory_pool)
+ 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;
+}
+
+/*
+
+=item C<void initialize_memory_pools(PARROT_INTERP)>
+
+Initialize the managed memory pools. Parrot maintains two C<Memory_Pool>
+structures, the general memory pool and the constant string pool. Create
+and initialize both pool structures, and allocate initial blocks of memory
+for both.
+
+=cut
+
+*/
+
+void
+initialize_memory_pools(PARROT_INTERP)
+{
+ ASSERT_ARGS(initialize_memory_pools)
+ 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");
+}
+
+
+/*
+
+=item C<void merge_pools(Memory_Pool *dest, Memory_Pool *source)>
+
+Merge two memory pools together. Do this by moving all memory blocks
+from the C<*source> pool into the C<*dest> pool. The C<source> pool
+is emptied, but is not destroyed here.
+
+=cut
+
+*/
+
+void
+merge_pools(ARGMOD(Memory_Pool *dest), ARGMOD(Memory_Pool *source))
+{
+ ASSERT_ARGS(merge_pools)
+ 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;
+}
+
+/*
+
+=back
+
+=head1 SEE ALSO
+
+F<src/gc/memory.c>.
+
+=head1 HISTORY
+
+Initial version by Dan on 2001.10.2.
+
+=cut
+
+*/
+
+/*
+ * Local variables:
+ * c-file-style: "parrot"
+ * End:
+ * vim: expandtab shiftwidth=4:
+ */
Deleted: branches/gc_internals/src/gc/resources.c
==============================================================================
--- branches/gc_internals/src/gc/resources.c Thu May 21 01:41:48 2009 (r39003)
+++ /dev/null 00:00:00 1970 (deleted)
@@ -1,750 +0,0 @@
-/*
-Copyright (C) 2001-2009, Parrot Foundation.
-$Id$
-
-=head1 NAME
-
-src/gc/resources.c - Allocate and deallocate tracked resources
-
-=head1 DESCRIPTION
-
-Functions to manage non-PObj memory, including strings and buffers.
-
-=head2 Parrot Memory Management Code
-
-=over 4
-
-=cut
-
-*/
-
-#include "parrot/parrot.h"
-#include "gc_private.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: src/gc/gc_private.h */
-
-/* HEADERIZER BEGIN: static */
-/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
-
-static void alloc_new_block(PARROT_INTERP,
- size_t size,
- ARGMOD(Memory_Pool *pool),
- ARGIN(const char *why))
- __attribute__nonnull__(1)
- __attribute__nonnull__(3)
- __attribute__nonnull__(4)
- FUNC_MODIFIES(*pool);
-
-PARROT_CANNOT_RETURN_NULL
-PARROT_WARN_UNUSED_RESULT
-static const char* buffer_location(PARROT_INTERP, ARGIN(const PObj *b))
- __attribute__nonnull__(1)
- __attribute__nonnull__(2);
-
-static void debug_print_buf(PARROT_INTERP, ARGIN(const PObj *b))
- __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));
-
-#define ASSERT_ARGS_alloc_new_block __attribute__unused__ int _ASSERT_ARGS_CHECK = \
- PARROT_ASSERT_ARG(interp) \
- || 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_debug_print_buf __attribute__unused__ int _ASSERT_ARGS_CHECK = \
- PARROT_ASSERT_ARG(interp) \
- || PARROT_ASSERT_ARG(b)
-#define ASSERT_ARGS_new_memory_pool __attribute__unused__ int _ASSERT_ARGS_CHECK = 0
-/* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
-/* HEADERIZER END: static */
-
-
-/*
-
-=item C<static void alloc_new_block(PARROT_INTERP, size_t size, Memory_Pool
-*pool, const char *why)>
-
-Allocate a new memory block. We allocate either the requested size or the
-default size, whichever is larger. Add the new block to the given memory
-pool. The given C<char *why> text is used for debugging.
-
-=cut
-
-*/
-
-static void
-alloc_new_block(PARROT_INTERP, size_t size, ARGMOD(Memory_Pool *pool),
- ARGIN(const char *why))
-{
- ASSERT_ARGS(alloc_new_block)
- 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;
-}
-
-/*
-
-=item C<void * mem_allocate(PARROT_INTERP, size_t size, Memory_Pool *pool)>
-
-Allocates memory for headers.
-
-Alignment problems history:
-
-See L<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.
-
-=cut
-
-*/
-
-PARROT_MALLOC
-PARROT_CANNOT_RETURN_NULL
-void *
-mem_allocate(PARROT_INTERP, size_t size, ARGMOD(Memory_Pool *pool))
-{
- ASSERT_ARGS(mem_allocate)
- 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 GC mark run to get live flags set
- * for incremental M&S collection is run from there
- * but only if there may be something worth collecting!
- * TODO pass required allocation size to the GC system,
- * so that collection can be skipped if needed
- */
- if (!interp->arena_base->gc_mark_block_level
- && interp->arena_base->mem_allocs_since_last_collect) {
- Parrot_gc_mark_and_sweep(interp, GC_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;
-}
-
-/*
-
-=item C<static const char* buffer_location(PARROT_INTERP, const PObj *b)>
-
-Recturns a constant string representing the location of the given
-PObj 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 PObj *b))
-{
- ASSERT_ARGS(buffer_location)
- int i;
- static char reg[10];
-
- Parrot_Context* const ctx = CONTEXT(interp);
-
- 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 "???";
-}
-
-/*
-
-=item C<static void debug_print_buf(PARROT_INTERP, const PObj *b)>
-
-Prints a debug statement with information about the given PObj C<b>.
-=cut
-
-*/
-
-static void
-debug_print_buf(PARROT_INTERP, ARGIN(const PObj *b))
-{
- ASSERT_ARGS(debug_print_buf)
- 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
-
-/*
-
-=back
-
-=head2 Compaction Code
-
-=over 4
-
-=item C<void compact_pool(PARROT_INTERP, Memory_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_Pool *pool))
-{
- ASSERT_ARGS(compact_pool)
- INTVAL j;
- 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;
- Arenas * const arena_base = interp->arena_base;
-
- /* Bail if we're blocked */
- if (arena_base->gc_sweep_block_level)
- return;
-
- ++arena_base->gc_sweep_block_level;
-
- if (interp->profile)
- Parrot_gc_profile_start(interp);
-
- /* We're collecting */
- arena_base->mem_allocs_since_last_collect = 0;
- arena_base->header_allocs_since_last_collect = 0;
- arena_base->gc_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 = pool->top_block;
-
- total_size = 0;
-
- 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 = (INTVAL)arena_base->num_sized - 1; j >= 0; --j) {
- Small_Object_Pool * const header_pool = arena_base->sized_header_pools[j];
- UINTVAL object_size;
-
- if (!header_pool)
- continue;
-
- object_size = header_pool->object_size;
-
- for (cur_buffer_arena = header_pool->last_Arena;
- cur_buffer_arena;
- cur_buffer_arena = cur_buffer_arena->prev) {
- Buffer *b = (Buffer *)ARENA_to_PObj(cur_buffer_arena->start_objects);
- UINTVAL i;
-
- for (i = cur_buffer_arena->used; i; --i) {
- INTVAL *ref_count = NULL;
-
- /* ! (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 = PObj_bufrefcountptr(b);
-
- 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 */
- if (ref_count)
- *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 = new_block->prev;
-
- PARROT_ASSERT(new_block == pool->top_block);
-
- 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_gc_profile_end(interp, PARROT_PROF_GC);
-
- --arena_base->gc_sweep_block_level;
-}
-
-/*
-
-=item C<size_t aligned_size(const Buffer *buffer, size_t len)>
-
-Determines the size of Buffer C<buffer> which has nominal length C<len>.
-The actual size in RAM of the Buffer might be different because of
-alignment issues.
-
-=cut
-
-*/
-
-PARROT_PURE_FUNCTION
-PARROT_WARN_UNUSED_RESULT
-size_t
-aligned_size(ARGIN(const Buffer *buffer), size_t len)
-{
- ASSERT_ARGS(aligned_size)
- 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;
-}
-
-/*
-
-=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(ARGIN(const Buffer *buffer), ARGIN(char *mem))
-{
- ASSERT_ARGS(aligned_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;
-}
-
-/*
-
-=item C<size_t aligned_string_size(size_t len)>
-
-Determines the size of a string of length C<len> in RAM, accounting for
-alignment.
-
-=cut
-
-*/
-
-/* XXX Looks like we can lose buffer here */
-PARROT_CONST_FUNCTION
-PARROT_WARN_UNUSED_RESULT
-size_t
-aligned_string_size(size_t len)
-{
- ASSERT_ARGS(aligned_string_size)
- len += sizeof (void*);
- len = (len + WORD_ALIGN_1) & WORD_ALIGN_MASK;
- return len;
-}
-
-/*
-
-=back
-
-=head2 Parrot Re/Allocate Code
-
-=over 4
-
-=item C<static Memory_Pool * new_memory_pool(size_t min_block, compact_f
-compact)>
-
-Allocate a new C<Memory_Pool> structures, and set some initial values.
-return a pointer to the new pool.
-
-=cut
-
-*/
-
-PARROT_MALLOC
-PARROT_CANNOT_RETURN_NULL
-static Memory_Pool *
-new_memory_pool(size_t min_block, NULLOK(compact_f compact))
-{
- ASSERT_ARGS(new_memory_pool)
- 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;
-}
-
-/*
-
-=item C<void initialize_memory_pools(PARROT_INTERP)>
-
-Initialize the managed memory pools. Parrot maintains two C<Memory_Pool>
-structures, the general memory pool and the constant string pool. Create
-and initialize both pool structures, and allocate initial blocks of memory
-for both.
-
-=cut
-
-*/
-
-void
-initialize_memory_pools(PARROT_INTERP)
-{
- ASSERT_ARGS(initialize_memory_pools)
- 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");
-}
-
-
-/*
-
-=item C<void merge_pools(Memory_Pool *dest, Memory_Pool *source)>
-
-Merge two memory pools together. Do this by moving all memory blocks
-from the C<*source> pool into the C<*dest> pool. The C<source> pool
-is emptied, but is not destroyed here.
-
-=cut
-
-*/
-
-void
-merge_pools(ARGMOD(Memory_Pool *dest), ARGMOD(Memory_Pool *source))
-{
- ASSERT_ARGS(merge_pools)
- 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;
-}
-
-/*
-
-=back
-
-=head1 SEE ALSO
-
-F<src/gc/memory.c>.
-
-=head1 HISTORY
-
-Initial version by Dan on 2001.10.2.
-
-=cut
-
-*/
-
-/*
- * Local variables:
- * c-file-style: "parrot"
- * End:
- * vim: expandtab shiftwidth=4:
- */
More information about the parrot-commits
mailing list