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:22:26 +0100 |
commit | c8d89fdf69b70f0add71e07413e2b3b23687ab6b (patch) | |
tree | f9494da83031c983044dfc54f93feebe0c824ef2 /tests/test_source.py | |
parent | c57bd80c07a4f846c4ebcc55dcd10f629319c0be (diff) | |
download | pygobject-benzea/gsource-fix2.tar.gz |
source: Fix source finalization in some corner casesbenzea/gsource-fix2
GLib will call the finalize handler when the GSource is destroyed. We
already enforce destruction from __del__, however, the final reference
might only be dropped later on because the main context (or something
else) is holding another reference. The main case where this happens is
if the source is pending at the time, e.g. if it is being destroyed
while it is dispatching.
After destroy is called on the source, the only callback that can still
be called is finalize. As such, we can simply call finalize immediately
and rely on GLib cleaning up the underlying GSource at a later point.
Note that it can also happen that __del__ is not called at all if the
interpreter quits. In this case, neither __del__ nor finalize will be
called even though the underlying GSource may be finalized correctly.
Compared to the previous behaviour this is a big improvement, as it
would crash previously.
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..40e09cd1 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 self.finalized + assert self.dispatched + def test_extra_init_args(self): class SourceWithInitArgs(GLib.Source): def __init__(self, arg, kwarg=None): |