Skip to content
Snippets Groups Projects
Commit 59d11b2c authored by Philip Withnall's avatar Philip Withnall
Browse files

tumbler: Fix potential race condition between Ready and Error signals


As explained in the new comment in the Tumbler mixin, it is possible for
the Error and Ready signals from tumblerd to race if one thumbnailer
fails fast while another succeeds (for the same URI) slowly. This
happened for me with the cover and gst thumbnailers on
big_buck_bunny_smaller.ogv — the cover thumbnailer failed fast because
the search terms ‘big buck bunny smaller’ return nothing on omdbapi.com,
while the gst thumbnailer eventually succeeded by grabbing a frame from
the video.

Reviewed-by: default avatarSimon McVittie <simon.mcvittie@collabora.co.uk>
Differential Revision: https://phabricator.apertis.org/D1299
parent 3b54bac9
No related branches found
No related tags found
No related merge requests found
......@@ -30,6 +30,20 @@ class TumblerMixin:
None)
self.tumbler.connect('g-signal', self.__g_signal_cb)
# Track pending errors. Tumbler emits three signals which we care
# about:
# - Ready, which indicates successful thumbnailing for some URIs
# - Error, which indicates errors in thumbnailing for some URIs
# - Finished, which is the final signal emitted for a request
# Ready and Error may both be emitted for the same URI, for example if
# one of the thumbnailers encounters an error when thumbnailing that
# URI, but another thumbnailer subsequently succeeds.
# Therefore we must track errors and only propagate them when Finished
# is received.
#
# Each element of this list of a tuple: (uri, error_message).
self.tumbler_errors = []
# Spy on unicast signals that weren't meant for us, because
# the Thumbnailer API uses those, and we want to use them to
# determine when it has finished. GDBus doesn't have
......@@ -54,13 +68,30 @@ class TumblerMixin:
elif signal_name == 'Finished':
child = parameters.get_child_value(0)
self.tumbler_queue.remove(child.get_uint32())
# Any errors remaining?
if len(self.tumbler_errors) > 0:
l = [': '.join(p) for p in self.tumbler_errors]
raise Exception("Error creating thumbnails: %s" % ', '.join(l))
# Clear them.
self.tumbler_errors = []
elif signal_name == 'Ready':
child = parameters.get_child_value(1)
uris = child.get_strv()
# Remove any now-successful URIs from the error list.
self.tumbler_errors = [p for p in self.tumbler_errors
if p[0] not in uris]
elif signal_name == 'Error':
child = parameters.get_child_value(1)
uris = child.get_strv()
child = parameters.get_child_value(3)
msg = child.get_string()
raise Exception("Error creating thumbnail for %s: %s" %
', '.join(uris), msg)
# Append to the error list.
for uri in uris:
self.tumbler_errors.append((uri, msg))
def __get_supported_cb(self, source, result):
self.tumbler.call_finish(result)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment