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 b4fef94  Workspace shared IO supports close on input stream.
     new e9d9555  Merge pull request #3019 from sdedic/lsp/deadlock-workspaceio
b4fef94 is described below

commit b4fef94cff545e4e52c790b8bfec396e64850674
Author: Svata Dedic <svatopluk.de...@oracle.com>
AuthorDate: Fri Jun 25 13:24:29 2021 +0200

    Workspace shared IO supports close on input stream.
---
 .../lsp/server/protocol/WorkspaceIOContext.java    | 32 ++++++++++---
 .../server/ui/AbstractLspInputOutputProvider.java  |  6 ++-
 .../lsp/server/protocol/WorkspaceContextTest.java  | 56 ++++++++++++++++++----
 3 files changed, 76 insertions(+), 18 deletions(-)

diff --git 
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceIOContext.java
 
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceIOContext.java
index e0a3bc5..0de30b1 100644
--- 
a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceIOContext.java
+++ 
b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceIOContext.java
@@ -26,8 +26,6 @@ import org.eclipse.lsp4j.services.LanguageClient;
 import org.netbeans.modules.java.lsp.server.ui.IOContext;
 
 abstract class WorkspaceIOContext extends IOContext {
-    private final InputStream inputSink = new EmptyBlockingInputStream();
-    
     WorkspaceIOContext() {
     }
 
@@ -38,7 +36,7 @@ abstract class WorkspaceIOContext extends IOContext {
 
     @Override
     protected InputStream getStdIn() throws IOException {
-        return inputSink;
+        return new EmptyBlockingInputStream();
     }
 
     @Override
@@ -68,20 +66,40 @@ abstract class WorkspaceIOContext extends IOContext {
 
     protected abstract LanguageClient client();
     
+    /**
+     * This should mimic Streams provided by core.output2 module; those 
InputStreams
+     * support asynchronous close() without blocking, but close only 
temporarilyl the 
+     * first reader gets -1, then the stream resets internally and is ready to 
be read
+     * again (i.e. reused tab).
+     */
     private static class EmptyBlockingInputStream extends InputStream {
+        private boolean closed = false;
+        
         @Override
         public int read() throws IOException {
             synchronized (this) {
-                try {
-                    wait();
-                } catch (InterruptedException ex) {
-                    throw new IOException(ex);
+                if (!closed) {
+                    try {
+                        wait();
+                    } catch (InterruptedException ex) {
+                        throw new IOException(ex);
+                    }
                 }
+                closed = false;
             }
             return -1;
         }
 
         @Override
+        public void close() throws IOException {
+            synchronized (this) {
+                closed = true;
+                notifyAll();
+            }
+            return;
+        }
+        
+        @Override
         public boolean markSupported() {
             return false;
         }
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 e128015..e5a54f6 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
@@ -20,6 +20,7 @@ package org.netbeans.modules.java.lsp.server.ui;
 
 import java.io.CharArrayReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.Reader;
@@ -144,11 +145,12 @@ public abstract class AbstractLspInputOutputProvider 
implements InputOutputProvi
             this.err = new PrintWriter(new LspWriter(false));
             Reader in;
             try {
-                in = new InputStreamReader(ioCtx.getStdIn(), "UTF-8") {
+                InputStream is = ioCtx.getStdIn();
+                in = new InputStreamReader(is, "UTF-8") {
                     @Override
                     public void close() throws IOException {
                         // the underlying StreamDecoder would just block on 
synchronized read(); close the underlying stream.
-                        ioCtx.getStdIn().close();
+                        is.close();
                         super.close();
                     }
                 };
diff --git 
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceContextTest.java
 
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceContextTest.java
index a0ec069..7012941 100644
--- 
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceContextTest.java
+++ 
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/WorkspaceContextTest.java
@@ -18,25 +18,25 @@
  */
 package org.netbeans.modules.java.lsp.server.protocol;
 
+import java.io.Reader;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CompletableFuture;
-import org.eclipse.lsp4j.DidChangeConfigurationParams;
-import org.eclipse.lsp4j.DidChangeWatchedFilesParams;
-import org.eclipse.lsp4j.ExecuteCommandParams;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 import org.eclipse.lsp4j.MessageActionItem;
 import org.eclipse.lsp4j.MessageParams;
 import org.eclipse.lsp4j.PublishDiagnosticsParams;
 import org.eclipse.lsp4j.ShowMessageRequestParams;
-import org.eclipse.lsp4j.SymbolInformation;
-import org.eclipse.lsp4j.WorkspaceSymbolParams;
 import org.eclipse.lsp4j.services.LanguageClient;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import static org.junit.Assert.*;
+import org.netbeans.modules.java.lsp.server.ui.AbstractLspInputOutputProvider;
+import 
org.netbeans.modules.java.lsp.server.ui.AbstractLspInputOutputProvider.LspIO;
+import org.netbeans.modules.nbcode.integration.LspInputOutputProvider;
+import org.openide.util.Lookup;
+import org.openide.util.test.MockLookup;
 
 public class WorkspaceContextTest {
 
@@ -64,6 +64,44 @@ public class WorkspaceContextTest {
         assertEquals("ahoj", msgs.get(0).getMessage());
         assertEquals("there!", msgs.get(1).getMessage());
     }
+    
+    /**
+     * WorkspaceIOContext is a dead input, but must allow to be close()d, 
returning -1
+     * from its read().
+     */
+    @Test
+    public void testReadDoesntBlockClose() throws Exception {
+        List<MessageParams> msgs = new ArrayList<>();
+        MockLanguageClient mlc = new MockLanguageClient(msgs);
+        WorkspaceIOContext wc = new WorkspaceIOContext() {
+            @Override
+            protected LanguageClient client() {
+                return mlc;
+            }
+        };
+        
+        //LspIO io = LspIOAccessor.createIO("Test", wc, Lookup.EMPTY);
+        MockLookup.setInstances(wc);
+        AbstractLspInputOutputProvider ioProvider = new 
LspInputOutputProvider();
+        LspIO lspIo = ioProvider.getIO("Test", true, Lookup.EMPTY);
+
+        Reader inReader = ioProvider.getIn(lspIo); 
+        CountDownLatch closeLatch = new CountDownLatch(1);
+        final Thread readerThread = Thread.currentThread();
+        Executors.newSingleThreadScheduledExecutor().schedule(() -> {
+            inReader.close();
+            closeLatch.countDown();
+            return null;
+        }, 300, TimeUnit.MILLISECONDS);
+        
+        Executors.newSingleThreadScheduledExecutor().schedule(() -> {
+            readerThread.interrupt();
+        }, 1000, TimeUnit.MILLISECONDS);
+        int r = inReader.read();
+        
+        assert r == -1;
+        assertTrue(closeLatch.await(500, TimeUnit.MILLISECONDS));
+    }
 
     private static final class MockLanguageClient implements LanguageClient {
         private final List<MessageParams> messages;

---------------------------------------------------------------------
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