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.Executors;
9 import java.util.concurrent.TimeUnit;
11 import net.sf.openrocket.database.ThrustCurveMotorSet;
12 import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
13 import net.sf.openrocket.file.iterator.DirectoryIterator;
14 import net.sf.openrocket.file.iterator.FileIterator;
15 import net.sf.openrocket.file.motor.GeneralMotorLoader;
16 import net.sf.openrocket.file.motor.MotorLoaderHelper;
17 import net.sf.openrocket.gui.util.SimpleFileFilter;
18 import net.sf.openrocket.gui.util.SwingPreferences;
19 import net.sf.openrocket.logging.LogHelper;
20 import net.sf.openrocket.motor.Motor;
21 import net.sf.openrocket.motor.ThrustCurveMotor;
22 import net.sf.openrocket.util.BugException;
23 import net.sf.openrocket.util.Pair;
26 * Load motors in parallel using a three stage pipeline.
28 * Stage 1: single thread managed by the ThrustCurveMotorSetDatabase. This thread generates
29 * one object for each thrust curve motor file and puts it in the second stage.
31 * Stage 2: multiple threads which process individual files. Each process takes
32 * a single motor file and parses out the list of motors it contains.
33 * The list of motors is queued up for the third stage to process.
35 * Stage 3: single thread which processes the list of motors generated in stage 2.
36 * This thread puts all the motors from the list in the motor set database.
38 * It is important that stage 3 be done with a single thread because ThrustCurveMotorSetDatabase
39 * is not thread safe. Even if synchronization were to be done, it is unlikely that parallelizing
40 * this process would improve anything.
44 public class ConcurrentLoadingThrustCurveMotorSetDatabase extends ThrustCurveMotorSetDatabase {
46 private static final LogHelper log = Application.getLogger();
47 private final String thrustCurveDirectory;
50 public ConcurrentLoadingThrustCurveMotorSetDatabase(String thrustCurveDirectory) {
51 // configure ThrustCurveMotorSetDatabase as true so we get our own thread in
54 this.thrustCurveDirectory = thrustCurveDirectory;
58 protected void loadMotors() {
60 BookKeeping keeper = new BookKeeping();
64 keeper.waitForFinish();
66 catch ( InterruptedException iex ) {
67 throw new BugException(iex);
74 private void addAll( List<Motor> motors ) {
75 for (Motor m : motors) {
76 addMotor( (ThrustCurveMotor) m);
81 * A class which holds all the threading data.
82 * Implemented as an inner class so we can easily jettison the references when
83 * the processing is terminated.
86 private class BookKeeping {
89 * Executor for Stage 3.
91 private final ExecutorService writerThread;
94 * Executor for Stage 2.
96 private final ExecutorService loaderPool;
99 * Runnable used for Stage 1.
101 private final WorkGenerator workGenerator;
103 private long startTime;
106 * Number of thrust curves loaded
108 private int thrustCurveCount = 0;
111 * Number of files processed.
113 private int fileCount = 0;
116 * We have to hold on to the zip file iterator which is used to load
117 * the system motor files until all processing is done. This is because
118 * closing the iterator prematurely causes all the InputStreams opened
121 private FileIterator iterator;
123 private BookKeeping() {
125 writerThread = Executors.newSingleThreadExecutor();
127 loaderPool = Executors.newFixedThreadPool(25);
129 workGenerator = new WorkGenerator();
133 private void start() {
135 startTime = System.currentTimeMillis();
137 log.info("Starting motor loading from " + thrustCurveDirectory + " in background thread.");
139 // Run the work generator - in this thread.
144 private void waitForFinish() throws InterruptedException {
146 loaderPool.shutdown();
147 loaderPool.awaitTermination(10, TimeUnit.SECONDS);
148 writerThread.shutdown();
149 writerThread.awaitTermination(10, TimeUnit.SECONDS);
155 long endTime = System.currentTimeMillis();
157 int distinctMotorCount = 0;
158 int distinctThrustCurveCount = 0;
159 distinctMotorCount = motorSets.size();
160 for (ThrustCurveMotorSet set : motorSets) {
161 distinctThrustCurveCount += set.getMotorCount();
164 log.info("Motor loading done, took " + (endTime - startTime) + " ms to load "
165 + fileCount + " files/directories containing "
166 + thrustCurveCount + " thrust curves which contained "
167 + distinctMotorCount + " distinct motors with "
168 + distinctThrustCurveCount + " distinct thrust curves.");
173 private class WorkGenerator implements Runnable {
178 log.info("Loading motors from " + thrustCurveDirectory);
180 iterator = DirectoryIterator.findDirectory(thrustCurveDirectory,
181 new SimpleFileFilter("", false, "eng", "rse"));
183 // Load the packaged thrust curves
184 if (iterator == null) {
185 throw new IllegalStateException("Thrust curve directory " + thrustCurveDirectory +
186 "not found, distribution built wrong");
189 while( iterator.hasNext() ) {
190 Pair<String,InputStream> f = iterator.next();
191 MotorLoader loader = new MotorLoader( f.getV(), f.getU() );
192 loaderPool.execute(loader);
196 // Load the user-defined thrust curves
197 for (File file : ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles()) {
198 log.info("Loading motors from " + file);
199 MotorLoader loader = new MotorLoader( file );
200 loaderPool.execute(loader);
206 private class MotorLoader implements Runnable {
208 private final InputStream is;
209 private final String fileName;
211 private final File file;
213 public MotorLoader( File file ) {
217 this.fileName = null;
220 public MotorLoader(InputStream is, String fileName) {
224 this.fileName = fileName;
229 log.debug("Loading motor from " + fileName);
233 if ( file == null ) {
234 motors = MotorLoaderHelper.load(is, fileName);
236 motors = MotorLoaderHelper.load(file);
238 writerThread.submit( new MotorInserter(motors));
244 } catch ( IOException iex ) {
251 private class MotorInserter implements Runnable {
253 private final List<Motor> motors;
255 MotorInserter( List<Motor> motors ) {
256 this.motors = motors;
261 thrustCurveCount += motors.size();
262 ConcurrentLoadingThrustCurveMotorSetDatabase.this.addAll(motors);