This is an automated email from the ASF dual-hosted git repository.

mbien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new cdb9576868 Improve output window performance for ant java tasks #4141.
     new e6b10039be Merge pull request #4180 from notzed/nozed-antio
cdb9576868 is described below

commit cdb95768688cd5c9f3a5efe3feb441860dd8b4d3
Author: Michael Zucchi <[email protected]>
AuthorDate: Sat Jun 18 12:44:15 2022 +0930

    Improve output window performance for ant java tasks #4141.
    
    Main changes:
     Change to use matches() rather than find() for the stack checking
      regex, which runs very slowly on long lines.
     Use buffered reads rather than single byte reads.
    
    Other changes:
     Use an ExecutorService to start and manage threads.
     Remove references to deprecated ThreadDeath exception.
     Move critical section to isolated functions.
     Split non-processing and processing stream handlers
      into separate classes.
     Fix some logic errors introduced from / and requiring ancient
      'hot fixes'.
     Fixed use of FoldingHelper - it's functions aren't thread
      safe but called from multiple threads, it's comment also
      says it is used as a synchronisation lock for cleaner output but
      it was only locking locally on this.
     Move the i/o thread setup to NbOutputStreamHandler::start() - this
      ensures all necessary threads are created even if not all three
      setProcess*Stream() functions are invoked, thus avoiding potential
      i/o lock.
---
 .../ant/module/bridge/impl/ForkedJavaOverride.java | 299 +++++++++++----------
 1 file changed, 150 insertions(+), 149 deletions(-)

diff --git 
a/extide/o.apache.tools.ant.module/src-bridge/org/apache/tools/ant/module/bridge/impl/ForkedJavaOverride.java
 
