- Timestamp:
- 2015-05-20T21:47:44+02:00 (10 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 1 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/cache/ICachedLoaderListener.java
r8344 r8403 11 11 SUCCESS, 12 12 FAILURE, 13 REJECTED13 CANCELED 14 14 } 15 15 /** -
trunk/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
r8401 r8403 16 16 import java.util.concurrent.ConcurrentHashMap; 17 17 import java.util.concurrent.ConcurrentMap; 18 import java.util.concurrent.Executor ;18 import java.util.concurrent.Executors; 19 19 import java.util.concurrent.LinkedBlockingDeque; 20 import java.util.concurrent. RejectedExecutionException;20 import java.util.concurrent.ThreadFactory; 21 21 import java.util.concurrent.ThreadPoolExecutor; 22 22 import java.util.concurrent.TimeUnit; … … 35 35 * 36 36 * @param <K> cache entry key type 37 * @param <V> cache value type 37 38 * 38 39 * Generic loader for HTTP based tiles. Uses custom attribute, to check, if entry has expired … … 62 63 public static final IntegerProperty THREAD_LIMIT = new IntegerProperty("cache.jcs.max_threads", 10); 63 64 64 public static class LIFOQueue extends LinkedBlockingDeque<Runnable> { 65 66 /** 67 * Constructs a new {@code LIFOQueue} with a capacity of {@link Integer#MAX_VALUE}. 68 */ 69 public LIFOQueue() { 70 super(); 71 } 72 73 /** 74 * Constructs a new {@code LIFOQueue} with the given (fixed) capacity. 75 * @param capacity the capacity of this deque 76 * @throws IllegalArgumentException if {@code capacity} is less than 1 77 */ 78 public LIFOQueue(int capacity) { 79 super(capacity); 80 } 81 82 @Override 83 public boolean offer(Runnable t) { 84 return super.offerFirst(t); 85 } 86 87 @Override 88 public Runnable remove() { 89 return super.removeFirst(); 90 } 91 } 92 93 94 /** 95 * ThreadPoolExecutor starts new threads, until THREAD_LIMIT is reached. Then it puts tasks into LIFOQueue, which is fairly 96 * small, but we do not want a lot of outstanding tasks queued, but rather prefer the class consumer to resubmit the task, which are 97 * important right now. 65 /* 66 * ThreadPoolExecutor starts new threads, until THREAD_LIMIT is reached. Then it puts tasks into LinkedBlockingDeque. 98 67 * 99 * This way, if some task gets outdated (for example - user paned the map, and we do not want to download this tile any more), 100 * the task will not be resubmitted, and thus - never queued. 68 * The queue works FIFO, so one needs to take care about ordering of the entries submitted 101 69 * 102 70 * There is no point in canceling tasks, that are already taken by worker threads (if we made so much effort, we can at least cache … … 104 72 * and performance (we do want to have something to offer to worker threads before tasks will be resubmitted by class consumer) 105 73 */ 106 private static Executor DEFAULT_DOWNLOAD_JOB_DISPATCHER = new ThreadPoolExecutor( 74 75 private static ThreadPoolExecutor DEFAULT_DOWNLOAD_JOB_DISPATCHER = new ThreadPoolExecutor( 107 76 2, // we have a small queue, so threads will be quickly started (threads are started only, when queue is full) 108 77 THREAD_LIMIT.get().intValue(), // do not this number of threads … … 110 79 TimeUnit.SECONDS, 111 80 // make queue of LIFO type - so recently requested tiles will be loaded first (assuming that these are which user is waiting to see) 112 new LIFOQueue(5) 81 new LinkedBlockingDeque<Runnable>(), 82 getNamedThreadFactory("JCS downloader") 113 83 ); 84 85 public static ThreadFactory getNamedThreadFactory(final String name) { 86 return new ThreadFactory(){ 87 public Thread newThread(Runnable r) { 88 Thread t = Executors.defaultThreadFactory().newThread(r); 89 t.setName(name); 90 return t; 91 } 92 }; 93 } 114 94 115 95 private static ConcurrentMap<String,Set<ICachedLoaderListener>> inProgress = new ConcurrentHashMap<>(); … … 127 107 private int readTimeout; 128 108 private Map<String, String> headers; 129 private Executor downloadJobExecutor; 109 private ThreadPoolExecutor downloadJobExecutor; 110 private Runnable finishTask; 130 111 131 112 /** … … 139 120 int connectTimeout, int readTimeout, 140 121 Map<String, String> headers, 141 Executor downloadJobExecutor) {122 ThreadPoolExecutor downloadJobExecutor) { 142 123 143 124 this.cache = cache; … … 209 190 } 210 191 // object not in cache, so submit work to separate thread 211 try { 212 if (executionGuard()) { 213 // use getter method, so subclasses may override executors, to get separate thread pool 214 getDownloadExecutor().execute(this); 215 } else { 216 log.log(Level.FINE, "JCS - guard rejected job for: {0}", getCacheKey()); 217 finishLoading(LoadResult.REJECTED); 218 } 219 } catch (RejectedExecutionException e) { 220 // queue was full, try again later 221 log.log(Level.FINE, "JCS - rejected job for: {0}", getCacheKey()); 222 finishLoading(LoadResult.REJECTED); 223 } 224 } 225 } 226 227 /** 228 * Guard method for execution. If guard returns true, the execution of download task will commence 229 * otherwise, execution will finish with result LoadResult.REJECTED 230 * 231 * It is responsibility of the overriding class, to handle properly situation in finishLoading class 232 * @return 233 */ 234 protected boolean executionGuard() { 235 return true; 192 getDownloadExecutor().execute(this); 193 } 236 194 } 237 195 … … 240 198 */ 241 199 protected void executionFinished() { 200 if (finishTask != null) { 201 finishTask.run(); 202 } 242 203 } 243 204 … … 269 230 * this needs to be non-static, so it can be overridden by subclasses 270 231 */ 271 protected Executor getDownloadExecutor() {232 protected ThreadPoolExecutor getDownloadExecutor() { 272 233 return downloadJobExecutor; 273 234 } … … 498 459 } 499 460 } 461 462 /** 463 * TODO: move to JobFactory 464 * cancels all outstanding tasks in the queue. 465 */ 466 public void cancelOutstandingTasks() { 467 ThreadPoolExecutor downloadExecutor = getDownloadExecutor(); 468 for(Runnable r: downloadExecutor.getQueue()) { 469 if (downloadExecutor.remove(r)) { 470 if (r instanceof JCSCachedTileLoaderJob) { 471 ((JCSCachedTileLoaderJob<?, ?>) r).handleJobCancellation(); 472 } 473 } 474 } 475 } 476 477 /** 478 * Sets a job, that will be run, when job will finish execution 479 * @param runnable that will be executed 480 */ 481 public void setFinishedTask(Runnable runnable) { 482 this.finishTask = runnable; 483 484 } 485 486 /** 487 * Marks this job as canceled 488 */ 489 public void handleJobCancellation() { 490 finishLoading(LoadResult.CANCELED); 491 } 500 492 } -
trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoader.java
r8397 r8403 16 16 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 17 17 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; 18 import org.openstreetmap.josm.data.cache.HostLimitQueue; 18 19 import org.openstreetmap.josm.data.cache.JCSCacheManager; 19 import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob .LIFOQueue;20 import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob; 20 21 import org.openstreetmap.josm.data.preferences.IntegerProperty; 21 22 … … 34 35 private TileLoaderListener listener; 35 36 private static final String PREFERENCE_PREFIX = "imagery.tms.cache."; 36 // average tile size is about 20kb 37 /** 38 * how many object on disk should be stored for TMS region. Average tile size is about 20kb 39 */ 37 40 public static final IntegerProperty MAX_OBJECTS_ON_DISK = new IntegerProperty(PREFERENCE_PREFIX + "max_objects_disk", 25000); // 25000 is around 500MB under this assumptions 38 41 … … 41 44 */ 42 45 public static final IntegerProperty THREAD_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobs", 25); 46 47 /** 48 * Limit definition for per host concurrent connections 49 */ 50 public static final IntegerProperty HOST_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobsperhost", 6); 51 43 52 44 53 /** … … 54 63 30, // keepalive for thread 55 64 TimeUnit.SECONDS, 56 // make queue of LIFO type - so recently requested tiles will be loaded first (assuming that these are which user is waiting to see) 57 new LIFOQueue() 58 /* keep the queue size fairly small, we do not want to 59 download a lot of tiles, that user is not seeing anyway */ 65 new HostLimitQueue(HOST_LIMIT.get().intValue()), 66 JCSCachedTileLoaderJob.getNamedThreadFactory("TMS downloader") 60 67 ); 61 68 } … … 123 130 124 131 /** 125 * Sets the download executor for this tile loader factory. Enables to use different queuing method126 * for this factory.127 * @param downloadExecutor128 */129 public void setDownloadExecutor(ThreadPoolExecutor downloadExecutor) {130 this.downloadExecutor = downloadExecutor;131 }132 133 /**134 * @return Executor that handles the jobs for this tile loader135 */136 public ThreadPoolExecutor getDownloadExecutor() {137 return downloadExecutor;138 }139 140 /**141 132 * cancels all outstanding tasks in the queue. This rollbacks the state of the tiles in the queue 142 133 * to loading = false / loaded = false 143 134 */ 144 135 public void cancelOutstandingTasks() { 145 for(Runnable elem: downloadExecutor.getQueue()) { 146 if (elem instanceof TMSCachedTileLoaderJob) { 147 TMSCachedTileLoaderJob loaderJob = (TMSCachedTileLoaderJob) elem; 148 if (downloadExecutor.remove(loaderJob)) { 149 Tile t = loaderJob.getTile(); 150 t.finishLoading(); 151 t.setLoaded(false); 152 } 136 for(Runnable r: downloadExecutor.getQueue()) { 137 if (downloadExecutor.remove(r) && r instanceof TMSCachedTileLoaderJob) { 138 ((TMSCachedTileLoaderJob)r).handleJobCancellation(); 153 139 } 154 140 } 155 141 } 156 157 142 } -
trunk/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
r8397 r8403 13 13 import java.util.concurrent.ConcurrentHashMap; 14 14 import java.util.concurrent.ConcurrentMap; 15 import java.util.concurrent.Executor; 16 import java.util.concurrent.Semaphore; 15 import java.util.concurrent.ThreadPoolExecutor; 17 16 import java.util.logging.Level; 18 17 import java.util.logging.Logger; … … 25 24 import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 26 25 import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTMSTileSource; 27 import org.openstreetmap.josm.Main;28 26 import org.openstreetmap.josm.data.cache.BufferedImageCacheEntry; 29 27 import org.openstreetmap.josm.data.cache.CacheEntry; … … 31 29 import org.openstreetmap.josm.data.cache.ICachedLoaderListener; 32 30 import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob; 33 import org.openstreetmap.josm.data.preferences.IntegerProperty;34 31 35 32 /** … … 49 46 50 47 /** 51 * Limit definition for per host concurrent connections52 */53 public static final IntegerProperty HOST_LIMIT = new IntegerProperty("imagery.tms.tmsloader.maxjobsperhost", 6);54 55 /*56 * Host limit guards the area - between submission to the queue up to loading is finished. It uses executionGuard method57 * from JCSCachedTileLoaderJob to acquire the semaphore, and releases it - when loadingFinished is called (but not when58 * LoadResult.GUARD_REJECTED is set)59 *60 */61 62 private Semaphore getSemaphore() {63 String host = getUrl().getHost();64 Semaphore limit = HOST_LIMITS.get(host);65 if (limit == null) {66 synchronized(HOST_LIMITS) {67 limit = HOST_LIMITS.get(host);68 if (limit == null) {69 limit = new Semaphore(HOST_LIMIT.get().intValue());70 HOST_LIMITS.put(host, limit);71 }72 }73 }74 return limit;75 }76 77 private boolean acquireSemaphore() {78 boolean ret = true;79 Semaphore limit = getSemaphore();80 if (limit != null) {81 ret = limit.tryAcquire();82 if (!ret) {83 Main.debug("rejecting job because of per host limit");84 }85 }86 return ret;87 }88 89 private void releaseSemaphore() {90 Semaphore limit = getSemaphore();91 if (limit != null) {92 limit.release();93 }94 }95 96 private static Map<String, Semaphore> HOST_LIMITS = new ConcurrentHashMap<>();97 98 /**99 * Reconfigures download dispatcher using current values of THREAD_LIMIT and HOST_LIMIT100 */101 public static final void reconfigureDownloadDispatcher() {102 HOST_LIMITS = new ConcurrentHashMap<>();103 }104 105 /**106 48 * Constructor for creating a job, to get a specific tile from cache 107 49 * @param listener … … 111 53 * @param readTimeout when connecting to remote resource 112 54 * @param headers to be sent together with request 55 * @param downloadExecutor that will be executing the jobs 113 56 */ 114 57 public TMSCachedTileLoaderJob(TileLoaderListener listener, Tile tile, 115 58 ICacheAccess<String, BufferedImageCacheEntry> cache, 116 59 int connectTimeout, int readTimeout, Map<String, String> headers, 117 Executor downloadExecutor) {60 ThreadPoolExecutor downloadExecutor) { 118 61 super(cache, connectTimeout, readTimeout, headers, downloadExecutor); 119 62 this.tile = tile; … … 207 150 } 208 151 209 @Override210 protected boolean executionGuard() {211 return acquireSemaphore();212 }213 214 @Override215 protected void executionFinished() {216 releaseSemaphore();217 }218 219 152 public void submit() { 220 153 tile.initLoading(); … … 234 167 tile.finishLoading(); // whatever happened set that loading has finished 235 168 switch(result){ 236 case FAILURE:237 tile.setError("Problem loading tile");238 // no break intentional here239 169 case SUCCESS: 240 170 handleNoTileAtZoom(); … … 249 179 tile.setError(tr("HTTP error {0} when loading tiles", httpStatusCode)); 250 180 } 181 break; 182 case FAILURE: 183 tile.setError("Problem loading tile"); 251 184 // no break intentional here 252 case REJECTED:185 case CANCELED: 253 186 // do nothing 254 187 } -
trunk/src/org/openstreetmap/josm/gui/layer/TMSLayer.java
r8401 r8403 22 22 import java.util.ArrayList; 23 23 import java.util.Collections; 24 import java.util.Comparator; 24 25 import java.util.HashMap; 25 26 import java.util.LinkedList; … … 1194 1195 } 1195 1196 1197 private Comparator<Tile> getTileDistanceComparator() { 1198 final int centerX = (int) Math.ceil((x0 + x1) / 2); 1199 final int centerY = (int) Math.ceil((y0 + y1) / 2); 1200 return new Comparator<Tile>() { 1201 private int getDistance(Tile t) { 1202 return Math.abs(t.getXtile() - centerX) + Math.abs(t.getYtile() - centerY); 1203 } 1204 @Override 1205 public int compare(Tile o1, Tile o2) { 1206 int distance1 = getDistance(o1); 1207 int distance2 = getDistance(o2); 1208 return Integer.compare(distance1, distance2); 1209 } 1210 }; 1211 } 1212 1196 1213 private void loadAllTiles(boolean force) { 1197 1214 if (!autoLoad && !force) 1198 1215 return; 1199 for (Tile t : this.allTilesCreate()) { 1216 List<Tile> allTiles = allTilesCreate(); 1217 Collections.sort(allTiles, getTileDistanceComparator()); 1218 for (Tile t : allTiles) { //, getTileDistanceComparator())) { 1200 1219 loadTile(t, false); 1201 1220 } -
trunk/src/org/openstreetmap/josm/gui/preferences/imagery/TMSSettingsPanel.java
r8307 r8403 45 45 maxElementsOnDisk = new JSpinner(new SpinnerNumberModel(TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.get().intValue(), 0, Integer.MAX_VALUE, 1)); 46 46 maxConcurrentDownloads = new JSpinner(new SpinnerNumberModel(TMSCachedTileLoaderJob.THREAD_LIMIT.get().intValue(), 0, Integer.MAX_VALUE, 1)); 47 maxDownloadsPerHost = new JSpinner(new SpinnerNumberModel(TMSCachedTileLoader Job.HOST_LIMIT.get().intValue(), 0, Integer.MAX_VALUE, 1));47 maxDownloadsPerHost = new JSpinner(new SpinnerNumberModel(TMSCachedTileLoader.HOST_LIMIT.get().intValue(), 0, Integer.MAX_VALUE, 1)); 48 48 49 49 add(new JLabel(tr("Auto zoom by default: ")), GBC.std()); … … 98 98 this.maxElementsOnDisk.setValue(TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.get()); 99 99 this.maxConcurrentDownloads.setValue(TMSCachedTileLoaderJob.THREAD_LIMIT.get()); 100 this.maxDownloadsPerHost.setValue(TMSCachedTileLoader Job.HOST_LIMIT.get());100 this.maxDownloadsPerHost.setValue(TMSCachedTileLoader.HOST_LIMIT.get()); 101 101 } 102 102 … … 117 117 TMSLayer.setMinZoomLvl((Integer)this.minZoomLvl.getValue()); 118 118 119 TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.put((Integer) this.maxElementsOnDisk.getValue()); 119 if (!TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.get().equals(this.maxElementsOnDisk.getValue())) { 120 TMSCachedTileLoader.MAX_OBJECTS_ON_DISK.put((Integer) this.maxElementsOnDisk.getValue()); 121 restartRequired = true; 122 } 120 123 121 TMSCachedTileLoaderJob.THREAD_LIMIT.put((Integer) this.maxConcurrentDownloads.getValue()); 122 TMSCachedTileLoaderJob.HOST_LIMIT.put((Integer) this.maxDownloadsPerHost.getValue()); 123 TMSCachedTileLoaderJob.reconfigureDownloadDispatcher(); 124 if(!TMSCachedTileLoader.THREAD_LIMIT.get().equals(this.maxConcurrentDownloads.getValue())) { 125 TMSCachedTileLoader.THREAD_LIMIT.put((Integer) this.maxConcurrentDownloads.getValue()); 126 restartRequired = true; 127 } 128 129 if(!TMSCachedTileLoader.HOST_LIMIT.get().equals(this.maxDownloadsPerHost.getValue())) { 130 TMSCachedTileLoader.HOST_LIMIT.put((Integer) this.maxDownloadsPerHost.getValue()); 131 restartRequired = true; 132 } 124 133 125 134 if (!TMSLayer.PROP_TILECACHE_DIR.get().equals(this.tilecacheDir.getText())) {
Note:
See TracChangeset
for help on using the changeset viewer.