[prev in list] [next in list] [prev in thread] [next in thread] 

List:       perl6-internals
Subject:    Memory leak with parrot interpreter (TT#1765)
From:       Mariano Wahlmann <dichoso () gmail ! com>
Date:       2010-12-28 20:11:46
Message-ID: AANLkTikiwRT994b7sfY7OK-phwa8U+GBr1YwCYd0=RPi () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Hi,

Recently I've been researching the TT#1765 and the solution for that fix is
neither trivial nor simple this is why I'm opening it to a broader audience,

The following code is sufficient to exhibit the problem

.sub 'test' :main
    $P0 = new 'ParrotInterpreter'
.end

*(running it using valgrind with the --leak-test you can see lost blocks )
*
The sequence is the following.

1. master interpreter is created ( frontend/parrot/main.c )
2. the interpreter registers Parrot_really_destroy in its list of
"on_exit_handler" ( src/interp/inter_create.c )
3. a new ParrotInterper PMC is created and the interp is boxed in it (
src/global_setup )
4. IMCC creates new ParrotInterpreter PMC which creates a child interpreter
redoing steps 2,3

*At this point we have:
3 x Parrot Interpreter PMC ( step 3, step 4,  step 3 again )
2 x interpreters ( step 1, step 4 )
*
5. Program runs and IMCC finish
6. Parrot_x_exit is called for the master interp ( src/exit.c )
7. Parrot_really_destroy is called *only* for the master interpreter, it
calls the GC and the GC destroys all PMCs ( src/interp/inter_create.c )
8. VTABLE destroy is called for the 3 interpreters freeing *only* the
Parrot_ParrotInterpreter_attributes part and not the interpreter (
src/pmc/parrotinterpreter.pmc )
9. exit is called ( src/exit.c )

The result is that the child interpreter is never freed. The first question
might arise is "why 3 PMC and 2 interpreters?", well thats a problem in the
"Parrot_gbl_init_world_once()" it is being executed *again* for every child
interpreter

Parrot_gbl_init_world_once(PARROT_INTERP)
{
    ...
    if (!interp->world_inited) {

if the interpreter is new interp->world_inited is false and then it calls
the init_world() boxing the new interpreter into a new PMC. The result is 2
PMC holding the same interpreter.

Getting back to the original subject if we give "VTABLE destroy()" the
responsibility of destroying the interpreter we will have "double free"
corruption for the child interpreter, also it will break
"Parrot_really_destroy()" because it calls "VTABLE destroy()" in the middle
so we will have 2 functions trying to free the same thing.

The other problem is that an interpreter doesn't know who its children are,
so "on_exit" handlers for the children interpreters are never called, then
Parrot_really_destroy is never called for the children.

I think there are 3 action items:
  * Fix the Parrot_gbl_init_world_once to guarantee the "once" part
  * re-think the destroy mechanism for the interpreters and distribute
responsibilities better between "Parrot_really_destroy" and "VTABLE destroy"
  * think what should we do with the children's "on_exit" handlers

Thanks,
bluescreen

[Attachment #5 (text/html)]

Hi,<br><br>Recently I&#39;ve been researching the TT#1765 and the solution for that \
fix is neither trivial nor simple this is why I&#39;m opening it to a broader \
audience,<br><br>The following code is sufficient to exhibit the problem <br> \
<br><span style="font-family: courier new,monospace;">.sub &#39;test&#39; \
:main</span><br style="font-family: courier new,monospace;"><span style="font-family: \
courier new,monospace;">    $P0</span><span style="font-family: courier \
new,monospace;"> = new &#39;ParrotInterpreter&#39;</span><br style="font-family: \
courier new,monospace;"> <span style="font-family: courier \
new,monospace;">.end</span><br><br><i>(running it using valgrind with the --leak-test \
you can see lost blocks )<br></i><br>The sequence is the following.<br><br>1. master \
interpreter is created ( frontend/parrot/main.c )<br> 2. the interpreter registers \
Parrot_really_destroy in its list of &quot;on_exit_handler&quot; ( \
src/interp/inter_create.c ) <br>3. a new ParrotInterper PMC is created and the interp \
is boxed in it ( src/global_setup )<br> 4. IMCC creates new ParrotInterpreter PMC \
which creates a child interpreter redoing steps 2,3<br><br><i>At this point we \
have:<br>3 x Parrot Interpreter PMC ( step 3, step 4,  step 3 again )<br>2 x \
interpreters ( step 1, step 4 )<br> </i><br>5. Program runs and IMCC finish<br>6. \
Parrot_x_exit is called for the master interp ( src/exit.c )<br>7. \
Parrot_really_destroy is called *only* for the master interpreter, it calls the GC \
and the GC destroys all PMCs ( src/interp/inter_create.c )<br> 8. VTABLE destroy is \
called for the 3 interpreters freeing *only* the Parrot_ParrotInterpreter_attributes \
part and not the interpreter ( src/pmc/parrotinterpreter.pmc )<br>9. exit is called ( \
src/exit.c )<br><br>The result is that the child interpreter is never freed. The \
first question might arise is &quot;why 3 PMC and 2 interpreters?&quot;, well thats a \
problem in the &quot;Parrot_gbl_init_world_once()&quot; it is being executed *again* \
for every child interpreter<br> <br><span style="font-family: courier \
new,monospace;">Parrot_gbl_init_world_once(PARROT_INTERP)</span><br \
style="font-family: courier new,monospace;"><span style="font-family: courier \
new,monospace;">{</span><br style="font-family: courier new,monospace;"> <span \
style="font-family: courier new,monospace;">    ...</span><br style="font-family: \
courier new,monospace;"><span style="font-family: courier new,monospace;">    if \
(!interp-&gt;world_inited) {</span><br> <br>if the interpreter is new \
interp-&gt;world_inited is false and then it calls the init_world() boxing the new \
interpreter into a new PMC. The result is 2 PMC holding the same interpreter.<br> \
<br>Getting back to the original subject if we give &quot;VTABLE destroy()&quot; the \
responsibility of destroying the interpreter we will have &quot;double free&quot; \
corruption for the child interpreter, also it will break \
&quot;Parrot_really_destroy()&quot; because it calls &quot;VTABLE destroy()&quot; in \
the middle so we will have 2 functions trying to free the same thing.<br> <br>The \
other problem is that an interpreter doesn&#39;t know who its children are, so \
&quot;on_exit&quot; handlers for the children interpreters are never called, then \
Parrot_really_destroy is never called for the children.<br> <br>I think there are 3 \
action items: <br>  * Fix the Parrot_gbl_init_world_once to guarantee the \
&quot;once&quot; part<br>  * re-think the destroy mechanism for the interpreters and \
distribute responsibilities better between &quot;Parrot_really_destroy&quot; and \
                &quot;VTABLE destroy&quot;<br>
  * think what should we do with the children&#39;s &quot;on_exit&quot; \
handlers<br><br>Thanks,<br>bluescreen<br>



_______________________________________________
http://lists.parrot.org/mailman/listinfo/parrot-dev


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic