From 5b16f6b085998ca41e484161eb9f08da2742805d Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Fri, 26 Jul 2013 23:35:05 +0200 Subject: [PATCH] borrow AsyncTask stuff from libgdx - for html backend async tasks are just put on Queue and run on next main-loop iteration --- .../src/org/oscim/android/AndroidMapView.java | 15 ++- .../org/oscim/utils/async/AsyncExecutor.java | 60 ++++++++++++ .../org/oscim/utils/async/AsyncResult.java | 49 ++++++++++ .../emu/org/oscim/utils/async/AsyncTask.java | 28 ++++++ .../org/oscim/utils/async/ThreadUtils.java | 27 ++++++ vtm-gdx/src/org/oscim/gdx/GdxMap.java | 15 ++- .../org/oscim/utils/async/AsyncExecutor.java | 94 +++++++++++++++++++ .../org/oscim/utils/async/AsyncResult.java | 46 +++++++++ vtm/src/org/oscim/utils/async/AsyncTask.java | 27 ++++++ .../org/oscim/utils/async/ThreadUtils.java | 27 ++++++ vtm/src/org/oscim/view/MapView.java | 60 +++++++----- 11 files changed, 422 insertions(+), 26 deletions(-) create mode 100644 vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncExecutor.java create mode 100644 vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncResult.java create mode 100644 vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncTask.java create mode 100644 vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/ThreadUtils.java create mode 100644 vtm/src/org/oscim/utils/async/AsyncExecutor.java create mode 100644 vtm/src/org/oscim/utils/async/AsyncResult.java create mode 100644 vtm/src/org/oscim/utils/async/AsyncTask.java create mode 100644 vtm/src/org/oscim/utils/async/ThreadUtils.java diff --git a/vtm-android/src/org/oscim/android/AndroidMapView.java b/vtm-android/src/org/oscim/android/AndroidMapView.java index 34dfb31d..e2032de3 100644 --- a/vtm-android/src/org/oscim/android/AndroidMapView.java +++ b/vtm-android/src/org/oscim/android/AndroidMapView.java @@ -28,6 +28,7 @@ import org.oscim.view.MapView; import android.content.Context; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.view.View; import android.widget.RelativeLayout; /** @@ -138,7 +139,7 @@ public class AndroidMapView extends RelativeLayout { if (!mWaitRedraw) { mWaitRedraw = true; - post(mRedrawRequest); + getView().post(mRedrawRequest); } } @@ -165,8 +166,13 @@ public class AndroidMapView extends RelativeLayout { } @Override - public boolean postRunnable(Runnable runnable) { - return post(runnable); + public boolean post(Runnable runnable) { + return getView().post(runnable); + } + + @Override + public boolean postDelayed(Runnable action, long delay) { + return getView().postDelayed(action, delay); } }; @@ -185,6 +191,9 @@ public class AndroidMapView extends RelativeLayout { mMapView.updateMap(false); } + View getView(){ + return this; + } public MapView getMap() { return mMapView; diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncExecutor.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncExecutor.java new file mode 100644 index 00000000..8865a17c --- /dev/null +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncExecutor.java @@ -0,0 +1,60 @@ +package org.oscim.utils.async; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.Disposable; +import com.badlogic.gdx.utils.Timer; + +/** + * GWT emulation of AsynchExecutor, will call tasks immediately :D + * @author badlogic + * + */ +public class AsyncExecutor implements Disposable { + + /** + * Creates a new AsynchExecutor that allows maxConcurrent + * {@link Runnable} instances to run in parallel. + * @param maxConcurrent + */ + public AsyncExecutor(int maxConcurrent) { + } + + // FIXME TODO add wrap into 'FakeFuture' and run via Gdx.app.post() + /** + * Submits a {@link Runnable} to be executed asynchronously. If + * maxConcurrent runnables are already running, the runnable + * will be queued. + * @param task the task to execute asynchronously + */ + @SuppressWarnings("rawtypes") + public AsyncResult submit(final AsyncTask task) { + + T result = null; + boolean error = false; + try { + task.run(); + result = task.getResult(); + } catch(Throwable t) { + error = true; + } + return new AsyncResult(result); + } + + /** + * Submits a {@link Runnable} to be executed asynchronously. If + * maxConcurrent runnables are already running, the runnable + * will be queued. + * @param task the task to execute asynchronously + */ + public void post(Runnable task) { + Gdx.app.postRunnable(task); + } + /** + * Waits for running {@link AsyncTask} instances to finish, + * then destroys any resources like threads. Can not be used + * after this method is called. + */ + @Override + public void dispose () { + } +} diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncResult.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncResult.java new file mode 100644 index 00000000..cec51131 --- /dev/null +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncResult.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.oscim.utils.async; + +//import java.util.concurrent.ExecutionException; +//import java.util.concurrent.Future; + + +/** + * Returned by {@link AsyncExecutor#submit(AsyncTask)}, allows to poll + * for the result of the asynch workload. + * @author badlogic + * + */ +public class AsyncResult { + private final T result; + + AsyncResult(T result) { + this.result = result; + } + + /** + * @return whether the {@link AsyncTask} is done + */ + public boolean isDone() { + return true; + } + + /** + * @return the result, or null if there was an error, no result, or the task is still running + */ + public T get() { + return result; + } +} diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncTask.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncTask.java new file mode 100644 index 00000000..ca307ed7 --- /dev/null +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/AsyncTask.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.oscim.utils.async; + + +/** + * Task to be submitted to an {@link AsyncExecutor}, returning a result of type T. + * @author badlogic + * + */ +public interface AsyncTask extends Runnable{ + public boolean cancel(); + public T getResult() throws Exception; +} \ No newline at end of file diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/ThreadUtils.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/ThreadUtils.java new file mode 100644 index 00000000..5852b928 --- /dev/null +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/utils/async/ThreadUtils.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.oscim.utils.async; + +/** + * GWT emulation of ThreadUtils, does nothing. + * @author badlogic + * + */ +public class ThreadUtils { + public static void yield() { + } +} diff --git a/vtm-gdx/src/org/oscim/gdx/GdxMap.java b/vtm-gdx/src/org/oscim/gdx/GdxMap.java index d4587b0e..582d5822 100644 --- a/vtm-gdx/src/org/oscim/gdx/GdxMap.java +++ b/vtm-gdx/src/org/oscim/gdx/GdxMap.java @@ -24,6 +24,8 @@ import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.input.GestureDetector; import com.badlogic.gdx.input.GestureDetector.GestureListener; import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.Timer; +import com.badlogic.gdx.utils.Timer.Task; public class GdxMap implements ApplicationListener { @@ -66,13 +68,24 @@ public class GdxMap implements ApplicationListener { } @Override - public boolean postRunnable(Runnable runnable) { + public boolean post(Runnable runnable) { Gdx.app.postRunnable(runnable); return true; } + + @Override + public boolean postDelayed(final Runnable action, long delay) { + Timer.schedule(new Task(){ + @Override + public void run() { + action.run(); + }}, delay / 1000f); + return true; + } }; mMapRenderer = new GLRenderer(mMapView); + } // Stage ui; diff --git a/vtm/src/org/oscim/utils/async/AsyncExecutor.java b/vtm/src/org/oscim/utils/async/AsyncExecutor.java new file mode 100644 index 00000000..d0851024 --- /dev/null +++ b/vtm/src/org/oscim/utils/async/AsyncExecutor.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.oscim.utils.async; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + + +/** + * Allows asnynchronous execution of {@link AsyncTask} instances on a separate thread. + * Needs to be disposed via a call to {@link #dispose()} when no longer used, in which + * case the executor waits for running tasks to finish. Scheduled but not yet + * running tasks will not be executed. + * @author badlogic + * + */ +public class AsyncExecutor { + private final ExecutorService executor; + + /** + * Creates a new AsynchExecutor that allows maxConcurrent + * {@link Runnable} instances to run in parallel. + * @param maxConcurrent + */ + public AsyncExecutor(int maxConcurrent) { + executor = Executors.newFixedThreadPool(maxConcurrent, new ThreadFactory() { + @Override + public Thread newThread (Runnable r) { + Thread thread = new Thread(r, "VtmAsyncExecutor"); + thread.setDaemon(true); + return thread; + } + }); + } + + /** + * Submits a {@link Runnable} to be executed asynchronously. If + * maxConcurrent runnables are already running, the runnable + * will be queued. + * @param task the task to execute asynchronously + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public AsyncResult submit(final AsyncTask task) { + return new AsyncResult(executor.submit(new Callable() { + @Override + public T call () throws Exception { + task.run(); + return task.getResult(); + } + })); + } + + /** + * Submits a {@link Runnable} to be executed asynchronously. If + * maxConcurrent runnables are already running, the runnable + * will be queued. + * @param task the task to execute asynchronously + */ + public void post(Runnable task) { + executor.execute(task); + } + + /** + * Waits for running {@link AsyncTask} instances to finish, + * then destroys any resources like threads. Can not be used + * after this method is called. + */ + //@Override + public void dispose () { + executor.shutdown(); + try { + executor.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); + } catch (InterruptedException e) { + new RuntimeException("Couldn't shutdown loading thread"); + } + } +} diff --git a/vtm/src/org/oscim/utils/async/AsyncResult.java b/vtm/src/org/oscim/utils/async/AsyncResult.java new file mode 100644 index 00000000..fc371566 --- /dev/null +++ b/vtm/src/org/oscim/utils/async/AsyncResult.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.oscim.utils.async; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** Returned by {@link AsyncExecutor#submit(AsyncTask)}, allows to poll for the result of the asynch workload. + * @author badlogic */ +public class AsyncResult { + private final Future future; + + AsyncResult (Future future) { + this.future = future; + } + + /** @return whether the {@link AsyncTask} is done */ + public boolean isDone () { + return future.isDone(); + } + + /** @return waits if necessary for the computation to complete and then returns the result + * @throws GdxRuntimeException if there was an error */ + public T get () { + try { + return future.get(); + } catch (InterruptedException ex) { + return null; + } catch (ExecutionException ex) { + throw new RuntimeException(ex.getCause()); + } + } +} diff --git a/vtm/src/org/oscim/utils/async/AsyncTask.java b/vtm/src/org/oscim/utils/async/AsyncTask.java new file mode 100644 index 00000000..6556132b --- /dev/null +++ b/vtm/src/org/oscim/utils/async/AsyncTask.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package org.oscim.utils.async; + +/** + * Task to be submitted to an {@link AsyncExecutor}, returning a result of type T. + * @author badlogic + * + */ +public interface AsyncTask extends Runnable{ + public boolean cancel(); + public T getResult() throws Exception; +} diff --git a/vtm/src/org/oscim/utils/async/ThreadUtils.java b/vtm/src/org/oscim/utils/async/ThreadUtils.java new file mode 100644 index 00000000..9533878f --- /dev/null +++ b/vtm/src/org/oscim/utils/async/ThreadUtils.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2013 See libgdx AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ +package org.oscim.utils.async; + +/** + * Utilities for threaded programming. + * + * @author badlogic + */ +public class ThreadUtils { + public static void yield() { + Thread.yield(); + } +} diff --git a/vtm/src/org/oscim/view/MapView.java b/vtm/src/org/oscim/view/MapView.java index 99550f9e..0c6f72fe 100644 --- a/vtm/src/org/oscim/view/MapView.java +++ b/vtm/src/org/oscim/view/MapView.java @@ -31,6 +31,8 @@ import org.oscim.theme.IRenderTheme; import org.oscim.theme.InternalRenderTheme; import org.oscim.theme.ThemeLoader; import org.oscim.tilesource.TileSource; +import org.oscim.utils.async.AsyncExecutor; +import org.oscim.utils.async.AsyncTask; public abstract class MapView { @@ -40,35 +42,35 @@ public abstract class MapView { private final LayerManager mLayerManager; private final MapViewPosition mMapViewPosition; private final MapPosition mMapPosition; + private final AsyncExecutor mAsyncExecutor; private DebugSettings mDebugSettings; + protected boolean mClearMap; + public MapView() { mMapViewPosition = new MapViewPosition(this); mMapPosition = new MapPosition(); mLayerManager = new LayerManager(); + mAsyncExecutor = new AsyncExecutor(2); - // FIXME + // FIXME! mDebugSettings = new DebugSettings(); MapTileLoader.setDebugSettings(mDebugSettings); mLayerManager.add(0, new MapEventLayer(this)); } + private MapTileLayer mBaseLayer; + //private BitmapTileLayer mBackgroundLayer; public MapTileLayer setBaseMap(TileSource tileSource) { mBaseLayer = new MapTileLayer(this); mBaseLayer.setTileSource(tileSource); - //mLayerManager.add(0, new MapEventLayer(this)); - mLayerManager.add(1, mBaseLayer); - //mRotationEnabled = true; - - //mLayerManager.add(new GenericOverlay(this, new GridRenderLayer(this))); - return mBaseLayer; } @@ -77,19 +79,18 @@ public abstract class MapView { } public MapTileLayer setBaseMap(BitmapTileLayer tileLayer) { - //mLayerManager.add(0, new MapEventLayer(this)); mLayerManager.add(1, tileLayer); return null; } public void setTheme(InternalRenderTheme theme) { - if (mBaseLayer == null){ + if (mBaseLayer == null) { Log.e(TAG, "No base layer set"); throw new IllegalStateException(); } IRenderTheme t = ThemeLoader.load(theme); - if (t == null){ + if (t == null) { Log.e(TAG, "Invalid theme"); throw new IllegalStateException(); } @@ -99,6 +100,7 @@ public abstract class MapView { public void destroy() { mLayerManager.destroy(); + mAsyncExecutor.dispose(); } /** @@ -110,12 +112,35 @@ public abstract class MapView { */ public abstract void updateMap(boolean forceRedraw); + /** + * Request to render a frame. Request will be handled on main + * thread. Use this for animations in RenderLayers. + */ + public abstract void render(); + + /** + * Post a runnable to be executed on main-thread + */ + public abstract boolean post(Runnable action); + + /** + * Post a runnable to be executed on main-thread. Execution is delayed for + * at least 'delay' milliseconds. + */ + public abstract boolean postDelayed(Runnable action, long delay); + + /** + * Post a task to run on a shared worker-thread. Only use for + * tasks running less than a second! + * */ + public void addTask(Runnable task){ + mAsyncExecutor.post(task); + } public abstract int getWidth(); - public abstract int getHeight(); - public abstract boolean postRunnable(Runnable runnable); - protected boolean mClearMap; + public abstract int getHeight(); + /** * Request to clear all layers before rendering next frame @@ -124,12 +149,6 @@ public abstract class MapView { mClearMap = true; } - /** - * Request to render a frame. Request will be handled on main - * thread. Use this for animations. - */ - public abstract void render(); - /** * Do not call directly! This function is run on main-loop * before rendering a frame. @@ -198,7 +217,4 @@ public abstract class MapView { return mMapViewPosition.getViewBox(); } - - - }