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
1=== modified file 'softwarecenter/utils.py'
2--- softwarecenter/utils.py 2012-09-17 10:33:16 +0000
3+++ softwarecenter/utils.py 2012-09-24 08:00:27 +0000
4@@ -921,11 +921,28 @@
5 f.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_SIZE, 0, 0,
6 self._cancellable,
7 self._check_url_reachable_and_then_download_cb,
8- None)
9-
10- def _check_url_reachable_and_then_download_cb(self, f, result,
11- user_data=None):
12+ url)
13+
14+ def _ensure_correct_url(self, want_url):
15+ """This function will ensure that the url we requested to download
16+ earlier matches that is now downloaded.
17+ """
18+ # this function is needed as there is a rance condition when the
19+ # operation is finished but the signal is not delivered yet (its
20+ # still in the gtk event loop). in this case there is nothing to
21+ # self._cancel but self.url/self.dest_file_path will still point to
22+ # the wrong file
23+ if self.url != want_url:
24+ self.LOG.warn("url changed from '%s' to '%s'" % (
25+ want_url, self.url))
26+ return False
27+ return True
28+
29+ def _check_url_reachable_and_then_download_cb(self, f, result, want_url):
30 self.LOG.debug("_check_url_reachable_and_then_download_cb: %s" % f)
31+ if not self._ensure_correct_url(want_url):
32+ return
33+ # normal operation
34 try:
35 info = f.query_info_finish(result)
36 etag = info.get_etag()
37@@ -935,15 +952,17 @@
38 etag))
39 # url is reachable, now download the file
40 f.load_contents_async(
41- self._cancellable, self._file_download_complete_cb, None)
42+ self._cancellable, self._file_download_complete_cb, want_url)
43 except GObject.GError as e:
44 self.LOG.debug("file *not* reachable %s" % self.url)
45 self.emit('file-url-reachable', False)
46 self.emit('error', GObject.GError, e)
47 del f
48
49- def _file_download_complete_cb(self, f, result, path=None):
50+ def _file_download_complete_cb(self, f, result, want_url):
51 self.LOG.debug("file download completed %s" % self.dest_file_path)
52+ if not self._ensure_correct_url(want_url):
53+ return
54 # The result from the download is actually a tuple with three
55 # elements (content, size, etag?)
56 # The first element is the actual content so let's grab that
57
58=== modified file 'tests/test_downloader.py'
59--- tests/test_downloader.py 2012-08-15 12:11:04 +0000
60+++ tests/test_downloader.py 2012-09-24 08:00:27 +0000
61@@ -1,7 +1,10 @@
62+import glob
63 import os
64+import tempfile
65 import unittest
66
67 from tests.utils import (
68+ do_events,
69 do_events_with_sleep,
70 setup_test_env,
71 )
72@@ -60,5 +63,28 @@
73 self.assertTrue(self._image_is_reachable)
74 self.assertTrue(os.path.exists(self.DOWNLOAD_FILENAME))
75
76+ @unittest.skip("highly dependant on network speed so disabled by default")
77+ def test_download_race_lp839462(self):
78+ hosts = ["google", "ubuntu"]
79+ tmpdir = tempfile.mkdtemp()
80+ # mvo: in order to reproduce the race these paramters can be tweaked
81+ # its highly depedant on network latency, for me it hit 1/3 times
82+ # at around i=7,8 - if the race ever hits again this will be a useful
83+ # starting point
84+ for i in range(5,15):
85+ for host in hosts:
86+ url = "http://www.%s.com/" % host
87+ target = os.path.join(tmpdir, host)
88+ self.downloader.download_file(url, target)
89+ do_events_with_sleep(iterations=i)
90+ for host in hosts:
91+ downloaded_file = os.path.join(tmpdir, host)
92+ if os.path.exists(downloaded_file):
93+ self.assertTrue(
94+ host in open(downloaded_file).read().lower(),
95+ "file '%s' contains wrong content" % downloaded_file)
96+ for f in glob.glob(tmpdir+"/*"):
97+ os.unlink(f)
98+
99 if __name__ == "__main__":
100 unittest.main()

Subscribers

People subscribed via source and target branches