# New Ticket Created by  Leopold Toetsch 
# Please include the string:  [perl #18142]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt2/Ticket/Display.html?id=18142 >


Attached is a (big) patch that implements Parrot_destroy.

Test results with --gc-debug: All tests successful.

Test results with leak detection[1]/--gc-debug:

Failed Test  Status Wstat Total Fail  Failed  List of failed
-------------------------------------------------------------------------------
t/op/hacks.t                  2    1  50,00%  1
t/op/interp.t                 2    2 100,00%  1-2
t/op/rx.t                    23    7  30,43%  8-9, 12-14, 21-22
t/op/stacks.t                35    2   5,71%  31-32
t/pmc/pmc.t                  83    4   4,82%  62-64, 76
7 subtests skipped.
Failed 5/34 test scripts, 85.29% okay. 16/495 subtests failed, 96.77% okay.

Some notes WRT still exisiting leaks:

- hacks.t: PIO related, open does new_IO which doesn't seem to store
   this structure somewhere. Explicit closing the handle would help, as
   done now in embed.c
- interp.t: crashes with ATEXIT_DESTROY (2 interpreters interfere)
- rx.t:    Bitmaps leakage
- stacks.t: leaking intstack
- pmc.t:  leaking intqueue

TODO:
- header file changes WRT global warnings
- probably remove intqueue from classes
- move the stack destroying code to stacks, reuse it in
   classes/coroutine.pmc
- fix PIO_destroy
- to clean up properly after exceptions, I added a define locally to
   interpreter.c: ATEXT_DESTROY, which installs an atexit-handler. This
   works only for one existing interpreter, due to the __interpreter
   global. We should either keep an interpreter array or fix
   internal_exception to take an Parrot_Interp*, so that Parrot_destroy
   can be called.
- Running with --gc=malloc is broken (mostly COWed strings).

- (unrelated) mmap in Parrot_readbc is not used now, missing
   HAS_HEADER_SYSMMAN definition

Some notes to the changes:
- classe/*.pmc missing flags setting in clone, destroy
- dod.c  made free_unused_* global
- embed.c PIO_close
- headers.c honor active_destroy, destroy_headers
- chartype.c usascii (better latin1) is default
- interpreter Parrot_destroy
- io/io.c PIO_destroy / _close
- res*.c destroy_memory_pools
- t/src/manifest accout for skipped test

[1] I used run-yamd (s. attached testyamd script) for running all tests
through the memory debugger and made leaking tests fail. This is part
of YAMD (Yet Another Memory Debugger).

Please test the patch on different platforms. Mine is i386/linux.

Have fun,
leo



-- attachment  1 ------------------------------------------------------
url: http://rt.perl.org/rt2/attach/40716/32820/b03730/parrot_destroy.patch

--- parrot/classes/continuation.pmc     Mon Oct 21 13:56:24 2002
+++ parrot-leo/classes/continuation.pmc Tue Oct 29 10:47:04 2002
@@ -60,6 +60,7 @@
             = mem_sys_allocate(sizeof(struct Parrot_Continuation));
         ret->vtable = &Parrot_base_vtables[enum_class_Continuation];
         ret->data = retc;
+        ret->flags |= PMC_custom_mark_FLAG | PMC_active_destroy_FLAG;
         memcpy(retc, SELF->data, sizeof(struct Parrot_Continuation));
         /* we may have copied our stack already, so we need to re-COW
          * it */
--- parrot/classes/coroutine.pmc        Mon Oct 21 13:56:24 2002
+++ parrot-leo/classes/coroutine.pmc    Tue Oct 29 10:57:23 2002
@@ -33,6 +33,9 @@
     }
 
     void destroy () {
+       /* XXX stack_destroy for deeper stacks */
+       mem_sys_free(((struct Parrot_Coroutine *)SELF->data)->ctx.user_stack);
+       mem_sys_free(((struct Parrot_Coroutine *)SELF->data)->ctx.control_stack);
         mem_sys_free(SELF->data);
     }
 
@@ -84,6 +87,7 @@
         PMC * ret = new_pmc_header(INTERP);
         ret->vtable = &Parrot_base_vtables[enum_class_Coroutine];
         ret->data = retc = mem_sys_allocate(sizeof(struct Parrot_Coroutine));
+        ret->flags |= PMC_custom_mark_FLAG | PMC_active_destroy_FLAG;
         memcpy(ret->data, SELF->data, sizeof(struct Parrot_Coroutine));
         stack_mark_cow(retc->ctx.user_stack);
         stack_mark_cow(retc->ctx.control_stack);
