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 <[email protected]>
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: [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