Merge branch 'wip_sync'

This commit is contained in:
Hannes Janetzek 2014-01-19 22:53:48 +01:00
commit 2c7cd1c880
14 changed files with 458 additions and 423 deletions

View File

@ -23,16 +23,10 @@ import android.widget.RelativeLayout.LayoutParams;
public class AndroidMap extends Map {
private final MapView mMapView;
private boolean mWaitRedraw;
final GLView mGLView;
boolean mPausing = false;
private final Runnable mRedrawRequest = new Runnable() {
@Override
public void run() {
redrawMapInternal(false);
}
};
private volatile boolean mWaitRedraw;
private volatile boolean mPausing;
public AndroidMap(MapView mapView) {
super();
@ -45,8 +39,6 @@ public class AndroidMap extends Map {
android.view.ViewGroup.LayoutParams.MATCH_PARENT);
mapView.addView(mGLView, params);
//mGestureDetector =
}
@Override
@ -60,9 +52,9 @@ public class AndroidMap extends Map {
}
@Override
public synchronized void updateMap(boolean requestRender) {
if (requestRender && !mClearMap && !mPausing) // && mInitialized)
mGLView.requestRender();
public void updateMap(boolean redraw) {
//if (redraw && !mClearMap && !mPausing)
// mGLView.requestRender();
if (!mWaitRedraw) {
mWaitRedraw = true;
@ -71,26 +63,29 @@ public class AndroidMap extends Map {
}
@Override
public synchronized void render() {
public void render() {
if (mPausing)
return;
if (mClearMap)
updateMap(false);
else
mGLView.requestRender();
}
synchronized void redrawMapInternal(boolean forceRedraw) {
boolean clear = mClearMap;
mWaitRedraw = false;
private final Runnable mRedrawRequest = new Runnable() {
@Override
public void run() {
redrawMapInternal();
}
};
if (forceRedraw && !clear)
mGLView.requestRender();
void redrawMapInternal() {
mWaitRedraw = false;
updateLayers();
if (clear) {
mGLView.requestRender();
mClearMap = false;
}
}
@Override
@ -103,9 +98,7 @@ public class AndroidMap extends Map {
return mMapView.postDelayed(action, delay);
}
public synchronized void pause(boolean pause) {
public void pause(boolean pause) {
mPausing = pause;
}
}

View File

@ -24,9 +24,12 @@ import org.oscim.map.Map;
import org.oscim.tiling.TileLoader;
import org.oscim.tiling.TileManager;
import org.oscim.tiling.TileRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class TileLayer<T extends TileLoader> extends Layer implements Map.UpdateListener {
//static final Logger log = LoggerFactory.getLogger(TileLayer.class);
static final Logger log = LoggerFactory.getLogger(TileLayer.class);
private final static int MAX_ZOOMLEVEL = 17;
private final static int MIN_ZOOMLEVEL = 2;
private final static int CACHE_LIMIT = 250;
@ -37,8 +40,6 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
protected final int mNumTileLoader = 4;
protected final ArrayList<T> mTileLoader;
protected boolean mInitial = true;
public TileLayer(Map map) {
this(map, MIN_ZOOMLEVEL, MAX_ZOOMLEVEL, CACHE_LIMIT);
}
@ -71,10 +72,13 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
@Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear || mInitial) {
if (clear) {
// sync with TileRenderer
synchronized (mRenderLayer) {
mRenderLayer.clearTiles();
mTileManager.init(mInitial);
mInitial = false;
mTileManager.init();
}
changed = true;
}
@ -87,16 +91,13 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
for (T loader : mTileLoader) {
loader.pause();
loader.interrupt();
loader.cleanup();
//try {
// tileWorker.join(10000);
//} catch (InterruptedException e) {
// // restore the interrupted status
// Thread.currentThread().interrupt();
//}
try {
loader.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
loader.cleanup();
}
mTileManager.destroy();
}
void notifyLoaders() {
@ -110,13 +111,14 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements M
if (!loader.isPausing())
loader.pause();
}
if (wait) {
if (!wait)
return;
for (T loader : mTileLoader) {
if (!loader.isPausing())
loader.awaitPausing();
}
}
}
protected void resumeLoaders() {
for (T loader : mTileLoader)

View File

@ -16,6 +16,8 @@
*/
package org.oscim.layers.tile.vector;
import java.util.concurrent.CancellationException;
import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
@ -131,14 +133,18 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
mTile = tile;
mTile.layers = new ElementLayers();
QueryResult result = null;
try {
// query database, which calls 'process' callback
QueryResult result = mTileDataSource.executeQuery(mTile, this);
result = mTileDataSource.executeQuery(mTile, this);
} catch (CancellationException e) {
log.debug("canceled {}", mTile);
} catch (Exception e) {
log.debug("{}", e);
} finally {
mTile = null;
clearState();
}
return (result == QueryResult.SUCCESS);
}
@ -205,8 +211,12 @@ public class VectorTileLoader extends TileLoader implements IRenderTheme.Callbac
@Override
public void process(MapElement element) {
clearState();
if (isCanceled())
throw new CancellationException();
mElement = element;
if (element.type == GeometryType.POINT) {

View File

@ -28,7 +28,7 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi
static final Logger log = LoggerFactory.getLogger(LabelLayer.class);
private final TextRenderer mTextRenderer;
private int multi;
//private int multi;
public LabelLayer(Map map, TileRenderer tileRenderLayer) {
super(map);
@ -49,19 +49,19 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi
@Override
public void onMotionEvent(MotionEvent e) {
int action = e.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_POINTER_DOWN) {
multi++;
mTextRenderer.hold(true);
} else if (action == MotionEvent.ACTION_POINTER_UP) {
multi--;
if (multi == 0)
mTextRenderer.hold(false);
} else if (action == MotionEvent.ACTION_CANCEL) {
multi = 0;
log.debug("cancel " + multi);
mTextRenderer.hold(false);
}
// int action = e.getAction() & MotionEvent.ACTION_MASK;
// if (action == MotionEvent.ACTION_POINTER_DOWN) {
// multi++;
// mTextRenderer.hold(true);
// } else if (action == MotionEvent.ACTION_POINTER_UP) {
// multi--;
// if (multi == 0)
// mTextRenderer.hold(false);
// } else if (action == MotionEvent.ACTION_CANCEL) {
// multi = 0;
// log.debug("cancel " + multi);
// mTextRenderer.hold(false);
// }
}
@Override

View File

@ -51,6 +51,7 @@ import org.oscim.tiling.TileRenderer;
import org.oscim.tiling.TileSet;
import org.oscim.utils.FastMath;
import org.oscim.utils.OBB2D;
import org.oscim.utils.async.ContinuousTask;
import org.oscim.utils.pool.Pool;
class TextRenderer extends ElementRenderer {
@ -143,6 +144,8 @@ class TextRenderer extends ElementRenderer {
//mActiveTiles = new HashMap<MapTile, LabelTile>();
mRelabelCnt = 0;
mLabelTask = new LabelTask(map);
}
// remove Label l from mLabels and return l.next
@ -274,6 +277,108 @@ class TextRenderer extends ElementRenderer {
return dx;
}
private Label updateWayLabels(MapTile t, Label l, float dx, float dy, double scale,
ElementLayers dbg) {
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
// check if path at current scale is long enough for text
if (dbg == null && ti.width > ti.length * scale)
continue;
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
// set line endpoints relative to view to be able to
// check intersections with label from other tiles
float w = (ti.x2 - ti.x1) / 2f;
float h = (ti.y2 - ti.y1) / 2f;
l.bbox = null;
l.x1 = l.x - w;
l.y1 = l.y - h;
l.x2 = l.x + w;
l.y2 = l.y + h;
if (!wayIsVisible(l))
continue;
byte overlaps = -1;
if (l.bbox == null)
l.bbox = new OBB2D(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
else
l.bbox.set(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
if (dbg == null || ti.width < ti.length * scale)
overlaps = checkOverlap(l);
if (dbg != null)
Debug.addDebugBox(dbg, l, ti, overlaps, false, (float) scale);
if (overlaps == 0) {
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
}
return l;
}
private Label updateNodeLabels(MapTile t, Label l, float dx, float dy, double scale, float cos,
float sin) {
O: for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (!ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
if (!nodeIsVisible(l))
continue;
if (l.bbox == null)
l.bbox = new OBB2D();
l.bbox.setNormalized(l.x, l.y, cos, -sin,
l.width + MIN_CAPTION_DIST,
l.text.fontHeight + MIN_CAPTION_DIST,
l.text.dy);
for (Label lp = mLabels; lp != null;) {
if (l.bbox.overlaps(lp.bbox)) {
if (l.text.priority < lp.text.priority) {
lp = removeLabel(lp);
continue;
}
continue O;
}
lp = (Label) lp.next;
}
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
return l;
}
boolean updateLabels() {
// nextLayer is not loaded yet
if (mNextLayer.ready)
@ -400,6 +505,7 @@ class TextRenderer extends ElementRenderer {
/* add way labels */
for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i];
synchronized (t) {
if (!t.state(MapTile.STATE_READY))
continue;
@ -407,65 +513,14 @@ class TextRenderer extends ElementRenderer {
float dy = (float) (t.tileY * Tile.SIZE - tileY);
dx = flipLongitude(dx, maxx);
for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
// check if path at current scale is long enough for text
if (dbg == null && ti.width > ti.length * scale)
continue;
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
// set line endpoints relative to view to be able to
// check intersections with label from other tiles
float w = (ti.x2 - ti.x1) / 2f;
float h = (ti.y2 - ti.y1) / 2f;
l.bbox = null;
l.x1 = l.x - w;
l.y1 = l.y - h;
l.x2 = l.x + w;
l.y2 = l.y + h;
if (!wayIsVisible(l))
continue;
byte overlaps = -1;
if (l.bbox == null)
l.bbox = new OBB2D(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
else
l.bbox.set(l.x, l.y, l.x1, l.y1,
l.width + MIN_WAY_DIST,
l.text.fontHeight + MIN_WAY_DIST);
if (dbg == null || ti.width < ti.length * scale)
overlaps = checkOverlap(l);
if (dbg != null)
Debug.addDebugBox(dbg, l, ti, overlaps, false, (float) scale);
if (overlaps == 0) {
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
}
l = updateWayLabels(t, l, dx, dy, scale, dbg);
}
}
/* add caption */
for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i];
synchronized (t) {
if (!t.state(MapTile.STATE_READY))
continue;
@ -473,45 +528,7 @@ class TextRenderer extends ElementRenderer {
float dy = (float) (t.tileY * Tile.SIZE - tileY);
dx = flipLongitude(dx, maxx);
O: for (TextItem ti = t.labels; ti != null; ti = ti.next) {
if (!ti.text.caption)
continue;
// acquire a TextItem to add to TextLayer
if (l == null)
l = getLabel();
l.clone(ti);
l.move(ti, dx, dy, (float) scale);
if (!nodeIsVisible(l))
continue;
//l.setAxisAlignedBBox();
if (l.bbox == null)
l.bbox = new OBB2D();
l.bbox.setNormalized(l.x, l.y, cos, -sin,
l.width + MIN_CAPTION_DIST,
l.text.fontHeight + MIN_CAPTION_DIST,
l.text.dy);
for (Label lp = mLabels; lp != null;) {
if (l.bbox.overlaps(lp.bbox)) {
if (l.text.priority < lp.text.priority) {
lp = removeLabel(lp);
continue;
}
continue O;
}
lp = (Label) lp.next;
}
addLabel(l);
l.item = TextItem.copy(ti);
l.tile = t;
l.active = mRelabelCnt;
l = null;
l = updateNodeLabels(t, l, dx, dy, scale, cos, sin);
}
}
@ -541,6 +558,7 @@ class TextRenderer extends ElementRenderer {
for (int i = 0, n = mTileSet.cnt; i < n; i++) {
MapTile t = tiles[i];
synchronized (t) {
if (!t.state(MapTile.STATE_READY))
continue;
@ -562,6 +580,7 @@ class TextRenderer extends ElementRenderer {
sl.addSymbol(s);
}
}
}
// temporary used Label
l = (Label) mPool.release(l);
@ -588,11 +607,6 @@ class TextRenderer extends ElementRenderer {
public synchronized void update(MapPosition pos, boolean changed,
Matrices matrices) {
//if (System.currentTimeMillis() - lastDraw > 1000){
// updateLabels();
// lastDraw = System.currentTimeMillis();
//
//}
if (mNextLayer.ready) {
// exchange current with next layers
TextureLayers tmp = mCurLayer;
@ -615,92 +629,19 @@ class TextRenderer extends ElementRenderer {
compile();
}
if (mRequestClear)
cleanup();
//if (!mHolding)
postLabelTask();
}
/* private */LabelTask mLabelTask;
/* private */long mLastRun;
/* private */boolean mRequestRun;
/* private */boolean mRequestClear;
/* private */boolean mRelabel;
class LabelTask implements Runnable {
private boolean isCancelled;
@Override
public void run() {
boolean labelsChanged = false;
if (isCancelled) {
return;
}
long now = System.currentTimeMillis();
//log.debug("relabel after " + (now - mLastRun));
mLastRun = now;
labelsChanged = updateLabels();
if (!isCancelled && labelsChanged)
mMap.render();
mLabelTask = null;
mRequestRun = false;
isCancelled = false;
if (mRelabel) {
mRelabel = false;
postLabelTask();
}
}
public void cancel() {
isCancelled = true;
}
}
/* private */void cleanup() {
mLabels = (Label) mPool.releaseAll(mLabels);
mTileSet.releaseTiles();
mLabelTask = null;
mRequestClear = false;
}
private final Runnable mLabelUpdate = new Runnable() {
@Override
public void run() {
if (mLabelTask == null) {
mLabelTask = new LabelTask();
mMap.addTask(mLabelTask);
}
}
};
/* private */void postLabelTask() {
synchronized (mLabelUpdate) {
if (mRequestRun) {
mRelabel = true;
} else {
mRequestRun = true;
long delay = (mLastRun + MAX_RELABEL_DELAY) - System.currentTimeMillis();
//log.debug("relabel in: " + delay);
mMap.postDelayed(mLabelUpdate, Math.max(delay, 0));
}
}
mLabelTask.submit((mLastRun + MAX_RELABEL_DELAY) - System.currentTimeMillis());
}
@Override
public synchronized void render(MapPosition pos, Matrices m) {
layers.vbo.bind();
GLState.test(false, false);
float scale = (float) (mMapPosition.scale / pos.scale);
setMatrix(pos, m, true);
synchronized (layers) {
layers.vbo.bind();
if (layers.baseLayers != null) {
for (RenderElement l = layers.baseLayers; l != null;) {
if (l.type == RenderElement.POLYGON) {
@ -717,25 +658,39 @@ class TextRenderer extends ElementRenderer {
for (RenderElement l = layers.textureLayers; l != null;)
l = TextureLayer.Renderer.draw(l, scale, m);
}
//private boolean mHolding;
/**
* @param enable layer updates
*/
public synchronized void hold(boolean enable) {
// mHolding = enable;
// if (!enable)
// runLabelTask();
}
public synchronized void clearLabels() {
if (mRequestRun) {
mRequestClear = true;
//mRelabel = true;
} else {
cleanup();
//postLabelTask();
final class LabelTask extends ContinuousTask {
public LabelTask(Map map) {
super(map, 10);
}
@Override
public void doWork() {
if (updateLabels())
mMap.render();
mLastRun = System.currentTimeMillis();
}
@Override
public void cleanup() {
clearLabelsInternal();
}
}
private final LabelTask mLabelTask;
/* private */long mLastRun;
/* private */void clearLabelsInternal() {
mLabels = (Label) mPool.releaseAll(mLabels);
mTileSet.releaseTiles();
}
public void clearLabels() {
mLabelTask.cancel();
}
}

View File

@ -90,10 +90,6 @@ public class MapRenderer {
private static volatile boolean mUpdateColor = false;
// drawlock to synchronize Main- and GL-Thread
// static ReentrantLock tilelock = new ReentrantLock();
public static Object drawlock = new Object();
public static long frametime;
// Do not use the same buffer to upload data within a frame twice
@ -210,13 +206,8 @@ public class MapRenderer {
}
public void onDrawFrame() {
// prevent main thread recreating all tiles (updateMap)
// while rendering is going on.
synchronized (drawlock) {
frametime = System.currentTimeMillis();
draw();
}
mBufferPool.releaseBuffers();
}

View File

@ -61,6 +61,8 @@ public class MapTile extends Tile {
*/
public final static byte STATE_ERROR = 1 << 3;
public final static byte STATE_CANCEL = 1 << 4;
/**
* absolute tile coordinates: tileX,Y / Math.pow(2, zoomLevel)
*/
@ -89,7 +91,7 @@ public class MapTile extends Tile {
}
/**
* Tile is in view region. Set by GLRenderer.
* Tile is in view region. Set by TileRenderer.
*/
public boolean isVisible;
@ -100,7 +102,6 @@ public class MapTile extends Tile {
/**
* to avoid drawing a tile twice per frame
* FIXME what if multiple layers use the same tile?
*/
int lastDraw = 0;

View File

@ -42,6 +42,7 @@ public abstract class TileLoader extends PausableThread {
@Override
protected void doWork() {
MapTile tile = mTileManager.getTileJob();
if (tile == null)
@ -53,7 +54,7 @@ public abstract class TileLoader extends PausableThread {
success = executeJob(tile);
} catch (Exception e) {
e.printStackTrace();
return;
success = false;
}
if (isInterrupted())

View File

@ -17,9 +17,9 @@
package org.oscim.tiling;
import static org.oscim.tiling.MapTile.STATE_CANCEL;
import static org.oscim.tiling.MapTile.STATE_LOADING;
import static org.oscim.tiling.MapTile.STATE_NEW_DATA;
import static org.oscim.tiling.MapTile.STATE_NONE;
import java.util.ArrayList;
import java.util.Arrays;
@ -29,7 +29,6 @@ import org.oscim.core.Tile;
import org.oscim.map.Map;
import org.oscim.map.Viewport;
import org.oscim.renderer.BufferObject;
import org.oscim.renderer.MapRenderer;
import org.oscim.utils.FastMath;
import org.oscim.utils.ScanBox;
import org.oscim.utils.quadtree.QuadTree;
@ -132,11 +131,6 @@ public class TileManager {
mUpdateSerial = 0;
}
public void destroy() {
// there might be some leaks in here
// ... free static pools
}
private int[] mZoomTable;
public void setZoomTable(int[] zoomLevel) {
@ -144,24 +138,10 @@ public class TileManager {
}
public void init(boolean first) {
// sync with GLRender thread
// ... and labeling thread?
synchronized (MapRenderer.drawlock) {
if (!first) {
public void init() {
// pass VBOs and VertexItems back to pools
for (int i = 0; i < mTilesSize; i++)
clearTile(mTiles[i]);
}
// FIXME any of this still needed?
// mInitialized is set when surface changed
// and VBOs might be lost
// VertexPool.init();
// clear cache index
// QuadTree.init();
// clear references to cached MapTiles
Arrays.fill(mTiles, null);
@ -175,9 +155,6 @@ public class TileManager {
mNewTiles = new TileSet(numTiles);
mCurrentTiles = new TileSet(numTiles);
log.debug("max tiles: " + numTiles);
}
}
/**
@ -187,7 +164,7 @@ public class TileManager {
* @param pos
* current MapPosition
*/
public synchronized boolean update(MapPosition pos) {
public boolean update(MapPosition pos) {
// clear JobQueue and set tiles to state == NONE.
// one could also append new tiles and sort in JobQueue
// but this has the nice side-effect that MapWorkers dont
@ -405,13 +382,14 @@ public class TileManager {
if (t == null)
return;
synchronized (t) {
// still belongs to TileLoader thread
if (t.state != STATE_LOADING)
t.clear();
t.state = STATE_CANCEL;
mIndex.remove(t);
// QuadTree.remove(t);
t.state = STATE_NONE;
}
mTilesCount--;
}
@ -549,8 +527,7 @@ public class TileManager {
* @return caller does not care
*/
public void jobCompleted(MapTile tile, boolean success) {
if (!success) {
if (!success || tile.state == STATE_CANCEL) {
tile.clear();
return;
}

View File

@ -64,8 +64,11 @@ public class TileRenderer extends LayerRenderer {
mAlpha = alpha;
}
/**
* synced with clearTiles
*/
@Override
protected void update(MapPosition pos, boolean positionChanged, Matrices m) {
protected synchronized void update(MapPosition pos, boolean positionChanged, Matrices m) {
if (mAlpha == 0) {
mTileManager.releaseTiles(mDrawTiles);
@ -106,10 +109,8 @@ public class TileRenderer extends LayerRenderer {
public void clearTiles() {
// Clear all references to MapTiles as all current
// tiles will also be removed from TileManager.
synchronized (MapRenderer.drawlock) {
mDrawTiles = new TileSet();
}
}
/** compile tile layer data and upload to VBOs */
private static int compileTileLayers(MapTile[] tiles, int tileCnt) {

View File

@ -56,6 +56,7 @@ public class LwHttp {
private Socket mSocket;
private OutputStream mCommandStream;
private InputStream mResponseStream;
private OutputStream mCacheOutputStream;
private long mLastRequest = 0;
private SocketAddress mSockAddr;
@ -106,40 +107,34 @@ public class LwHttp {
}
static class Buffer extends BufferedInputStream {
final OutputStream mCache;
final OutputStream mCacheOutputstream;
public Buffer(InputStream is, OutputStream cache) {
super(is, 4096);
mCache = cache;
mCacheOutputstream = cache;
}
@Override
public synchronized int read() throws IOException {
public int read() throws IOException {
int data = super.read();
if (data >= 0)
mCache.write(data);
mCacheOutputstream.write(data);
return data;
}
@Override
public synchronized int read(byte[] buffer, int offset, int byteCount)
public int read(byte[] buffer, int offset, int byteCount)
throws IOException {
int len = super.read(buffer, offset, byteCount);
if (len >= 0)
mCache.write(buffer, offset, len);
mCacheOutputstream.write(buffer, offset, len);
return len;
}
}
OutputStream mCacheOutputStream;
public void setOutputStream(OutputStream outputStream) {
mCacheOutputStream = outputStream;
}
public void close() {
if (mSocket != null) {
try {
@ -177,6 +172,10 @@ public class LwHttp {
while (end < read && (buf[end] != '\n'))
end++;
if (end == BUFFER_SIZE) {
return null;
}
if (buf[end] != '\n')
continue;
@ -209,7 +208,7 @@ public class LwHttp {
if (!ok) {
String line = new String(buf, pos, end - pos - 1);
log.debug(">" + line + "< ");
log.debug("> {} <", line);
}
pos += (end - pos) + 1;
@ -255,12 +254,14 @@ public class LwHttp {
mMaxReq = RESPONSE_EXPECTED_LIVES;
// log.debug("create connection");
} else {
// FIXME not sure if this is correct way to drain socket
int avail = mResponseStream.available();
if (avail > 0) {
log.debug("Consume left-over bytes: " + avail);
while ((avail = mResponseStream.available()) > 0)
mResponseStream.read(buffer);
log.debug("left-over bytes: " + avail);
close();
lwHttpConnect();
// FIXME not sure if this is correct way to drain socket
//while ((avail = mResponseStream.available()) > 0)
// mResponseStream.read(buffer);
}
}
@ -353,8 +354,16 @@ public class LwHttp {
return true;
}
public void requestCompleted() {
public void setOutputStream(OutputStream outputStream) {
mCacheOutputStream = outputStream;
}
public void requestCompleted(boolean keepConnection) {
mLastRequest = System.nanoTime();
mCacheOutputStream = null;
if (!keepConnection)
close();
}
public int getContentLength() {

View File

@ -30,10 +30,6 @@ import org.oscim.utils.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*
*/
public abstract class PbfTileDataSource implements ITileDataSource {
static final Logger log = LoggerFactory.getLogger(PbfTileDataSource.class);
@ -48,66 +44,55 @@ public abstract class PbfTileDataSource implements ITileDataSource {
@Override
public QueryResult executeQuery(MapTile tile, ITileDataSink sink) {
boolean success = true;
ITileCache.TileWriter cacheWriter = null;
if (mTileCache != null) {
ITileCache.TileReader c = mTileCache.getTile(tile);
if (c == null) {
// create new cache entry
cacheWriter = mTileCache.writeTile(tile);
mConn.setOutputStream(cacheWriter.getOutputStream());
} else {
try {
InputStream is = c.getInputStream();
try {
if (mTileDecoder.decode(tile, sink, is, c.getBytes())) {
IOUtils.closeQuietly(is);
return QueryResult.SUCCESS;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(is);
}
log.debug(tile + " Cache read failed");
}
}
boolean success = false;
try {
if (cacheWriter != null)
mConn.setOutputStream(cacheWriter.getOutputStream());
InputStream is;
if (!mConn.sendRequest(tile)) {
log.debug(tile + " Request failed");
success = false;
} else if ((is = mConn.readHeader()) != null) {
log.debug("{} Request failed", tile);
} else if ((is = mConn.readHeader()) == null) {
log.debug("{} Network Error", tile);
} else {
int bytes = mConn.getContentLength();
success = mTileDecoder.decode(tile, sink, is, bytes);
if (!success)
log.debug(tile + " Decoding failed");
} else {
log.debug(tile + " Network Error");
success = false;
}
} catch (SocketException e) {
log.debug(tile + " Socket exception: " + e.getMessage());
success = false;
log.debug("{} Socket exception: {}", tile, e.getMessage());
} catch (SocketTimeoutException e) {
log.debug(tile + " Socket Timeout");
success = false;
log.debug("{} Socket Timeout", tile);
} catch (UnknownHostException e) {
log.debug(tile + " No Network");
success = false;
} catch (Exception e) {
log.debug("{} No Network", tile);
} catch (IOException e) {
e.printStackTrace();
success = false;
}
mConn.requestCompleted();
} finally {
mConn.requestCompleted(success);
if (cacheWriter != null)
cacheWriter.complete(success);
if (success)
mConn.close();
}
return success ? QueryResult.SUCCESS : QueryResult.FAILED;
}

View File

@ -17,12 +17,18 @@
*/
package org.oscim.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An abstract base class for threads which support pausing and resuming.
*/
public abstract class PausableThread extends Thread {
private final static Logger log = LoggerFactory.getLogger(PausableThread.class);
private final static boolean DEBUG = false;
private boolean mPausing = true;
private boolean mShouldPause;
private boolean mShouldPause = false;
/**
* Causes the current thread to wait until this thread is pausing.
@ -30,8 +36,10 @@ public abstract class PausableThread extends Thread {
public final void awaitPausing() {
synchronized (this) {
while (!isInterrupted() && !isPausing()) {
if (DEBUG)
log.debug("await {}", getThreadName());
try {
wait(100);
wait(10);
} catch (InterruptedException e) {
// restore the interrupted status
Thread.currentThread().interrupt();
@ -42,17 +50,13 @@ public abstract class PausableThread extends Thread {
@Override
public void interrupt() {
if (DEBUG)
log.debug("interrupt {}", getThreadName());
// first acquire the monitor which is used to call wait()
synchronized (this) {
super.interrupt();
}
//try {
// this.join(10000);
//} catch (InterruptedException e) {
// // restore the interrupted status
// Thread.currentThread().interrupt();
//}
}
/**
@ -72,14 +76,16 @@ public abstract class PausableThread extends Thread {
}
}
public final synchronized boolean isCanceled() {
return mShouldPause;
}
/**
* The paused thread should continue with its work.
*/
public final synchronized void proceed() {
if (mShouldPause) {
mShouldPause = false;
mPausing = false;
afterPause();
notify();
}
}
@ -102,6 +108,14 @@ public abstract class PausableThread extends Thread {
interrupt();
}
}
if (mPausing) {
mPausing = false;
afterPause();
}
if (DEBUG)
log.debug("resume {}", getThreadName());
}
if (isInterrupted()) {
@ -116,6 +130,11 @@ public abstract class PausableThread extends Thread {
}
}
if (DEBUG)
log.debug("finish {}", getThreadName());
mPausing = true;
afterRun();
}

View File

@ -0,0 +1,91 @@
package org.oscim.utils.async;
import org.oscim.map.Map;
public abstract class ContinuousTask implements Runnable {
private final Map mMap;
protected boolean mRunning;
protected boolean mWait;
protected boolean mCancel;
protected boolean mDelayed;
protected long mMinDelay;
public ContinuousTask(Map map, long minDelay) {
mMap = map;
mMinDelay = minDelay;
}
@Override
public void run() {
synchronized (this) {
//System.out.println("run " + mRunning + " "
// + mCancel + " " + mDelayed + " " + mWait);
if (mCancel) {
mCancel = false;
mRunning = false;
mDelayed = false;
mWait = false;
cleanup();
return;
}
if (mDelayed) {
// entered on main-loop
mDelayed = false;
mWait = false;
// unset running temporarily
mRunning = false;
submit(0);
return;
}
}
doWork();
synchronized (this) {
mRunning = false;
if (mCancel)
cleanup();
else if (mWait)
submit(mMinDelay);
mCancel = false;
mWait = false;
}
}
public abstract void doWork();
public abstract void cleanup();
public synchronized void submit(long delay) {
//System.out.println("submit " + mRunning + " " + mCancel + " " + delay);
if (mRunning) {
mWait = true;
return;
}
mRunning = true;
if (delay <= 0) {
mMap.addTask(this);
return;
}
mDelayed = true;
mMap.postDelayed(this, delay);
}
public synchronized void cancel() {
if (mRunning) {
mCancel = true;
return;
}
cleanup();
}
}