r1732 - trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser
Author: tchemit Date: 2010-04-02 22:02:13 +0200 (Fri, 02 Apr 2010) New Revision: 1732 Log: Anomalie #438: Blocage sur le parserJava lorsqu'il n'y a aucun fichier ?\195?\160 parser Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java Modified: trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java =================================================================== --- trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java 2010-03-30 20:35:54 UTC (rev 1731) +++ trunk/maven-i18n-plugin/src/main/java/org/nuiton/i18n/plugin/parser/ParserThread.java 2010-04-02 20:02:13 UTC (rev 1732) @@ -22,14 +22,23 @@ /** Logger */ private static final Log log = LogFactory.getLog(ParserThread.class); + /** + * This is a task to parse a {@link #file}. + * <p/> + * The task will be executed in the executor service created in the thread. + */ class ParserTask implements Runnable { + /** the file parser */ protected final FileParser parser; + /** the file to parse */ protected final File file; + /** starting time */ protected long startingTime; + /** ending time */ protected long endingTime; ParserTask(FileParser parser, File file) { @@ -83,6 +92,16 @@ destroy(); } + /** + * Register the result of the parsing of the {@link #file} after {@link + * #run()} method was invoked. + * <p/> + * This method should be invoked by the executor as an ending hook. + * + * @param treatedFiles list of files already treated + * @param touchedFiles list of files already touched + * @param result shared result. + */ protected synchronized void registerResult(List<File> treatedFiles, List<File> touchedFiles, SortedProperties result) { @@ -114,8 +133,15 @@ result.putAll(parser.getResult()); } } finally { + // destroy runner destroy(); + + // release lock2 + + synchronized (lock2) { + lock2.notifyAll(); + } } } } @@ -132,11 +158,17 @@ protected final SortedProperties result; /** - * object used to lock method {@link #terminatesAndWaits()} until all tasks - * are consumed. + * object used to lock method {@link #terminatesAndWaits()} until the thread + * is not fully terminated. */ private final Object lock = new Object(); + /** + * object used to lock in {@link #run()} until all tasks are consumed after + * the shutdown of the executor + */ + private final Object lock2 = new Object(); + /** number of files registred to consume */ protected int nbFiles; @@ -156,28 +188,39 @@ return touchedFiles; } + /** + * Add a file to be consumed. + * + * @param parser the parser of the file + * @param files files to parse + */ public void addFile(FileParser parser, File... files) { - synchronized (tasks) { + for (File f : files) { - for (File f : files) { - - nbFiles++; - if (log.isInfoEnabled()) { - log.info("[" + nbFiles + "] " + f); - } - tasks.offer(new ParserTask(parser, f)); + nbFiles++; + if (log.isInfoEnabled()) { + log.info("[" + nbFiles + "] " + f); } + tasks.offer(new ParserTask(parser, f)); } } - public synchronized void terminatesAndWaits() { - synchronized (tasks) { - if (log.isDebugEnabled()) { - log.debug("add terminate runner"); - } - tasks.offer(new ParserTask(null, null)); + /** + * Ask the thread to stop. + * <p/> + * It will finish all incoming files (but will not accept more files to + * parse) + * <p/> + * <b>Note:</b> The method does not return until all files are not + * consumed. + */ + public void terminatesAndWaits() { + + if (log.isInfoEnabled()) { + log.info("add terminate runner"); } + tasks.offer(new ParserTask(null, null)); // block until end of service @@ -185,6 +228,10 @@ log.info("block until ends..."); } + //TODO-TC20100402 Should test (and use another internal state) to be + //TODO-TC20100402 sure We have to perform a lock, sometimes it is not + //TODO-TC20100402 necessary if thread is dead before coming at this point + synchronized (lock) { try { lock.wait(); @@ -204,6 +251,7 @@ } } + /** clean internal state after usage of the thread. */ public void clear() { treatedFiles.clear(); touchedFiles.clear(); @@ -213,36 +261,12 @@ @Override public void run() { - // creates thread factory - ThreadFactory factory = - new ThreadFactory() { - final ThreadFactory defaultFactory = - Executors.defaultThreadFactory(); - - public Thread newThread(Runnable r) { - Thread thread = - defaultFactory.newThread(r); - thread.setName(ParserThread.this + "-" + thread.getName()); - return thread; - } - }; - // create pool executor - ExecutorService service = new ThreadPoolExecutor( - 8, 10, - 1L, TimeUnit.SECONDS, - new LinkedBlockingQueue<Runnable>(), - factory) { + ExecutorService executor = createExecutor(); - @Override - protected void afterExecute(Runnable r, Throwable t) { - super.afterExecute(r, t); - ParserTask i18n = (ParserTask) r; - i18n.registerResult(treatedFiles, touchedFiles, result); - } - }; + boolean canQuit = false; - while (true) { + while (!canQuit) { // waiting for a new file to treate ParserTask runner; @@ -252,60 +276,136 @@ if (log.isErrorEnabled()) { log.error(e); } + canQuit = true; continue; } if (runner.getFile() == null) { // ask to quit the tread - if (log.isInfoEnabled()) { - log.info("no more files to treate, wait termination..."); + if (log.isDebugEnabled()) { + log.debug("no more files to treate, wait termination..."); } - service.shutdown(); - break; + //FIXME-TC20100402, sleep to make sure that the lock2 is always + //FIXME-TC20100402 block before the thread is done. + //FIXME-TC20100402 otherwise, it could ends in a dead-end... + //TODO Should really use another lock or internal state before + //TODO doing lock in method terminatesAndWaits + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + log.error(e); + } + canQuit = true; + continue; } if (log.isDebugEnabled()) { log.debug("consume " + runner); } - service.execute(runner); + executor.execute(runner); if (log.isDebugEnabled()) { log.debug("after consume " + runner); } } - if (log.isInfoEnabled()) { - log.info("will waits until ends of all " + nbFiles + " runner(s)!"); + // ask executor to terminate + executor.shutdown(); + + if (nbFiles == 0) { + // no file consumed + if (log.isInfoEnabled()) { + log.info("no file consumed."); + } + } else { + if (log.isInfoEnabled()) { + log.info("will waits until all files (" + nbFiles + + ") are consumed (still " + getFilesToTreate() + + " file(s) to consume)"); + } } - // waits end of service + // waits end of executor - while (true) { + canQuit = false; - if (service.isTerminated()) { - if (log.isInfoEnabled()) { - log.info("all runners are terminated. [" + treatedFiles.size() + "]"); - } + while (!canQuit) { - // release lock - synchronized (lock) { - lock.notifyAll(); - } - break; + int treate = getFilesToTreate(); + + // can quit if and only if executore is terminated are there is no + // more files to consume + // Note : normally, the second is not necessary... + canQuit = executor.isTerminated() || treate == 0; + + if (canQuit) { + continue; } if (log.isDebugEnabled()) { - log.debug("waiting end..."); + log.debug("waiting end... (still " + treate + + " file(s) to consume)"); } - try { - sleep(200); - } catch (InterruptedException e) { - if (log.isErrorEnabled()) { + // waits a task to be done + synchronized (lock2) { + try { + lock2.wait(); + } catch (InterruptedException e) { log.error(e); + break; } } } + + if (log.isInfoEnabled()) { + log.info("executor is terminated, will release thread lock."); + } + + // service was shutdown and terminated, release thread main lock. + + synchronized (lock) { + lock.notifyAll(); + } + } + protected ExecutorService createExecutor() { + // creates thread factory + ThreadFactory factory = + new ThreadFactory() { + final ThreadFactory defaultFactory = + Executors.defaultThreadFactory(); + + public Thread newThread(Runnable r) { + Thread thread = + defaultFactory.newThread(r); + thread.setName(ParserThread.this + "-" + + thread.getName()); + return thread; + } + }; + + // create pool executor + ExecutorService executor = new ThreadPoolExecutor( + 8, 10, + 1L, TimeUnit.SECONDS, + new LinkedBlockingQueue<Runnable>(), + factory) { + + @Override + protected void afterExecute(Runnable r, Throwable t) { + super.afterExecute(r, t); + ParserTask i18n = (ParserTask) r; + i18n.registerResult(treatedFiles, touchedFiles, result); + } + }; + return executor; + } + + protected int getFilesToTreate() { + return nbFiles - treatedFiles.size(); + } + }
participants (1)
-
tchemit@users.nuiton.org