> A quick test (on Mono) says "no", but trying with a class that
> declares __del__ doesn't either, so I'd want to hook up a debugger on
> Windows to check for sure. I'm not sure if the backing types created
> by IronPython have finalizers, but I think not (and reading through
> the RefEmit code will take longer than I have right now.)

Sounds like IronPython would not support __del__ at all. Is this a basic, maybe 
well-known issue or are finalizers principally supported, but currently broken?
I appended a unit test file and would like to know which of the tests exactly 
fail on IronPython. I will surely take the time to test this myself, but it 
might
take some days until I come to setting up mono and IronPython. And I thought 
you might be interested in these tests too ;-)

To support acquired finalizers, Jython would have to track all instances for 
each class. So it could update them if the class would acquire a finalizer.
Obviously this is insanely expensive for such a rare use-case, so I added a 
builtin method __ensure_finalizer__ to Jython, which allows - at least manually 
- to fix this.
One can tell already existing instances manually that their class acquired a 
finalizer (also works for new style classes):

class A():
   pass

 def A__del__():
     print "A finalized"

 a1 = A()
 A.__del__ = A__del__
 a1.__ensure_finalizer__()
 a1 = None

I have only rough knowledge of .net, but believe it behaves somewhat equal to 
Java regarding to gc and finalizers.
Since IronPython appears to have equal issues, I would offer guidance and 
discussion if you might want to port the Jython approach.
At least I wanted to make you aware of the 
__ensure_finalizer__()-method-manual-fix-approach and suggest that if 
IronPython would
ever implement an equal method to agree on a common name for it.

> IronPython is about the same:
> https://github.com/IronLanguages/main/blob/ipy-2.7-maint/Languages/IronPython/IronPython.Modules/gc.cs
Okay, I already expected something like this. I have rough plans to enhance the 
Jython gc module by a manual track-method.
For debugging one can add certain objects to it via gc.track(object). Then gc 
would monitor these and serve the original
gc module methods with this information (e.g. number of collected (i.e. 
monitored and collected) objects).
I just wanted to make you aware of this and suggest that if you might want to 
do an equal monitoring, to agree on a common
name for the manual track method too. However, I would resume this topic when I 
proceed with this.


- Stefan