--- parrot/classes/sub.pmc      Mon Aug  5 08:49:15 2002
+++ parrot-leo/classes/sub.pmc  Tue Oct 29 10:45:50 2002
@@ -54,6 +54,7 @@
    PMC* clone () {
        PMC * ret = new_pmc_header(INTERP);
        ret->vtable = &Parrot_base_vtables[enum_class_Sub];
+       ret->flags |= PMC_active_destroy_FLAG;
        ret->data = mem_sys_allocate(sizeof(struct Parrot_Sub));
        memcpy(ret->data, SELF->data, sizeof(struct Parrot_Sub));
        return ret;
--- parrot/dod.c        Mon Oct 28 14:59:42 2002
+++ parrot-leo/dod.c    Tue Oct 29 10:44:16 2002
@@ -279,7 +279,7 @@
 }
 
 /* Free up any PMCs that aren't in use */
-static void
+void
 free_unused_PMCs(struct Parrot_Interp *interpreter)
 {
     struct Small_Object_Arena *cur_arena;
@@ -365,7 +365,7 @@
 #endif /* GC_IS_MALLOC */
 /* Put any buffers that are now unused, on to the free list
  * Avoid buffers that are immune from collection (ie, constant) */
-static void
+void
 free_unused_buffers(struct Parrot_Interp *interpreter,
                     struct Small_Object_Pool *pool)
 {
--- parrot/embed.c      Mon Oct 28 14:59:43 2002
+++ parrot-leo/embed.c  Tue Oct 29 08:45:48 2002
@@ -171,6 +171,7 @@
                     "Parrot VM: Problem reading packfile from PIO.\n");
             return NULL;
         }
+        PIO_close(interpreter, io);
     }
     else {
         /* if we've gotten here, we opted not to use PIO to read the file.
@@ -372,13 +373,6 @@
             interpreter->mem_allocs_since_last_collect
         );
     }
-}
-
-void
-Parrot_destroy(struct Parrot_Interp *interp)
-{
-    /* XXX Leaks tons of memory. */
-    mem_sys_free(interp);
 }
 
 /* XXX Doesn't handle arguments with spaces */
