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