> Gesendet: Donnerstag, 18. September 2014 um 14:35 Uhr
> Von: "Jeff Hardy" <jdha...@gmail.com>
> An: "Stefan Richthofer" <stefan.richtho...@gmx.de>
> Cc: "ironpython-users@python.org" <ironpython-users@python.org>, "Dino 
> Viehland" <di...@microsoft.com>
> Betreff: Re: [Ironpython-users] Some questions about gc and finalizers
>
> (+dinov, who just needs to page this in rather than learn it all from scratch)
> 
> On Wed, Sep 17, 2014 at 2:18 PM, Stefan Richthofer
> <stefan.richtho...@gmx.de> wrote:
> > Dear IronPython community,
> >
> > I recently worked on Jython issue 1057 (http://bugs.jython.org/issue1057) 
> > and also improved the current solution of 
> > http://bugs.jython.org/issue1634167 a bit. For these issues, it is very 
> > hard for Jython to emulate CPython behavior due to the fundamentally 
> > different GC implementation, so I suspected, IronPython might have similar 
> > problems and wondered how they are solved here.
> > Since I never used IronPython or .net, I would appreciate answers on a 
> > rather abstract level and apologize that I have no ambition to look into 
> > the source myself. I'm just hoping to find someone (optimally a core-dev), 
> > who can simply answer it and is maybe open for a discussion of solutions.
> >
> > My questions are:
> > - does IronPython support acquired finalizers?
> > i.e.
> > class A():
> >   pass
> >
> > def A__del__():
> >     print "A finalized"
> >
> > a1 = A()
> > A.__del__ = A__del__
> > a1 = None
> >
> >
> > Would it output "A finalized" or not?
> 
> A quick test (on Mono) says "no", but trying with a class that
> declares __del__ doesn't either, so I'd want to hook up a debugger on
> Windows to check for sure. I'm not sure if the backing types created
> by IronPython have finalizers, but I think not (and reading through
> the RefEmit code will take longer than I have right now.)
> 
> > In Jython this won't work so easy, because Jython avoids to overwrite the 
> > finalize method for all instances for its expensiveness. So only instances 
> > known to need finalization on creation time will be finalized. AfaIk, 
> > finalizers in .net are as expensive as in Java, so how would this work in 
> > IronPython?
> >
> >
> > - how complete is the support of the gc module?
> > i.e.
> > In Jython the support is rather poor; most methods are just implemented as 
> > stubs, i.e. one-liners as
> >    throw Py.NotImplementedError("not applicable to Java GC")
> >
> 
> IronPython is about the same:
> https://github.com/IronLanguages/main/blob/ipy-2.7-maint/Languages/IronPython/IronPython.Modules/gc.cs
> 
> > gc.collect usually returns the number of collected objects, but Jython just 
> > calls java.lang.System.gc() and returns None. I believe, tracking the 
> > number of collected objects would be possible, but very expensive, so this 
> > maybe could be added as a start-up-flag feature for debugging. How far does 
> > IronPython support this currently?
> 
> IronPython just returns the number of bytes freed by the call:
> https://github.com/IronLanguages/main/blob/ipy-2.7-maint/Languages/IronPython/IronPython/Runtime/PythonContext.cs#L3465.
> 
> - Jeff
> 
'''
Created on 06.08.2014
'''

import unittest
import types
import time
import gc

finalizeMsgList = []
verbose = False
resurrectedObject_I = None
resurrectedObject_J = None
resurrectedObject_K = None
resurrectedObject_L = None
resurrectedObject_M = None
resurrectedObject_N = None

def runGC():
    gc.collect()
    #The sleep time is for Python implementations that run gc in a parallel thread (e.g. Jython)
    time.sleep(0.1)


class ResurrectableDummyClass():

    def __init__(self, name):
        self.name = name
        self.doResurrection = True

    def __str__(self):
        return self.name


class ResurrectableDummyClassNew(object):

    def __init__(self, name):
        self.name = name
        self.doResurrection = True

    def __str__(self):
        return self.name


def __del__I(self):
    global resurrectedObject_I
    global finalizeMsgList
    if finalizeMsgList is None:
        finalizeMsgList = []
    finalizeMsgList.append(str(self)+" finalized (ResurrectableDummyClass)")
    if verbose:
        print str(self)+" finalized (ResurrectableDummyClass)"
    if self.doResurrection:
        resurrectedObject_I = self

def __del__J(self):
    global resurrectedObject_J
    finalizeMsgList.append(str(self)+" finalized (ResurrectableDummyClass)")
    if verbose:
        print str(self)+" finalized (ResurrectableDummyClass)"
    if self.doResurrection:
        resurrectedObject_J = self

def __del__K(self):
    global resurrectedObject_K
    finalizeMsgList.append(str(self)+" finalized (ResurrectableDummyClass)")
    if verbose:
        print str(self)+" finalized (ResurrectableDummyClass)"
    if self.doResurrection:
        resurrectedObject_K = self

def __del__L(self):
    global resurrectedObject_L
    global finalizeMsgList
    if finalizeMsgList is None:
        finalizeMsgList = []
    finalizeMsgList.append(str(self)+" finalized (ResurrectableDummyClass)")
    if verbose:
        print str(self)+" finalized (ResurrectableDummyClass)"
    if self.doResurrection:
        resurrectedObject_L = self

def __del__M(self):
    global resurrectedObject_M
    finalizeMsgList.append(str(self)+" finalized (ResurrectableDummyClass)")
    if verbose:
        print str(self)+" finalized (ResurrectableDummyClass)"
    if self.doResurrection:
        resurrectedObject_M = self

def __del__N(self):
    global resurrectedObject_N
    finalizeMsgList.append(str(self)+" finalized (ResurrectableDummyClass)")
    if verbose:
        print str(self)+" finalized (ResurrectableDummyClass)"
    if self.doResurrection:
        resurrectedObject_N = self

delI = __del__I
delJ = __del__J
delK = __del__K
delL = __del__L
delM = __del__M
delN = __del__N


class DummyClass():
    
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return self.name


class DummyClassDel():
    
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return self.name
    
    def __del__(self):
        finalizeMsgList.append(str(self)+" finalized (DummyClassDel)")
        if verbose:
            print str(self)+" finalized (DummyClassDel)"


class DummyClassNew(object):
    
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return self.name

class DummyClassDelNew(object):
    
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return self.name
    
    def __del__(self):
        finalizeMsgList.append(str(self)+" finalized (DummyClassDelNew)")
        if verbose:
            print str(self)+" finalized (DummyClassDelNew)"

class DummyFileClassNew(file):
    
    def __init__(self, name):
        self.name0 = name
    
    def __str__(self):
        return self.name0

    def __del__(self):
        finalizeMsgList.append(str(self)+" finalized (DummyFileClassNew)")
        if verbose:
            print str(self)+" finalized (DummyFileClassNew)"


def __del__class(self):
    finalizeMsgList.append(str(self)+" finalized (acquired by class)")
    if verbose:
        print str(self)+" finalized (acquired by class)"

def __del__object(self):
    finalizeMsgList.append(str(self)+" finalized (acquired by object)")
    if verbose:
        print str(self)+" finalized (acquired by object)"

def __del__object0():
    finalizeMsgList.append("_ finalized (acquired by object)")
    if verbose:
        print "_ finalized (acquired by object)"

delClass = __del__class
delObject = __del__object
delObject0 = __del__object0

class TestFinalizers(unittest.TestCase):
    def test_finalizer_builtin_oldStyleClass(self):
        A = DummyClassDel("A")
        A = None
        runGC()
        self.assertIn("A finalized (DummyClassDel)", finalizeMsgList)

    def test_classAcquiresFinalizer_beforeInstanciation_oldStyleClass(self):
        DummyClass.__del__ = delClass
        B = DummyClass("B")
        B = None
        runGC()
        self.assertIn("B finalized (acquired by class)", finalizeMsgList)
        del DummyClass.__del__

    def test_classAcquiresFinalizer_afterInstanciation_oldStyleClass(self):
        #okay to fail in Jython without the manual ensureFinalizer call
        C = DummyClass("C")
        DummyClass.__del__ = delClass
        try:
            C.__ensure_finalizer__()
        except:
            pass
        C = None
        runGC()
        self.assertIn("C finalized (acquired by class)", finalizeMsgList)
        del DummyClass.__del__

    def test_instanceAcquiresFinalizer_bound_oldStyleClass(self):
        D = DummyClassDel("D")
        dl = types.MethodType(delObject, D.name)
        D.__del__ = dl
        D = None
        runGC()
        self.assertNotIn("D finalized (DummyClassDel)", finalizeMsgList)
        self.assertIn("D finalized (acquired by object)", finalizeMsgList)

    def test_finalizer_builtin_newStyleClass(self):
        E = DummyClassDelNew("E")
        E = None
        runGC()
        self.assertIn("E finalized (DummyClassDelNew)", finalizeMsgList)

    def test_classAcquiresFinalizer_beforeInstanciation_newStyleClass(self):
        DummyClassNew.__del__ = delClass
        F = DummyClassNew("F")
        F = None
        runGC()
        self.assertIn("F finalized (acquired by class)", finalizeMsgList)
        del DummyClassNew.__del__

    def test_classAcquiresFinalizer_afterInstanciation_newStyleClass(self):
        #okay to fail in Jython without the manual __ensure_finalizer__ call
        G = DummyClassNew("G")
        DummyClassNew.__del__ = delClass
        try:
            G.__ensure_finalizer__()
        except:
            pass
        G = None
        runGC()
        self.assertIn("G finalized (acquired by class)", finalizeMsgList)
        del DummyClassNew.__del__

    def test_instanceAcquiresFinalizer_bound_newStyleClass(self):
        """
        It seems, CPython prohibits new style instances from acquiring a finalizer.
        """
        H = DummyClassDelNew("H")
        H.__del__ = types.MethodType(delObject, H.name)
        H = None
        runGC()
        self.assertIn("H finalized (DummyClassDelNew)", finalizeMsgList)
        self.assertNotIn("H finalized (acquired by object)", finalizeMsgList)

    def test_instanceAcquiresFinalizer_bound_newStyleClass2(self):
        """
        It seems, CPython prohibits new style instances from acquiring a finalizer.
        If one calls the instance-acquired __del__ manually, it works, but the gc
        will still call the old one.
        """
        H = DummyClassDelNew("H2")
        H.__del__ = types.MethodType(delObject, H.name)
        H.__del__()
        H = None
        runGC()
        self.assertIn("H2 finalized (DummyClassDelNew)", finalizeMsgList)
        self.assertIn("H2 finalized (acquired by object)", finalizeMsgList)

    def test_objectResurrection_oldStyleClass(self):
        ResurrectableDummyClass.__del__ = delI
        I = ResurrectableDummyClass("I")
        I = None
        runGC()
        self.assertIn("I finalized (ResurrectableDummyClass)", finalizeMsgList)
        self.assertEqual(str(resurrectedObject_I), "I")

    def test_objectDoubleResurrection_oldStyleClass(self):
        #okay to fail in Jython without the manual ensureFinalizer calls
        ResurrectableDummyClass.__del__ = delJ
        J = ResurrectableDummyClass("J")
        J = None
        
        runGC()
        self.assertIn("J finalized (ResurrectableDummyClass)", finalizeMsgList)
        global resurrectedObject_J
        self.assertEqual(str(resurrectedObject_J), "J")
        J = resurrectedObject_J
        resurrectedObject_J = None
        self.assertIsNone(resurrectedObject_J)
        try:
            #For Jython one can restore the finalizer manually.
            #This is offered as an easy fix if the CPython behavior
            #in this test should be needed for some reason.
            J.__ensure_finalizer__()
        except:
            pass
        J = None

        runGC()
        self.assertEqual(str(resurrectedObject_J), "J")
        resurrectedObject_J.doResurrection = False
        try:
            #again...
            resurrectedObject_J.__ensure_finalizer__()
        except:
            pass
        resurrectedObject_J = None
        
        runGC()
        self.assertIsNone(resurrectedObject_J)
        

    def test_objectDoubleResurrectionAndFinalize_oldStyleClass(self):
        #okay to fail in Jython without the manual ensureFinalizer calls
        ResurrectableDummyClass.__del__ = delK
        K = ResurrectableDummyClass("K")
        K = None

        runGC()
        self.assertIn("K finalized (ResurrectableDummyClass)", finalizeMsgList)
        finalizeMsgList.remove("K finalized (ResurrectableDummyClass)")
        self.assertNotIn("K finalized (ResurrectableDummyClass)", finalizeMsgList)
        global resurrectedObject_K
        self.assertEqual(str(resurrectedObject_K), "K")
        K = resurrectedObject_K
        resurrectedObject_K = None
        self.assertIsNone(resurrectedObject_K)
        try:
            K.__ensure_finalizer__()
        except:
            pass
        K = None

        runGC()
        self.assertIn("K finalized (ResurrectableDummyClass)", finalizeMsgList)
        self.assertEqual(str(resurrectedObject_K), "K")

    def test_objectResurrection_newStyleClass(self):
        ResurrectableDummyClassNew.__del__ = delL
        L = ResurrectableDummyClassNew("L")
        L = None
        runGC()
        self.assertIn("L finalized (ResurrectableDummyClass)", finalizeMsgList)
        self.assertEqual(str(resurrectedObject_L), "L")

    def test_objectDoubleResurrection_newStyleClass(self):
        #okay to fail in Jython without the manual ensureFinalizer calls
        ResurrectableDummyClassNew.__del__ = delM
        M = ResurrectableDummyClassNew("M")
        M = None

        runGC()
        self.assertIn("M finalized (ResurrectableDummyClass)", finalizeMsgList)
        global resurrectedObject_M
        self.assertEqual(str(resurrectedObject_M), "M")
        M = resurrectedObject_M
        resurrectedObject_M = None
        self.assertIsNone(resurrectedObject_M, None)
        try:
            M.__ensure_finalizer__()
        except:
            pass
        M = None

        runGC()
        self.assertEqual(str(resurrectedObject_M), "M")

    def test_objectDoubleResurrectionAndFinalize_newStyleClass(self):
        #okay to fail in Jython without the manual ensureFinalizer calls
        ResurrectableDummyClassNew.__del__ = delN
        N = ResurrectableDummyClassNew("N")
        N = None

        runGC()
        self.assertIn("N finalized (ResurrectableDummyClass)", finalizeMsgList)
        finalizeMsgList.remove("N finalized (ResurrectableDummyClass)")
        self.assertNotIn("N finalized (ResurrectableDummyClass)", finalizeMsgList)
        global resurrectedObject_N
        self.assertEqual(str(resurrectedObject_N), "N")
        N = resurrectedObject_N
        resurrectedObject_N = None
        self.assertIsNone(resurrectedObject_N)
        try:
            N.__ensure_finalizer__()
        except:
            pass
        N = None

        runGC()
        self.assertIn("N finalized (ResurrectableDummyClass)", finalizeMsgList)
        self.assertEqual(str(resurrectedObject_N), "N")

    def test_file_overwrite_del(self):
        O = DummyFileClassNew("O")
        O = None

        runGC()
        self.assertIn("O finalized (DummyFileClassNew)", finalizeMsgList)

if __name__ == '__main__':
    unittest.main()

_______________________________________________
Ironpython-users mailing list
Ironpython-users@python.org
https://mail.python.org/mailman/listinfo/ironpython-users

Reply via email to