-
Kirill Smelkov authored
Before passing objects to _chanselect for send, pyselect increfs them, as just send does, to indicate that one object reference is passed to channel buffer. On exit, since only one case is actually executed by select, pyselect needs to decref incref'ed object from not executed cases. Pyselect already implements the latter cleanup, but currently the cleanup is executed only if control flow reaches _chanselect at all. Which is a bug, since pyselect can panic or raise an exception just in the middle of preparation phase. -> Fix it by associating the finally-decref cleanup with whole prepare+_chanselect code. Without the fix, the second part of added test (abnormal exit) fails e.g. like: @mark.skipif(not hasattr(sys, 'getrefcount'), # skipped e.g. on PyPy reason="needs sys.getrefcount") def test_select_refleak(): ch1 = chan() ch2 = chan() obj1 = object() obj2 = object() tx1 = (ch1.send, obj1) tx2 = (ch2.send, obj2) # normal exit gc.collect() nref1 = sys.getrefcount(obj1) nref2 = sys.getrefcount(obj2) _, _rx = select( tx1, # 0 tx2, # 1 default, # 2 ) assert (_, _rx) == (2, None) gc.collect() assert sys.getrefcount(obj1) == nref1 gc.collect() assert sys.getrefcount(obj1) == nref2 # abnormal exit with raises(AttributeError) as exc: select( tx1, # 0 tx2, # 1 'zzz', # 2 causes pyselect to panic ) assert exc.value.args == ("'str' object has no attribute '__self__'",) gc.collect() > assert sys.getrefcount(obj1) == nref1 E assert 4 == 3 E -4 E +3 golang/golang_test.py:690: AssertionError The bug was introduced in 3b241983 (Port/move channels to C/C++/Pyx).
e9180de1