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

sdedic 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 ba5bb8a  [NETBEANS-5744] Avoid java.io deadlock of close vs. pending 
read.
     new 1ca0470  Merge pull request #2986 from sdedic/lsp/consoleInputLockup
ba5bb8a is described below

commit ba5bb8ab4977793354265d0d9f46ef3a4da98c0f
Author: Svata Dedic <svatopluk.de...@oracle.com>
AuthorDate: Thu Jun 3 15:36:32 2021 +0200

    [NETBEANS-5744] Avoid java.io deadlock of close vs. pending read.
---
 .../server/debugging/launch/NbProcessConsole.java  |  14 ++-
 .../server/ui/AbstractLspInputOutputProvider.java  |  10 +-
 .../debugging/launch/NbProcessConsoleTest.java     | 114 +++++++++++++++++++++
 .../modules/java/lsp/server/ui/LspIOAccessor.java  |  36 +++++++
 4 files changed, 171 insertions(+), 3 deletions(-)

diff --git 
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsole.java
 
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsole.java
index ce857f6..55d39da 100644
--- 
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsole.java
+++ 
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsole.java
@@ -71,7 +71,18 @@ public final class NbProcessConsole extends IOContext {
     protected InputStream getStdIn() throws IOException {
         synchronized (this) {
             if (inputSink == null) {
-                inputSink = new PipedInputStream(inputSource);
+                inputSink = new PipedInputStream(inputSource) {
+                    @Override
+                    public void close() throws IOException {
+                        synchronized(this) {
+                            super.close();
+                            // Bug in Piped*Stream: in.close() closes, but does
+                            // not unblock waiters, nor returns -1 from writer 
to
+                            // stop waiting. Close the writer - will also 
notifyAll().
+                            inputSource.close();
+                        }
+                    }
+                };
             }
         }
         return inputSink;
@@ -85,6 +96,7 @@ public final class NbProcessConsole extends IOContext {
         }
         inputBuffer.write(line);
         inputBuffer.newLine();
+        inputBuffer.flush();
     }
 
     @Override
diff --git 
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
 
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
index fb9acfd..e128015 100644
--- 
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
+++ 
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/ui/AbstractLspInputOutputProvider.java
@@ -30,7 +30,6 @@ import org.netbeans.api.io.OutputColor;
 import org.netbeans.api.io.ShowOperation;
 import 
org.netbeans.modules.java.lsp.server.ui.AbstractLspInputOutputProvider.LspIO;
 import org.netbeans.spi.io.InputOutputProvider;
-import org.openide.util.Exceptions;
 import org.openide.util.Lookup;
 
 public abstract class AbstractLspInputOutputProvider implements 
InputOutputProvider<LspIO, PrintWriter, Void, Void> {
@@ -145,7 +144,14 @@ public abstract class AbstractLspInputOutputProvider 
implements InputOutputProvi
             this.err = new PrintWriter(new LspWriter(false));
             Reader in;
             try {
-                in = new InputStreamReader(ioCtx.getStdIn(), "UTF-8");
+                in = new InputStreamReader(ioCtx.getStdIn(), "UTF-8") {
+                    @Override
+                    public void close() throws IOException {
+                        // the underlying StreamDecoder would just block on 
synchronized read(); close the underlying stream.
+                        ioCtx.getStdIn().close();
+                        super.close();
+                    }
+                };
             } catch (IOException ex) {
                 err.write(ex.getLocalizedMessage());
                 in = new CharArrayReader(new char[0]) {
diff --git 
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsoleTest.java
 
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsoleTest.java
new file mode 100644
index 0000000..7f7fd85
--- /dev/null
+++ 
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/debugging/launch/NbProcessConsoleTest.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.debugging.launch;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.netbeans.modules.java.lsp.server.ui.AbstractLspInputOutputProvider;
+import org.netbeans.modules.java.lsp.server.ui.LspIOAccessor;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author sdedic
+ */
+public class NbProcessConsoleTest {
+    NbProcessConsole console = new NbProcessConsole(new 
Consumer<NbProcessConsole.ConsoleMessage>() {
+        @Override
+        public void accept(NbProcessConsole.ConsoleMessage t) {
+        }
+    });
+    
+    @Test
+    public void testConsoleClose() throws Exception {
+        InputStream sin = console.getStdIn();
+        BufferedReader rdr = new BufferedReader(new InputStreamReader(sin, 
"UTF-8"));
+        assertReaderClosed(rdr);
+    }
+    
+    void assertReaderClosed(BufferedReader rdr) throws IOException {
+        InputStream sin = console.getStdIn();
+        ScheduledExecutorService scheduler = 
Executors.newSingleThreadScheduledExecutor();
+        AtomicBoolean read1 = new AtomicBoolean(false);
+        
+        // do not wait > 5 secs, abort
+        scheduler.schedule(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                if (!read1.get()) {
+                    System.err.println("CLOSED!");
+                    sin.close();
+                }
+                return null;
+            }
+        }, 5, TimeUnit.SECONDS);
+
+        scheduler.schedule(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                console.stdIn("Hello, world!");
+                return null;
+            }
+        }, 100, TimeUnit.MILLISECONDS);
+
+        assertEquals("Hello, world!", rdr.readLine());
+        read1.set(true);
+        
+        console.stdIn("Still there");
+        assertEquals("Still there", rdr.readLine());
+        
+        // do not wait > 5 secs, abort
+        scheduler.schedule(new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                // close asynchronously
+                sin.close();
+                return null;
+            }
+        }, 300, TimeUnit.MILLISECONDS);
+
+        long millis = System.currentTimeMillis();
+        assertNull(rdr.readLine());
+        long millis2 = System.currentTimeMillis();
+        
+        assertTrue("Close should be delayed.", millis2 - millis >= 300);
+    }
+    
+    @Test
+    public void testCloseLspIOContextInput() throws Exception {
+        AbstractLspInputOutputProvider.LspIO io = 
LspIOAccessor.createIO("test", console, Lookup.EMPTY);
+        
+        Reader r = LspIOAccessor.reader(io);
+        BufferedReader rdr = new BufferedReader(r);
+        assertReaderClosed(rdr);
+    }
+}
diff --git 
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/LspIOAccessor.java
 
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/LspIOAccessor.java
new file mode 100644
index 0000000..b74e62c
--- /dev/null
+++ 
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/ui/LspIOAccessor.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.java.lsp.server.ui;
+
+import java.io.Reader;
+import org.openide.util.Lookup;
+
+/**
+ *
+ * @author sdedic
+ */
+public class LspIOAccessor {
+    public static Reader reader(AbstractLspInputOutputProvider.LspIO io) {
+        return io.in;
+    }
+    
+    public static AbstractLspInputOutputProvider.LspIO createIO(String name, 
IOContext ioCtx, Lookup lookup) {
+        return new AbstractLspInputOutputProvider.LspIO(name, ioCtx, lookup);
+    }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

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

Reply via email to