www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 3523] New: Fiber is not garbage collected properly

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3523

           Summary: Fiber is not garbage collected properly
           Product: D
           Version: 2.032
          Platform: Other
        OS/Version: Linux
            Status: NEW
          Severity: normal
          Priority: P2
         Component: druntime
        AssignedTo: sean invisibleduck.org
        ReportedBy: baryluk smp.if.uj.edu.pl


--- Comment #0 from Witold Baryluk <baryluk smp.if.uj.edu.pl> 2009-11-18
19:50:33 PST ---
Program below leaks memory.

import core.thread;
import std.stdio;

lass DerivedFiber : Fiber {
  this() { super( &run );  }
 private void run() { Fiber.yield(); }
}

import core.memory : GC;

void main() {
    foreach (i ; 1 .. 1000000) {
        Fiber derived = new DerivedFiber();
        derived.call();
        GC.collect(); // doesn't work
    }
}

Manual destruction of fiber works:
              delete derived;
        }
and then it doesn't leek.

chaning Fiber to "scope" also works (just like delete befor "}"

I know it have something to do with similarity of Fiber to thread, that Fiber
have own pool of root variables and own stack. But if Fiber is not running, and
it is not accesible by any reference from any Thread, then it is imposible to
resume its operation by call(), so also it's root variables and stack is not
avaible, so it can (and should?) be garbage collected.

I have code which creates few fibers, do milions call/yield operations, then
destroy fibers, and recreat new ones, for and essentially repeats. I was hoping
this would not leak memory. Unfortonetly it is.

I this is intended behaviour it should be documented somewhere.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 18 2009
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3523



--- Comment #1 from Witold Baryluk <baryluk smp.if.uj.edu.pl> 2009-11-18
19:56:25 PST ---
If one will not call derived.call() (so leaving Fiber in TERM state, and never
running it at all) it will be properly collected.

Adding after derived.call(), a derived.reset() to make it back to TERM state,
doesn't help (still it is not collected).

Adding second derived.call(), after first one, will make collect() to work
correctly.

So i can assume it run() method terminates correctly and underlaying stack is
destroyed, object is properly destructed in colletion phase.

Essentially my problem is because my Fibers doesn't terminate at all they
"yield" infinitly (saving some auxilary data in fields of some objects, so this
data can be used outside of yield, essentiall in thread which called call), but
i want to terminate them automatically (when they are not referenced by any
thread or other Fiber) if needed.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 18 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3523


Sean Kelly <sean invisibleduck.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |ASSIGNED


--- Comment #2 from Sean Kelly <sean invisibleduck.org> 2009-11-19 07:28:26 PST
---
Hm... that's tricky.  The fiber implementation needs to hold a reference to the
fiber on its stack for context switching, and that's the reference that is
keeping the fiber alive.  I'll play with the stack pointers a bit and see if
things work if I exclude that reference from the GC scan.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 19 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3523



--- Comment #3 from Witold Baryluk <baryluk smp.if.uj.edu.pl> 2009-11-24
16:05:22 PST ---
I "solved" my problem by changin one of my base clases from:

abstract class AGenerator : Fiber {
protected:
        this(void delegate() dg) {
                super(dg);
        }
}


To:
abstract class AGenerator {
private:
        Fiber x;

protected:
        this(void delegate() dg) {
                x = new Fiber(dg);
        }
        ~this() {
                delete x;
        }
public:
        void call() {
                x.call();
        }
        void yield() {
                x.yield();
        }
        Fiber.State state() {
                return x.state;
        }
}

And it magically started working correctly (Fibers are properly destroyed).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 24 2009
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3523



--- Comment #4 from Witold Baryluk <baryluk smp.if.uj.edu.pl> 2009-11-25
05:53:40 PST ---
No, sorry i made mistake. Even after change it is not garbage collected.
Which is normal, because this Fiber is running some method from this object
(namly method named void iter() ), so this object (AGenerator) is still
referenced by Fiber, and it's destructor can't be called. So probably only way
to call destructor will be to separate this into two classes, which is
referenced by delegate from class A, and second which is used only for calling
from outside of fiber.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 25 2009
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=3523



--- Comment #5 from Witold Baryluk <baryluk smp.if.uj.edu.pl> 2009-11-25
06:11:41 PST ---
Ok, i now i solved it using kind of hack:

/** This class is written because Fiber's are not correctly garbage collected
*/
class GenWrap(T : AGenerator, T2) {
    private T x; /// T derives from AGenerator which derives from Fiber
public:
    this(T x_) { x = x_; }
    ~this() { delete x; }
    T2 getNext() { return x.getNext(); }
    T o() { return x; } // don't assign return value to any variable which can
live longer than this object
}

This is hack, because it can destroy Fibers which are still referenced
somewhere. So All my direct usages of variables of type T, must be changed to
use GenWrap.o(), to be sure that delete x inside destructor is safe.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Nov 25 2009