1 package net.sf.openrocket.startup;
4 import java.io.IOException;
5 import java.io.InputStream;
7 import java.util.concurrent.ExecutorService;
8 import java.util.concurrent.LinkedBlockingQueue;
9 import java.util.concurrent.ThreadFactory;
10 import java.util.concurrent.ThreadPoolExecutor;
11 import java.util.concurrent.TimeUnit;
13 import net.sf.openrocket.database.ThrustCurveMotorSet;
14 import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
15 import net.sf.openrocket.file.iterator.DirectoryIterator;
16 import net.sf.openrocket.file.iterator.FileIterator;
17 import net.sf.openrocket.file.motor.MotorLoaderHelper;
18 import net.sf.openrocket.gui.util.SimpleFileFilter;
19 import net.sf.openrocket.gui.util.SwingPreferences;
20 import net.sf.openrocket.logging.LogHelper;
21 import net.sf.openrocket.motor.Motor;
22 import net.sf.openrocket.motor.ThrustCurveMotor;
23 import net.sf.openrocket.util.BugException;
24 import net.sf.openrocket.util.Pair;
27 * Load motors in parallel using a three stage pipeline.
29 * Stage 1: single thread managed by the ThrustCurveMotorSetDatabase. This thread generates
30 * one object for each thrust curve motor file and puts it in the second stage.
32 * Stage 2: multiple threads which process individual files. Each process takes
33 * a single motor file and parses out the list of motors it contains.
34 * The list of motors is queued up for the third stage to process.
36 * Stage 3: single thread which processes the list of motors generated in stage 2.
37 * This thread puts all the motors from the list in the motor set database.
39 * It is important that stage 3 be done with a single thread because ThrustCurveMotorSetDatabase
40 * is not thread safe. Even if synchronization were to be done, it is unlikely that parallelizing
41 * this process would improve anything.
45 public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMotorSetDatabase {
47 private static final LogHelper log = Application.getLogger();
48 private final String thrustCurveDirectory;
51 public ConcurrentLoadingThrustCurveMotorSetDatabase(String thrustCurveDirectory) {
52 // configure ThrustCurveMotorSetDatabase as true so we get our own thread in
55 this.thrustCurveDirectory = thrustCurveDirectory;
59 protected void loadMotors() {
61 BookKeeping keeper = new BookKeeping();
65 keeper.waitForFinish();
67 catch ( InterruptedException iex ) {
68 throw new BugException(iex);
75 private void addAll( List<Motor> motors ) {
76 for (Motor m : motors) {
77 addMotor( (ThrustCurveMotor) m);
82 * A class which holds all the threading data.
83 * Implemented as an inner class so we can easily jettison the references when
84 * the processing is terminated.
87 private class BookKeeping {
90 * Executor for Stage 3.
92 private final ExecutorService writerThread;
95 * Executor for Stage 2.
97 private final ExecutorService loaderPool;
100 * Runnable used for Stage 1.
102 private final WorkGenerator workGenerator;
104 private long startTime;
107 * Number of thrust curves loaded
109 private int thrustCurveCount = 0;
112 * Number of files processed.
114 private int fileCount = 0;
117 * We have to hold on to the zip file iterator which is used to load
118 * the system motor files until all processing is done. This is because
119 * closing the iterator prematurely causes all the InputStreams opened
122 private FileIterator iterator;
124 private BookKeeping() {
126 writerThread = new ThreadPoolExecutor(1,1,200, TimeUnit.SECONDS,
127 new LinkedBlockingQueue<Runnable>(),
128 new ThreadFactory() {
130 public Thread newThread(Runnable r) {
131 Thread t = new Thread(r,"MotorWriterThread");
136 loaderPool = new ThreadPoolExecutor(25,25, 2, TimeUnit.SECONDS,
137 new LinkedBlockingQueue<Runnable>(),
138 new ThreadFactory() {
141 public Thread newThread(Runnable r) {
142 Thread t = new Thread(r,"MotorLoaderPool-" + threadCount++);
147 workGenerator = new WorkGenerator();
151 private void start() {
153 startTime = System.currentTimeMillis();
155 log.info("Starting motor loading from " + thrustCurveDirectory + " in background thread.");
157 // Run the work generator - in this thread.
162 private void waitForFinish() throws InterruptedException {
164 loaderPool.shutdown();
165 loaderPool.awaitTermination(10, TimeUnit.SECONDS);
166 writerThread.shutdown();
167 writerThread.awaitTermination(10, TimeUnit.SECONDS);
173 long endTime = System.currentTimeMillis();
175 int distinctMotorCount = 0;
176 int distinctThrustCurveCount = 0;
177 distinctMotorCount = motorSets.size();
178 for (ThrustCurveMotorSet set : motorSets) {
179 distinctThrustCurveCount += set.getMotorCount();
182 log.info("Motor loading done, took " + (endTime - startTime) + " ms to load "
183 + fileCount + " files/directories containing "
184 + thrustCurveCount + " thrust curves which contained "
185 + distinctMotorCount + " distinct motors with "
186 + distinctThrustCurveCount + " distinct thrust curves.");
191 private class WorkGenerator implements Runnable {
196 log.info("Loading motors from " + thrustCurveDirectory);
198 iterator = DirectoryIterator.findDirectory(thrustCurveDirectory,
199 new SimpleFileFilter("", false, "eng", "rse"));
201 // Load the packaged thrust curves
202 if (iterator == null) {
203 throw new IllegalStateException("Thrust curve directory " + thrustCurveDirectory +
204 "not found, distribution built wrong");
207 while( iterator.hasNext() ) {
208 Pair<String,InputStream> f = iterator.next();
209 MotorLoader loader = new MotorLoader( f.getV(), f.getU() );
210 loaderPool.execute(loader);
214 // Load the user-defined thrust curves
215 for (File file : ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles()) {
216 log.info("Loading motors from " + file);
217 MotorLoader loader = new MotorLoader( file );
218 loaderPool.execute(loader);
224 private class MotorLoader implements Runnable {
226 private final InputStream is;
227 private final String fileName;
229 private final File file;
231 public MotorLoader( File file ) {
235 this.fileName = null;
238 public MotorLoader(InputStream is, String fileName) {
242 this.fileName = fileName;
247 log.debug("Loading motor from " + fileName);
251 if ( file == null ) {
252 motors = MotorLoaderHelper.load(is, fileName);
254 motors = MotorLoaderHelper.load(file);
256 writerThread.submit( new MotorInserter(motors));
262 } catch ( IOException iex ) {
269 private class MotorInserter implements Runnable {
271 private final List<Motor> motors;
273 MotorInserter( List<Motor> motors ) {
274 this.motors = motors;
279 thrustCurveCount += motors.size();
280 ConcurrentLoadingThrustCurveMotorSetDatabase.this.addAll(motors);