--- parrot/headers.c    Mon Oct 28 14:59:43 2002
+++ parrot-leo/headers.c        Tue Oct 29 10:44:26 2002
@@ -34,8 +34,9 @@
 add_free_pmc(struct Parrot_Interp *interpreter,
              struct Small_Object_Pool *pool, void *pmc)
 {
+    if ( ((PMC *)pmc)->flags & PMC_active_destroy_FLAG )
+         ((PMC *)pmc)->vtable->destroy(interpreter, (PMC*) pmc);
     ((PMC *)pmc)->flags = PMC_on_free_list_FLAG;
-    /* XXX custom destroy ?! */
 
     /* Don't let it point to garbage memory */
     ((PMC *)pmc)->data = NULL;
@@ -400,9 +401,10 @@
 void
 Parrot_initialize_header_pools(struct Parrot_Interp *interpreter)
 {
+#if 0
     Parrot_allocate(interpreter, &interpreter->arena_base->extra_buffer_headers, 0);
     
add_extra_buffer_header(interpreter,&interpreter->arena_base->extra_buffer_headers);
-
+#endif
     /* Init the constant string header pool */
     interpreter->arena_base->constant_string_header_pool = 
new_string_pool(interpreter, 1);
 
@@ -416,10 +418,50 @@
 
     /* Init the PMC header pool */
     interpreter->arena_base->pmc_pool = new_pmc_pool(interpreter);
+}
 
+void
+Parrot_destroy_header_pools(struct Parrot_Interp *interpreter)
+{
+    struct Small_Object_Pool *header_pool;
+    struct Small_Object_Arena *cur_arena, *next;
+    int i, j;
+    /* run twice to get rid of COWed strings
+     * in the 2nd pass, the pools themself are freed */
+    for (i = 1; i < 2; i++) {
+        for (j = -4; j < (INTVAL) interpreter->arena_base->num_sized; j++) {
+            if (j == -4)
+                header_pool = interpreter->arena_base->
+                    constant_string_header_pool;
+            else if (j == -3)
+                header_pool = interpreter->arena_base->pmc_pool;
+            else if (j == -2)
+                header_pool = interpreter->arena_base->string_header_pool;
+            else if (j == -1)
+                header_pool = interpreter->arena_base->buffer_header_pool;
+            else
+                header_pool = interpreter->arena_base->sized_header_pools[j];
+            if (header_pool) {
+                if (j == -3)
+                    free_unused_PMCs(interpreter);
+                else
+                    free_unused_buffers(interpreter, header_pool);
+            }
+            if (i == 1 && header_pool) {
+                for (cur_arena = header_pool->last_Arena; cur_arena; ) {
+                    next = cur_arena->prev;
+                    mem_sys_free(cur_arena->start_objects);
+                    mem_sys_free(cur_arena);
+                    cur_arena = next;
+                }
+                free(header_pool);
+            }
 
-
+        }
+    }
+    mem_sys_free(interpreter->arena_base->sized_header_pools);
 }
+
 #if 0
 
 /* if we want these names, they must be added in DOD */
--- parrot/include/parrot/chartype.h    Wed Jun 26 03:00:03 2002
+++ parrot-leo/include/parrot/chartype.h        Fri Oct 25 15:53:45 2002
@@ -16,8 +16,8 @@
 typedef Parrot_UInt (*Parrot_CharType_Transcoder)(Parrot_UInt c);
 
 enum {
-    enum_chartype_unicode,
     enum_chartype_usascii,
+    enum_chartype_unicode,
     enum_chartype_MAX
 };
 
--- parrot/interpreter.c        Mon Oct 28 14:59:43 2002
+++ parrot-leo/interpreter.c    Tue Oct 29 11:11:29 2002
@@ -22,6 +22,8 @@
 #  include "parrot/oplib/core_ops_cg.h"
 #endif
 
+#define ATEXIT_DESTROY
+
 extern op_lib_t *PARROT_CORE_PREDEREF_OPLIB_INIT(void);
 
 
@@ -393,6 +395,10 @@
 /*=for api interpreter make_interpreter
  *  Create the Parrot interpreter.  Allocate memory and clear the registers.
  */
+#ifdef ATEXIT_DESTROY
+static struct Parrot_Interp * __interpreter;
+void Parrot_really_destroy(void);
+#endif
 struct Parrot_Interp *
 make_interpreter(Interp_flags flags)
 {
@@ -538,9 +544,116 @@
     interpreter->resume_offset = 0;
 
     interpreter->prederef_code = (void **)NULL;
+#ifdef ATEXIT_DESTROY
+    atexit(Parrot_really_destroy);
+    __interpreter = interpreter;
+#endif
 
     return interpreter;
 }
+void
+Parrot_destroy(struct Parrot_Interp *interpreter)
+{
+#ifdef ATEXIT_DESTROY
+}
+void
+Parrot_really_destroy(void)
+{
+    Interp * interpreter = __interpreter;
+#endif
+    int i;
+    Parrot_destroy_header_pools(interpreter);
+    Parrot_destroy_memory_pools(interpreter);
+    mem_sys_free(interpreter->arena_base);
+
+    /* XXX walk the stash, pmc's are already dead */
+    mem_sys_free(interpreter->perl_stash);
+    if (interpreter->profile)
+        mem_sys_free(interpreter->profile);
+    mem_sys_free(interpreter->warns );
+
+    /* register */
+    for (i = 0; i< 4; i++) {
+        struct IRegChunk *top, *next;
+        switch(i) {
+            case 0:
+                top = interpreter->ctx.int_reg_top;
+                break;
+            case 1:
+                top = (struct IRegChunk*) interpreter->ctx.num_reg_top;
+                break;
+            case 2:
+                top = (struct IRegChunk*) interpreter->ctx.string_reg_top;
+                break;
+            case 3:
+                top = (struct IRegChunk*) interpreter->ctx.pmc_reg_top;
+                break;
+        }
+        for (; top ; ) {
+            next = top->next;
+            mem_sys_free(top);
+            top = next;
+        }
+    }
+    /* stacks */
+    for (i = 0; i< 3; i++) {
+        Stack_Chunk_t *top, *next;
+        switch(i) {
+            case 0:
+                top = interpreter->ctx.pad_stack;
+                break;
+            case 1:
+                top = interpreter->ctx.user_stack;
+                break;
+            case 2:
+                top = interpreter->ctx.control_stack;
+                break;
+        }
+        while (top->next)
+            top = top->next;
+        while(top) {
+            next = top->prev;
+            mem_sys_free(top);
+            top = next;
+        }
+    }
+    /* intstack */
+    {
+        IntStack top, next, chunk;
+        chunk = top = interpreter->ctx.intstack;
+        while(chunk != top) {
+            next = chunk->prev;
+            mem_sys_free(chunk);
+            chunk = next;
+        }
+        mem_sys_free(top);
+    }
+    /* packfile */
+    {
+        struct PackFile *pf = interpreter->code;
+#if 0
+        PackFile_destroy(pf);
+#else
+        if (pf) {
+            mem_sys_free(pf->header);
+            mem_sys_free(pf->fixup_table);
+            for (i = 0; i < pf->const_table->const_count; i++) {
+                mem_sys_free(pf->const_table->constants[i]);
+            }
+            if (pf->const_table->constants)
+                mem_sys_free(pf->const_table->constants);
+            mem_sys_free(pf->const_table);
+            mem_sys_free(pf->byte_code);
+            mem_sys_free(pf);
+        }
+#endif
+    }
+
+    PIO_destroy(interpreter);
+
+    mem_sys_free(interpreter);
+}
+
 
 /*
  * Local variables:
--- parrot/io/io.c      Mon Oct 28 14:59:45 2002
+++ parrot-leo/io/io.c  Tue Oct 29 09:35:25 2002
@@ -135,6 +135,37 @@
     }
 }
 
+void
+PIO_destroy(theINTERP)
+{
+    ParrotIOLayer *p, *down;
+    ParrotIO *io;
+    int i;
+
+    /* XXX is this all correct? */
+
+    fflush(stdout);
+    fflush(stdout);
+
+    for (i = 0 ; i < PIO_NR_OPEN; i++) {
+        if ( (io = GET_INTERP_IOD(interpreter)->table[i]) ) {
+#if 0
+            PIO_flush(interpreter, io);
+            PIO_close(interpreter, io);
+#endif
+            mem_sys_free(io);
+        }
+    }
+    for (p = GET_INTERP_IO(interpreter); p; ) {
+        down = p->down;
+        if (p->api->Delete)
+            (*p->api->Delete) (p);
+        //mem_sys_free(p);
+        p = down;
+    }
+    mem_sys_free(GET_INTERP_IOD(interpreter)->table);
+    mem_sys_free(interpreter->piodata);
+}
 
 /*
  * IO system destructor, flush streams, free structures, etc.
@@ -486,12 +517,15 @@
 INTVAL
 PIO_close(theINTERP, ParrotIO *io)
 {
+    INTVAL res;
     if (io) {
         ParrotIOLayer *l = io->stack;
         while (l) {
             if (l->api->Close) {
                 PIO_flush(interpreter, io);
-                return (*l->api->Close) (interpreter, l, io);
+                res =  (*l->api->Close) (interpreter, l, io);
+                mem_sys_free(io);
+                return res;
             }
             l = PIO_DOWNLAYER(l);
         }
--- parrot/packfile.c   Mon Oct 28 14:59:43 2002
+++ parrot-leo/packfile.c       Mon Oct 28 17:33:14 2002
@@ -442,6 +442,7 @@
         }
 
     }
+    mem_sys_free(packed);
 
     return ((size_t)(cursor - packed) * sizeof(opcode_t)) == packed_size;
 }
--- parrot/res_lea.c    Sat Oct  5 11:40:33 2002
+++ parrot-leo/res_lea.c        Mon Oct 28 20:26:46 2002
@@ -73,6 +73,10 @@
 Parrot_initialize_memory_pools(struct Parrot_Interp *interpreter)
 {
 }
+void
+Parrot_destroy_memory_pools(Interp *interpreter)
+{
+}
 
 /*
  * Local variables:
--- parrot/resources.c  Mon Oct 28 14:59:43 2002
+++ parrot-leo/resources.c      Mon Oct 28 16:11:43 2002
@@ -531,6 +531,27 @@
         new_memory_pool(8192, NULL);
 }
 
+void
+Parrot_destroy_memory_pools(Interp *interpreter)
+{
+    int i;
+    for (i = 0; i < 2; i++) {
+        struct Memory_Pool *pool = i ?
+            interpreter->arena_base->constant_string_pool :
+            interpreter->arena_base->memory_pool;
+        struct Memory_Block *cur_block, *next_block;
+
+        cur_block = pool->top_block;
+        while (cur_block) {
+            next_block = cur_block->prev;
+            mem_sys_free(cur_block);
+            cur_block = next_block;
+        }
+
+        mem_sys_free(pool);
+    }
+}
+
 
 /*
  * Local variables:
--- parrot/t/src/manifest.t     Wed Oct 23 05:16:28 2002
+++ parrot-leo/t/src/manifest.t Mon Oct 28 19:20:37 2002
@@ -17,6 +17,7 @@
 sub read_manifest;
 
 SKIP: {
+  ok ("ok 3", "ok 3") unless -e 'CVS';
   skip ('No CVS version', 1) unless -e 'CVS';
 
   local @cvs_entries;
--- parrot/testyamd     Tue Oct 29 11:31:12 2002
+++ parrot-leo/testyamd Tue Oct 29 09:58:47 2002
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# run parrot tests under run-yamd
+#
+
+cleanup() {
+       mv parrot.orig parrot
+       exit
+}
+
+trap cleanup 1 2 3
+mv parrot parrot.orig
+echo "run-yamd -l2 -n ./parrot.orig \$@ 2>&1 | perl -ne'
+       BEGIN { undef $/; $|=1; };
+       print \$1 if (/\*{9,9}\n(.*?)\*{9,9}/s);
+       print \$1 if (/((?:WARNI|ERROR).*)/);
+       1;'" > parrot
+chmod 755 parrot
+make test
+cleanup

Reply via email to