Merge lp:~mvo/software-center/downloader-fix-race839462-again into lp:software-center

Proposed by Michael Vogt
Status: Merged
Merged at revision: 3199
Proposed branch: lp:~mvo/software-center/downloader-fix-race839462-again
Merge into: lp:software-center
Diff against target: 100 lines (+51/-6)
2 files modified
softwarecenter/utils.py (+25/-6)
tests/test_downloader.py (+26/-0)
To merge this branch: bzr merge lp:~mvo/software-center/downloader-fix-race839462-again
Reviewer Review Type Date Requested Status
Gary Lasker (community) Approve
Łukasz Czyżykowski (community) Approve
Dave Morley (community) tested the code Approve
Review via email: mp+125941@code.launchpad.net

Description of the change

This branch fixes a race condition in the SimpleFileDownloader when the
file is downloaded but the signal did not get delivered yet by the gtk
event loop.

To post a comment you must log in.
Revision history for this message
Dave Morley (davmor2) wrote :

Tested the code on my box that originally displayed the issue and everything seems to be working fine.

review: Approve (tested the code)
Revision history for this message
Łukasz Czyżykowski (lukasz-czyzykowski) :
review: Approve
Revision history for this message
Gary Lasker (gary-lasker) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'softwarecenter/utils.py'
--- softwarecenter/utils.py 2012-09-17 10:33:16 +0000
+++ softwarecenter/utils.py 2012-09-24 08:00:27 +0000
@@ -921,11 +921,28 @@
921 f.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_SIZE, 0, 0,921 f.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_SIZE, 0, 0,
922 self._cancellable,922 self._cancellable,
923 self._check_url_reachable_and_then_download_cb,923 self._check_url_reachable_and_then_download_cb,
924 None)924 url)
925925
926 def _check_url_reachable_and_then_download_cb(self, f, result,926 def _ensure_correct_url(self, want_url):
927 user_data=None):927 """This function will ensure that the url we requested to download
928 earlier matches that is now downloaded.
929 """
930 # this function is needed as there is a rance condition when the
931 # operation is finished but the signal is not delivered yet (its
932 # still in the gtk event loop). in this case there is nothing to
933 # self._cancel but self.url/self.dest_file_path will still point to
934 # the wrong file
935 if self.url != want_url:
936 self.LOG.warn("url changed from '%s' to '%s'" % (
937 want_url, self.url))
938 return False
939 return True
940
941 def _check_url_reachable_and_then_download_cb(self, f, result, want_url):
928 self.LOG.debug("_check_url_reachable_and_then_download_cb: %s" % f)942 self.LOG.debug("_check_url_reachable_and_then_download_cb: %s" % f)
943 if not self._ensure_correct_url(want_url):
944 return
945 # normal operation
929 try:946 try:
930 info = f.query_info_finish(result)947 info = f.query_info_finish(result)
931 etag = info.get_etag()948 etag = info.get_etag()
@@ -935,15 +952,17 @@
935 etag))952 etag))
936 # url is reachable, now download the file953 # url is reachable, now download the file
937 f.load_contents_async(954 f.load_contents_async(
938 self._cancellable, self._file_download_complete_cb, None)955 self._cancellable, self._file_download_complete_cb, want_url)
939 except GObject.GError as e:956 except GObject.GError as e:
940 self.LOG.debug("file *not* reachable %s" % self.url)957 self.LOG.debug("file *not* reachable %s" % self.url)
941 self.emit('file-url-reachable', False)958 self.emit('file-url-reachable', False)
942 self.emit('error', GObject.GError, e)959 self.emit('error', GObject.GError, e)
943 del f960 del f
944961
945 def _file_download_complete_cb(self, f, result, path=None):962 def _file_download_complete_cb(self, f, result, want_url):
946 self.LOG.debug("file download completed %s" % self.dest_file_path)963 self.LOG.debug("file download completed %s" % self.dest_file_path)
964 if not self._ensure_correct_url(want_url):
965 return
947 # The result from the download is actually a tuple with three966 # The result from the download is actually a tuple with three
948 # elements (content, size, etag?)967 # elements (content, size, etag?)
949 # The first element is the actual content so let's grab that968 # The first element is the actual content so let's grab that
950969
=== modified file 'tests/test_downloader.py'
--- tests/test_downloader.py 2012-08-15 12:11:04 +0000
+++ tests/test_downloader.py 2012-09-24 08:00:27 +0000
@@ -1,7 +1,10 @@
1import glob
1import os2import os
3import tempfile
2import unittest4import unittest
35
4from tests.utils import (6from tests.utils import (
7 do_events,
5 do_events_with_sleep,8 do_events_with_sleep,
6 setup_test_env,9 setup_test_env,
7)10)
@@ -60,5 +63,28 @@
60 self.assertTrue(self._image_is_reachable)63 self.assertTrue(self._image_is_reachable)
61 self.assertTrue(os.path.exists(self.DOWNLOAD_FILENAME))64 self.assertTrue(os.path.exists(self.DOWNLOAD_FILENAME))
6265
66 @unittest.skip("highly dependant on network speed so disabled by default")
67 def test_download_race_lp839462(self):
68 hosts = ["google", "ubuntu"]
69 tmpdir = tempfile.mkdtemp()
70 # mvo: in order to reproduce the race these paramters can be tweaked
71 # its highly depedant on network latency, for me it hit 1/3 times
72 # at around i=7,8 - if the race ever hits again this will be a useful
73 # starting point
74 for i in range(5,15):
75 for host in hosts:
76 url = "http://www.%s.com/" % host
77 target = os.path.join(tmpdir, host)
78 self.downloader.download_file(url, target)
79 do_events_with_sleep(iterations=i)
80 for host in hosts:
81 downloaded_file = os.path.join(tmpdir, host)
82 if os.path.exists(downloaded_file):
83 self.assertTrue(
84 host in open(downloaded_file).read().lower(),
85 "file '%s' contains wrong content" % downloaded_file)
86 for f in glob.glob(tmpdir+"/*"):
87 os.unlink(f)
88
63if __name__ == "__main__":89if __name__ == "__main__":
64 unittest.main()90 unittest.main()

Subscribers

People subscribed via source and target branches