b/extide/o.apache.tools.ant.module/src-bridge/org/apache/tools/ant/module/bridge/impl/ForkedJavaOverride.java
index 1a2e73c978..4cf00ef556 100644
--- 
a/extide/o.apache.tools.ant.module/src-bridge/org/apache/tools/ant/module/bridge/impl/ForkedJavaOverride.java
+++ 
b/extide/o.apache.tools.ant.module/src-bridge/org/apache/tools/ant/module/bridge/impl/ForkedJavaOverride.java
@@ -24,7 +24,11 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Vector;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
@@ -162,219 +166,216 @@ public class ForkedJavaOverride extends Java {
 
         private class NbOutputStreamHandler implements ExecuteStreamHandler {
 
-            private Thread outTask;
-            private Thread errTask;
-            private Thread inTask;
-            private Copier outCopier, errCopier; // #212526
-            private FoldingHelper foldingHelper;
-
+            private final ExecutorService tasks;
+            private final FoldingHelper foldingHelper;
+            private Future inputTask;
+            private InputStream stdout, stderr;
+            private OutputStream stdin;
+            
             NbOutputStreamHandler() {
                 this.foldingHelper = new FoldingHelper();
+                this.tasks = Executors.newFixedThreadPool(3, (r) -> {
+                    Thread t = new 
Thread(Thread.currentThread().getThreadGroup(),
+                        r,
+                        "I/O Thread for " + getProject().getName()); // NOI18N
+                    t.setDaemon(true);
+                    return t;
+                });
             }
 
-            public void start() throws IOException {}
+            private void setCopier(InputStream inputStream, OutputStream os, 
boolean delegate, boolean err) {
+                if (os == null || delegate) {
+                    tasks.submit(new TransferCopier(inputStream, 
AntBridge.delegateOutputStream(err)));
+                } else {
+                    tasks.submit(new TransferCopier(inputStream, os));
+                }
+            }
+
+            public void start() throws IOException {
+                NbBuildLogger buildLogger = 
getProject().getBuildListeners().stream()
+                    .filter(o -> o instanceof NbBuildLogger)
+                    .map(o -> (NbBuildLogger)o)
+                    .findFirst()
+                    .orElse(null);
+                if (buildLogger != null) {
+                    tasks.submit(new MarkupCopier(stdout, Project.MSG_INFO, 
false, outEncoding, buildLogger, foldingHelper));
+                    tasks.submit(new MarkupCopier(stderr, Project.MSG_WARN, 
true, errEncoding, buildLogger, foldingHelper));
+                } else {
+                    setCopier(stdout, getOutputStream(), delegateOutputStream, 
false);
+                    setCopier(stderr, getErrorStream(), delegateErrorStream, 
true);
+                }                                
+                InputStream is = getInputStream();
+                if (is == null)
+                    is = AntBridge.delegateInputStream();
+                inputTask = tasks.submit(new TransferCopier(is, stdin));
+            }
 
             public void stop() {
-                if (errTask != null) {
-                    try {
-                        errTask.join(3000);
-                    } catch (InterruptedException ex) {
-                    }
-                }
-                if (outTask != null) {
-                    try {
-                        outTask.join(3000);
-                    } catch (InterruptedException ex) {
-                    }
-                }
-                if (inTask != null) {
-                    inTask.interrupt();
-                    try {
-                        inTask.join(1000);
-                    } catch (InterruptedException ex) {
-                    }
-                }
-                if (outCopier != null) {
-                    outCopier.maybeFlush();
-                }
-                if (errCopier != null) {
-                    errCopier.maybeFlush();
+                try {
+                    if (inputTask != null)
+                        inputTask.cancel(true);
+                    tasks.shutdown();
+                    tasks.awaitTermination(3, TimeUnit.SECONDS);
+                } catch (InterruptedException ex) {
+                } finally {
+                    tasks.shutdownNow();
                 }
             }
 
             public void setProcessOutputStream(InputStream inputStream) throws 
IOException {
-                OutputStream os = getOutputStream();
-                Integer logLevel = null;
-                if (os == null || delegateOutputStream) {
-                    os = AntBridge.delegateOutputStream(false);
-                    logLevel = Project.MSG_INFO;
-                }
-                outTask = new Thread(Thread.currentThread().getThreadGroup(), 
outCopier = new Copier(inputStream, os, logLevel, outEncoding, foldingHelper),
-                        "Out Thread for " + getProject().getName()); // NOI18N
-                outTask.setDaemon(true);
-                outTask.start();
+                this.stdout = inputStream;
             }
 
             public void setProcessErrorStream(InputStream inputStream) throws 
IOException {
-                OutputStream os = getErrorStream();
-                Integer logLevel = null;
-                if (os == null || delegateErrorStream) {
-                    os = AntBridge.delegateOutputStream(true);
-                    logLevel = Project.MSG_WARN;
-                }
-                errTask = new Thread(Thread.currentThread().getThreadGroup(), 
errCopier = new Copier(inputStream, os, logLevel, errEncoding, foldingHelper),
-                        "Err Thread for " + getProject().getName()); // NOI18N
-                errTask.setDaemon(true);
-                errTask.start();
+                this.stderr = inputStream;
             }
 
             public void setProcessInputStream(OutputStream outputStream) 
throws IOException {
-                InputStream is = getInputStream();
-                if (is == null) {
-                    is = AntBridge.delegateInputStream();
-                }
-                inTask = new Thread(Thread.currentThread().getThreadGroup(), 
new Copier(is, outputStream, null, null, foldingHelper),
-                        "In Thread for " + getProject().getName()); // NOI18N
-                inTask.setDaemon(true);
-                inTask.start();
+                this.stdin = outputStream;
             }
 
         }
 
     }
 
-    private class Copier implements Runnable {
+    /**
+     * Simple copier that transfers all input to output.
+     */
+    private class TransferCopier implements Runnable {
 
         private final InputStream in;
         private final OutputStream out;
-        private final Integer logLevel;
+
+        public TransferCopier(InputStream in, OutputStream out) {
+            this.in = in;
+            this.out = out;
+        }
+
+        @Override
+        public void run() {
+            try {
+                byte[] data = new byte[1024];
+                int len;
+                while ((len = in.read(data)) >= 0) {
+                    out.write(data, 0, len);
+                    out.flush();
+                }
+            } catch (IOException  x) {
+                // ignore IOException: Broken pipe from 
FileOutputStream.writeBytes in BufferedOutputStream.flush
+            }
+        }
+
+    }
+
+    /**
+     * Filtering copier that marks up links, ignoring stack traces.
+     */
+    private class MarkupCopier implements Runnable {
+
+        private final InputStream in;
+        private final int logLevel;
         private final String encoding;
         private final RequestProcessor.Task flusher;
         private final ByteArrayOutputStream currentLine;
-        private OutputWriter ow = null;
-        private boolean err;
-        private AntSession session = null;
+        private final OutputWriter ow;
+        private final boolean err;
+        private final AntSession session;
         private final FoldingHelper foldingHelper;
 
-        public Copier(InputStream in, OutputStream out, Integer logLevel, 
String encoding/*, long init*/,
-                FoldingHelper foldingHelper) {
+        public MarkupCopier(InputStream in, int logLevel, boolean err, String 
encoding, NbBuildLogger buildLogger, FoldingHelper foldingHelper) {
             this.in = in;
-            this.out = out;
             this.logLevel = logLevel;
+            this.err = err;
             this.encoding = encoding;
             this.foldingHelper = foldingHelper;
-            if (logLevel != null) {
-                flusher = PROCESSOR.create(new Runnable() {
-                    public void run() {
-                        maybeFlush();
-                    }
-                });
-                currentLine = new ByteArrayOutputStream();
+
+            flusher = PROCESSOR.create(() -> maybeFlush(false));
+            currentLine = new ByteArrayOutputStream();
+
+            ow = err ? buildLogger.err : buildLogger.out;
+            session = buildLogger.thisSession;
+        }
+
+        private synchronized void append(byte[] data, int off, int len) {
+            currentLine.write(data, off, len);
+            if (currentLine.size() > 8192) {
+                flusher.run();
             } else {
-                flusher = null;
-                currentLine = null;
+                flusher.schedule(250);
             }
         }
 
+        private synchronized String appendAndTake(byte[] data, int off, int 
len) throws UnsupportedEncodingException {
+            currentLine.write(data, off, len);
+            String str = currentLine.toString(encoding);
+            currentLine.reset();
+            return str;
+        }
+
+        private synchronized String take() throws UnsupportedEncodingException 
{
+            String str = currentLine.toString(encoding);
+            currentLine.reset();
+            return str;
+        }
+
         public void run() {
-            /*
-            StringBuilder content = new StringBuilder();
-            long tick = System.currentTimeMillis();
-            content.append(String.format("[init: %1.1fsec]", (tick - init) / 
1000.0));
-             */
-            
-            if (ow == null && logLevel != null) {
-                Vector v = getProject().getBuildListeners();
-                for (Object o : v) {
-                    if (o instanceof NbBuildLogger) {
-                        NbBuildLogger l = (NbBuildLogger) o;
-                        err = logLevel != Project.MSG_INFO;
-                        ow = err ? l.err : l.out;
-                        session = l.thisSession;
-                        break;
-                    }
-                }
-            }
             try {
+                byte[] data = new byte[1024];
+                int len;
+
                 try {
-                    int c;
-                    while ((c = in.read()) != -1) {
-                        if (logLevel == null) {
-                            // Input gets sent immediately.
-                            out.write(c);
-                            out.flush();
-                        } else {
-                            synchronized (this) {
-                                if (c == '\n') {                               
     
-                                    String str = 
currentLine.toString(encoding);
-                                    int len = str.length();
-                                    if (len > 0 && str.charAt(len - 1) == 
'\r') {
-                                        str = str.substring(0, len - 1);
-                                    }
+                    while ((len = in.read(data)) >= 0) {
+                        int last = 0;
+                        for (int i = 0; i < len; i++) {
+                            int c = data[i] & 0xff;
 
+                            // Add folds for stack traces and mark up lines
+                            // not processed by JavaAntLogger stack trace 
detection
+                            if (c == '\n') {
+                                String str = appendAndTake(data, last, i > 
last && data[i - 1] == '\r' ? i - last - 1 : i - last);
+
+                                synchronized (foldingHelper) {
                                     foldingHelper.checkFolds(str, err, 
session);
-                                    if (str.length() < LOGGER_MAX_LINE_LENGTH) 
{ // not too long message, probably interesting
-                                        // skip stack traces (hyperlinks are 
created by JavaAntLogger), everything else write directly
-                                        if (!STACK_TRACE.matcher(str).find()) {
-                                            StandardLogger.findHyperlink(str, 
session, null).println(session, err);
-                                        }
-                                    } else {
-                                        // do not match long strings, directly 
create a trivial hyperlink
+                                    if (str.length() >= LOGGER_MAX_LINE_LENGTH 
|| !STACK_TRACE.matcher(str).matches())
                                         StandardLogger.findHyperlink(str, 
session, null).println(session, err);
-                                    }
                                     log(str, logLevel);
-                                    currentLine.reset();
-                                } else {                                    
-                                    currentLine.write(c);
-                                    if(currentLine.size() > 8192) {
-                                        flusher.run();
-                                    } else {
-                                        flusher.schedule(250);
-                                    }
                                 }
-                            }    
+                                last = i + 1;
+                            }
                         }
+
+                        if (last < len)
+                            append(data, last, len - last);
                     }
                 } finally {
-                    if (logLevel != null) {
-                        maybeFlush();
-                        if (err) {
-                            foldingHelper.clearHandle();
-                        }
-                    }
+                    maybeFlush(true);
                 }
             } catch (IOException x) {
                 // ignore IOException: Broken pipe from 
FileOutputStream.writeBytes in BufferedOutputStream.flush
-            } catch (ThreadDeath d) {
-                // OK, build just stopped.
-                return;
             }
-            //System.err.println("copied " + in + " to " + out + "; content='" 
+ content + "'");
         }
 
-        private synchronized void maybeFlush() {
-            if (ow == null) { // ?? #200365
-                return;
-            }
+        public void maybeFlush(boolean end) {
             try {
-                if (currentLine.size() > 0) {
-                    String str = currentLine.toString(encoding);
-                    ow.write(str);
-                    log(str, logLevel);
+                String str = take();
+                synchronized (foldingHelper) {
+                    if (!str.isEmpty()) {
+                        ow.write(str);
+                        log(str, logLevel);
+                    }
+                    if (end && err)
+                        foldingHelper.clearHandle();
                 }
             } catch (IOException x) {
-                // probably safe to ignore
-            } catch (ThreadDeath d) {
-                // OK, build just stopped.
+                // ignore IOException: Broken pipe from 
FileOutputStream.writeBytes in BufferedOutputStream.flush
             }
-            currentLine.reset();
         }
 
     }
 
     /**
      * A helper class for detecting stacktraces in the output and for creating
-     * folds for them. It is also used as a shared lock for {@link Copier}s of
+     * folds for them. It is also used as a shared lock for {@link 
MarkupCopier}s of
      * standard and error outputs, which should make the mixed output a bit 
more
      * readable.
      */


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to