summaryrefslogtreecommitdiff
path: root/tests/test_source.py
diff options
context:
space:
mode:
authorBenjamin Berg <benjamin@sipsolutions.net>2022-12-27 00:34:21 +0100
committerBenjamin Berg <benjamin@sipsolutions.net>2022-12-27 11:09:45 +0100
commit7d1230da9c6f871afe626d0d41093dd1bc863eac (patch)
tree82924fd521297bbe4b1b554efd0dd7ff08abf0fd /tests/test_source.py
parentc57bd80c07a4f846c4ebcc55dcd10f629319c0be (diff)
downloadpygobject-benzea/gsource-fix.tar.gz
source: Fix destruction in certain corner casesbenzea/gsource-fix
The GSource destructor assumed that __del__ would clean up the object and also cause finalize to be called. However, this may not be true in two scenarios: 1. On abnormal exit, __del__ might not be called 2. The source is pending (or being dispatched) as the context holds a reference The fix used here is to make sure to prevent calling the finalize handler if the object is already destroyed or is being destroyed. In the abnormal exit case (1), this is reasonable to do. The behaviour is more problematic for the case where destruction happens while the source is pending (2). In that case, a custom finalize handler will not be called as the corresponding python object will be destroyed already.
Diffstat (limited to 'tests/test_source.py')
-rw-r--r--tests/test_source.py37
1 files changed, 37 insertions, 0 deletions
diff --git a/tests/test_source.py b/tests/test_source.py
index 9249892e..4f0c5632 100644
--- a/tests/test_source.py
+++ b/tests/test_source.py
@@ -264,6 +264,43 @@ class TestSource(unittest.TestCase):
assert not self.dispatched
assert context.find_source_by_id(id_) is None
+ def test_python_unref_during_dispatch(self):
+ # Tests a Python derived Source which is free'd in the context of
+ # Python, while being dispatched
+ # i.e. finalize is never called as the python object is destroyed
+ self.dispatched = False
+ self.finalized = False
+
+ class S(GLib.Source):
+ def __init__(s, func=None):
+ s.func = func
+
+ def prepare(s):
+ return (True, 1)
+
+ def check(s):
+ pass
+
+ def dispatch(s, callback, args):
+ self.dispatched = True
+ self.source = None
+ return False
+
+ def finalize(s):
+ self.finalized = True
+
+ context = GLib.MainContext.new()
+ self.source = S()
+ id_ = self.source.attach(context)
+
+ while context.iteration(may_block=False):
+ pass
+
+ assert self.source is None
+ assert context.find_source_by_id(id_) is None
+ assert not self.finalized
+ assert self.dispatched
+
def test_extra_init_args(self):
class SourceWithInitArgs(GLib.Source):
def __init__(self, arg, kwarg=None):