diff options
author | Benjamin Berg <benjamin@sipsolutions.net> | 2022-12-27 00:34:21 +0100 |
---|---|---|
committer | Benjamin Berg <benjamin@sipsolutions.net> | 2022-12-27 11:09:45 +0100 |
commit | 7d1230da9c6f871afe626d0d41093dd1bc863eac (patch) | |
tree | 82924fd521297bbe4b1b554efd0dd7ff08abf0fd /tests/test_source.py | |
parent | c57bd80c07a4f846c4ebcc55dcd10f629319c0be (diff) | |
download | pygobject-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.py | 37 |
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): |