From 9eed1653fb9995c2dcf249ebdc5f36d4f8a0311c Mon Sep 17 00:00:00 2001 From: Kevin Ruland Date: Wed, 27 Jun 2012 02:58:53 +0000 Subject: [PATCH] Added threaded loader to ComponentPresetDatabase following the pattern in ThrustCurveMotorDatabase. Moved the wait into the background thread so the UI starts much faster. Changed the ConcurrentLoadingThrustCurveMotorSetDataBase implementation to have longer timeouts and wait in the background thread. --- .../sf/openrocket/android/Application.java | 7 +- .../database/ComponentPresetDatabase.java | 86 ++++++++++++++++++- .../openrocket/gui/figure3d/Quick3dMain.java | 10 ++- ...entLoadingThrustCurveMotorSetDatabase.java | 20 ++++- .../net/sf/openrocket/startup/Startup2.java | 25 ++++-- 5 files changed, 133 insertions(+), 15 deletions(-) diff --git a/android/src/net/sf/openrocket/android/Application.java b/android/src/net/sf/openrocket/android/Application.java index 624df23be..9661f400d 100644 --- a/android/src/net/sf/openrocket/android/Application.java +++ b/android/src/net/sf/openrocket/android/Application.java @@ -28,7 +28,12 @@ public class Application extends android.app.Application { net.sf.openrocket.startup.Application.setPreferences( new PreferencesAdapter() ); - net.sf.openrocket.startup.Application.setComponentPresetDao( new ComponentPresetDatabase() ); + net.sf.openrocket.startup.Application.setComponentPresetDao( new ComponentPresetDatabase(){ + @Override + protected void load() { + // We don't need components + } + } ); MotorDatabaseAdapter db = new MotorDatabaseAdapter(this); diff --git a/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java b/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java index 8acafcff5..91cfe609f 100644 --- a/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java +++ b/core/src/net/sf/openrocket/database/ComponentPresetDatabase.java @@ -4,17 +4,34 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.startup.Application; -public class ComponentPresetDatabase extends Database implements ComponentPresetDao { +public abstract class ComponentPresetDatabase extends Database implements ComponentPresetDao { + + private static final LogHelper logger = Application.getLogger(); + + private volatile boolean startedLoading = false; + private volatile boolean endedLoading = false; + private final boolean asynchronous; + + /** Set to true the first time {@link #blockUntilLoaded()} is called. */ + protected volatile boolean inUse = false; public ComponentPresetDatabase() { super(); + this.asynchronous = false; + } + + public ComponentPresetDatabase(boolean asynchronous ) { + super(); + this.asynchronous = asynchronous; } @Override public List listAll() { + blockUntilLoaded(); return list; } @@ -25,6 +42,7 @@ public class ComponentPresetDatabase extends Database implement @Override public List listForType( ComponentPreset.Type type ) { + blockUntilLoaded(); if ( type == null ) { return Collections.emptyList(); } @@ -50,6 +68,7 @@ public class ComponentPresetDatabase extends Database implement */ @Override public List listForType( ComponentPreset.Type type, boolean favorite ) { + blockUntilLoaded(); if ( !favorite ) { return listForType(type); @@ -67,6 +86,7 @@ public class ComponentPresetDatabase extends Database implement @Override public List listForTypes( ComponentPreset.Type ... type ) { + blockUntilLoaded(); if( type == null || type.length == 0 ) { return Collections.emptyList(); @@ -93,11 +113,13 @@ public class ComponentPresetDatabase extends Database implement @Override public List listForTypes( List types ) { + blockUntilLoaded(); return listForTypes( (ComponentPreset.Type[]) types.toArray() ); } @Override public List find(String manufacturer, String partNo) { + blockUntilLoaded(); List presets = new ArrayList(); for( ComponentPreset preset : list ) { if ( preset.getManufacturer().getSimpleName().equals(manufacturer) && preset.getPartNo().equals(partNo) ) { @@ -109,9 +131,71 @@ public class ComponentPresetDatabase extends Database implement @Override public void setFavorite( ComponentPreset preset, boolean favorite ) { + blockUntilLoaded(); preset.setFavorite(favorite); Application.getPreferences().setComponentFavorite( preset, favorite ); this.fireAddEvent(preset); } + + /** + * Used for loading the component preset database. This method will be called in a background + * thread to load the presets asynchronously. + */ + protected abstract void load(); + + /** + * Start loading the presets. + * + * @throws IllegalStateException if this method has already been called. + */ + public void startLoading() { + if (startedLoading) { + throw new IllegalStateException("Already called startLoading"); + } + startedLoading = true; + if (asynchronous) { + new LoadingThread().start(); + } else { + load(); + } + synchronized (this) { + endedLoading = true; + this.notifyAll(); + } + } + + /** + * Background thread for loading the presets. + */ + private class LoadingThread extends Thread { + @Override + public void run() { + load(); + } + } + + /** + * Block the current thread until loading of the presets has been completed. + * + * @throws IllegalStateException if startLoading() has not been called. + */ + public void blockUntilLoaded() { + inUse = true; + if (!startedLoading) { + throw new IllegalStateException("startLoading() has not been called"); + } + if (!endedLoading) { + synchronized (this) { + while (!endedLoading) { + try { + this.wait(); + } catch (InterruptedException e) { + logger.warn("InterruptedException occurred, ignoring", e); + } + } + } + } + } + } diff --git a/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java b/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java index 49dbbc9fd..d7c2e842c 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java +++ b/core/src/net/sf/openrocket/gui/figure3d/Quick3dMain.java @@ -41,8 +41,14 @@ public class Quick3dMain { Application.setPreferences(new SwingPreferences()); // Must be done after localization is initialized - ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(); - componentPresetDao.load("datafiles", ".*csv"); + ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase() { + + @Override + protected void load() { + // This test app doesn't need any presets loaded - just an empty database. + } + + }; Application.setComponentPresetDao( componentPresetDao ); OpenRocketDocument doc = new OpenRocketLoader().loadFromStream( diff --git a/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java b/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java index a546f739e..c52d72e19 100644 --- a/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java +++ b/core/src/net/sf/openrocket/startup/ConcurrentLoadingThrustCurveMotorSetDatabase.java @@ -9,6 +9,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import net.sf.openrocket.database.ThrustCurveMotorSet; import net.sf.openrocket.database.ThrustCurveMotorSetDatabase; @@ -47,6 +48,9 @@ public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMot private static final LogHelper log = Application.getLogger(); private final String thrustCurveDirectory; + /** Block motor loading for this many milliseconds */ + // Block motor loading for 1.5 seconds to allow window painting to be faster + private static AtomicInteger blockLoading = new AtomicInteger(1500); public ConcurrentLoadingThrustCurveMotorSetDatabase(String thrustCurveDirectory) { // configure ThrustCurveMotorSetDatabase as true so we get our own thread in @@ -58,6 +62,18 @@ public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMot @Override protected void loadMotors() { + // Block loading until timeout occurs or database is taken into use + log.info("Blocking motor loading while starting up"); + /* + while (!inUse && blockLoading.addAndGet(-100) > 0) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + */ + log.info("Blocking ended, inUse=" + inUse + " blockLoading=" + blockLoading.get()); + BookKeeping keeper = new BookKeeping(); keeper.start(); @@ -162,9 +178,9 @@ public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMot private void waitForFinish() throws InterruptedException { try { loaderPool.shutdown(); - loaderPool.awaitTermination(10, TimeUnit.SECONDS); + loaderPool.awaitTermination(30, TimeUnit.SECONDS); writerThread.shutdown(); - writerThread.awaitTermination(10, TimeUnit.SECONDS); + writerThread.awaitTermination(30, TimeUnit.SECONDS); } finally { iterator.close(); diff --git a/core/src/net/sf/openrocket/startup/Startup2.java b/core/src/net/sf/openrocket/startup/Startup2.java index 85500a361..6b7b49cdf 100644 --- a/core/src/net/sf/openrocket/startup/Startup2.java +++ b/core/src/net/sf/openrocket/startup/Startup2.java @@ -84,11 +84,23 @@ public class Startup2 { Splash.init(); // Must be done after localization is initialized - ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(); - ConcurrentComponentPresetDatabaseLoader presetLoader = new ConcurrentComponentPresetDatabaseLoader( componentPresetDao ); - presetLoader.load(); - + ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase(true) { + + @Override + protected void load() { + ConcurrentComponentPresetDatabaseLoader presetLoader = new ConcurrentComponentPresetDatabaseLoader( this ); + presetLoader.load(); + try { + presetLoader.await(); + } catch ( InterruptedException iex) { + + } + } + + }; Application.setComponentPresetDao( componentPresetDao ); + + componentPresetDao.startLoading(); // Setup the uncaught exception handler log.info("Registering exception handler"); @@ -124,11 +136,6 @@ public class Startup2 { Databases.fakeMethod(); - try { - presetLoader.await(); - } catch ( InterruptedException iex) { - - } // Starting action (load files or open new document) log.info("Opening main application window");