Skip to content
This repository has been archived by the owner on Dec 4, 2018. It is now read-only.

Commit

Permalink
Ensure that threading causes no problems for tile downloads.
Browse files Browse the repository at this point in the history
  • Loading branch information
otsaloma committed Feb 8, 2015
1 parent 3e6b026 commit 0d948c1
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 10 deletions.
2 changes: 0 additions & 2 deletions poor/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,6 @@ def _update_tile(self, tilesource, xmin, xmax, ymin, ymax, zoom,
return pyotherside.send("show-tile", item.uid)
path = tilesource.download(tile)
if path is None: return
# Abort if map moved out of view during download.
if timestamp != self._timestamp: return
uri = (poor.util.path2uri(path) if os.path.isabs(path) else path)
corners = tilesource.tile_corners(tile)
item = self.tilecollection.get_free(
Expand Down
30 changes: 22 additions & 8 deletions poor/tilesource.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import re
import sys
import time
import threading

__all__ = ("TileSource",)

Expand Down Expand Up @@ -53,12 +54,14 @@ def __init__(self, id):
# Initialize properties only once.
if hasattr(self, "id"): return
values = self._load_attributes(id)
self._active_urls = set()
self.attribution = values["attribution"]
self._blacklist = set()
self.extension = values.get("extension", "")
self._failures = {}
self.format = values["format"]
self.id = id
self._lock = threading.Lock()
self.max_age = values.get("max_age", None)
self.name = values["name"]
self._provider = None
Expand All @@ -70,6 +73,7 @@ def __init__(self, id):
self.z = max(0, min(40, values.get("z", 0)))
self._init_provider(values["format"])

@poor.util.locked_method
def _add_to_blacklist(self, url):
"""Add `url` to list of tiles to not try to download."""
self._blacklist.add(url)
Expand Down Expand Up @@ -97,8 +101,12 @@ def download(self, tile, retry=1):
return None
try:
connection = self._pool.get(url)
# ConnectionPool.get can block for a while, check that
# another thread didn't download the tile during.
with self._lock:
# Ensure that only one thread downloads URL.
if url in self._active_urls: return None
self._active_urls.add(url)
# Check again that another thread didn't download
# the tile during the above preparations.
cached = self._tile_in_cache(path)
if cached is not None:
return cached
Expand Down Expand Up @@ -146,16 +154,22 @@ def download(self, tile, retry=1):

# Keep track of the amount of failed downloads per URL
# and avoid trying to endlessly redownload the same tile.
self._failures.setdefault(url, 0)
self._failures[url] += 1
if self._failures[url] > 2:
print("Blacklisted after 3 failed attempts.",
file=sys.stderr)
should_blacklist = False
with self._lock:
self._failures.setdefault(url, 0)
self._failures[url] += 1
if self._failures[url] > 2:
should_blacklist = True
print("Blacklisted after 3 failed attempts.",
file=sys.stderr)
del self._failures[url]
if should_blacklist:
self._add_to_blacklist(url)
del self._failures[url]
return None
finally:
self._pool.put(url, connection)
with self._lock:
self._active_urls.discard(url)
assert retry > 0
return self.download(tile, retry-1)

Expand Down

0 comments on commit 0d948c1

Please sign in to comment.