Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Pazderski2019-03-27 08:15:08 -0400
committerPaul Pazderski2019-11-16 15:38:07 -0500
commit0fcbf89cc35b852138f2fe7750773bb9eb59457e (patch)
tree15554f9c580110a791b1ce98ab50578c50d4038b
parent2e6899ce766c03ac6e36f6579f75d0867abfc49b (diff)
downloadeclipse.platform.debug-0fcbf89cc35b852138f2fe7750773bb9eb59457e.tar.gz
eclipse.platform.debug-0fcbf89cc35b852138f2fe7750773bb9eb59457e.tar.xz
eclipse.platform.debug-0fcbf89cc35b852138f2fe7750773bb9eb59457e.zip
Bug 551745 - [console] Revise IOConsolePartitioner output appending andI20191118-0600
initialization Remove, rename or rework some fields and simplify how stream output is appended to document. (without losing existing functionality) Second try. This time with deadlock protection. Change-Id: I8d80ade8bb521895c97d6e08d2f520dd9b182a3b Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
-rw-r--r--org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java76
-rw-r--r--org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java4
-rw-r--r--org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java475
3 files changed, 313 insertions, 242 deletions
diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java
index dd2000786..77e6172fb 100644
--- a/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java
+++ b/org.eclipse.ui.console/src/org/eclipse/ui/console/IOConsole.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.IDocument;
import org.eclipse.ui.WorkbenchEncoding;
import org.eclipse.ui.internal.console.IOConsolePage;
import org.eclipse.ui.internal.console.IOConsolePartitioner;
@@ -42,7 +43,7 @@ public class IOConsole extends TextConsole {
/**
* The document partitioner
*/
- private IOConsolePartitioner partitioner;
+ private final IOConsolePartitioner partitioner;
/**
* The stream from which user input may be read
@@ -52,12 +53,12 @@ public class IOConsole extends TextConsole {
/**
* A collection of open streams connected to this console.
*/
- private List<Closeable> openStreams = Collections.synchronizedList(new ArrayList<>());
+ private final List<Closeable> openStreams = Collections.synchronizedList(new ArrayList<>());
/**
* The encoding used to for displaying console output.
*/
- private Charset charset;
+ private final Charset charset;
/**
@@ -115,10 +116,11 @@ public class IOConsole extends TextConsole {
inputStream = new IOConsoleInputStream(this);
openStreams.add(inputStream);
}
-
- if (inputStream instanceof IOConsoleInputStream) {
- partitioner = new IOConsolePartitioner((IOConsoleInputStream) inputStream, this);
- partitioner.connect(getDocument());
+ partitioner = new IOConsolePartitioner(this);
+ final IDocument document = getDocument();
+ if (document != null) {
+ partitioner.connect(document);
+ document.setDocumentPartitioner(partitioner);
}
}
@@ -174,6 +176,12 @@ public class IOConsole extends TextConsole {
/**
* Returns the input stream connected to the keyboard.
+ * <p>
+ * Note: It returns the stream connected to keyboard. There is no guarantee to
+ * get the stream last set with {@link #setInputStream(InputStream)}. The return
+ * value might be <code>null</code> if the current input stream is not connected
+ * to the keyboard.
+ * </p>
*
* @return the input stream connected to the keyboard.
*/
@@ -283,9 +291,7 @@ public class IOConsole extends TextConsole {
@Override
public void clearConsole() {
- if (partitioner != null) {
- partitioner.clearBuffer();
- }
+ partitioner.clearBuffer();
}
/**
@@ -293,28 +299,38 @@ public class IOConsole extends TextConsole {
*/
@Override
protected void dispose() {
- super.dispose();
- partitioner.disconnect();
- //make a copy of the open streams and close them all
- //a copy is needed as close the streams results in a callback that
- //removes the streams from the openStreams collection (bug 152794)
- List<Closeable> list = new ArrayList<>(openStreams);
- for (Closeable closable : list) {
- try {
- closable.close();
- } catch (IOException e) {
- // e.printStackTrace();
+ // Get lock on ourself before closing. Closing streams check for console finish.
+ // Finish check need lock on (this) console. Since closing also lock on stream
+ // this can deadlock with other threads (double) closing streams at same time
+ // but already own a lock on console. (bug 551902)
+ // Long story short. Before closing IOConsole...Stream get lock from associated
+ // console to prevent deadlocks.
+ synchronized (this) {
+ // make a copy of the open streams and close them all
+ // a copy is needed as close the streams results in a callback that
+ // removes the streams from the openStreams collection (bug 152794)
+ List<Closeable> list = new ArrayList<>(openStreams);
+ for (Closeable closable : list) {
+ try {
+ closable.close();
+ } catch (IOException e) {
+ // e.printStackTrace();
+ }
}
+ inputStream = null;
}
- inputStream = null;
+
+ final IDocument document = partitioner.getDocument();
+ document.setDocumentPartitioner(null);
+ partitioner.disconnect();
+
+ super.dispose();
}
/**
- * Returns the encoding for this console, or <code>null</code> to indicate
- * default encoding.
+ * Returns the encoding for this console.
*
- * @return the encoding set for this console, or <code>null</code> to indicate
- * default encoding
+ * @return the encoding set for this console
* @since 3.3
*/
public String getEncoding() {
@@ -322,11 +338,9 @@ public class IOConsole extends TextConsole {
}
/**
- * Returns the Charset for this console, or <code>null</code> to indicate
- * default encoding.
+ * Returns the Charset for this console.
*
- * @return the Charset for this console, or <code>null</code> to indicate
- * default encoding
+ * @return the Charset for this console
* @since 3.7
*/
public Charset getCharset() {
diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java
index 3f43137d0..34ffa88c3 100644
--- a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java
+++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartition.java
@@ -160,7 +160,7 @@ public class IOConsolePartition implements ITypedRegion {
*/
private int getFontStyle() {
if (type.equals(INPUT_PARTITION_TYPE)) {
- return inputStream.getFontStyle();
+ return inputStream != null ? inputStream.getFontStyle() : 0;
}
return outputStream.getFontStyle();
}
@@ -174,7 +174,7 @@ public class IOConsolePartition implements ITypedRegion {
*/
public Color getColor() {
if (type.equals(INPUT_PARTITION_TYPE)) {
- return inputStream.getColor();
+ return inputStream != null ? inputStream.getColor() : null;
}
return outputStream.getColor();
}
diff --git a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java
index 79dd58115..477e7a16b 100644
--- a/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java
+++ b/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/IOConsolePartitioner.java
@@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -59,6 +60,25 @@ import org.eclipse.ui.progress.WorkbenchJob;
public class IOConsolePartitioner
implements IConsoleDocumentPartitioner, IConsoleDocumentPartitionerExtension, IDocumentPartitionerExtension {
/**
+ * Enumeration used to distinct sources of document updates. (especially to
+ * distinct updates triggered by this partitioner from other document changes)
+ */
+ private enum DocUpdateType {
+ /**
+ * Default if reason for document change is not known. Document change is
+ * interpreted as user input.
+ */
+ INPUT,
+ /**
+ * Document update was triggered from this partitioner by appending content
+ * received from output streams.
+ */
+ OUTPUT,
+ /** Document update was triggered from this partitioner's {@link TrimJob}. */
+ TRIM,
+ }
+
+ /**
* If true validate partitioning after changes and do other additional
* assertions. Useful for developing/debugging.
*/
@@ -69,7 +89,6 @@ public class IOConsolePartitioner
*/
private static final Comparator<IRegion> CMP_REGION_BY_OFFSET = Comparator.comparing(IRegion::getOffset);
- private PendingPartition consoleClosedPartition;
/** The connected {@link IDocument} this partitioner manages. */
private IDocument document;
/**
@@ -77,59 +96,60 @@ public class IOConsolePartitioner
* {@link IRegion#getOffset()} and not contain <code>null</code> or 0-length
* elements. (see also {@link #checkPartitions()})
*/
- private ArrayList<IOConsolePartition> partitions;
+ private final ArrayList<IOConsolePartition> partitions = new ArrayList<>();
/** Blocks of data that have not yet been appended to the document. */
- private ArrayList<PendingPartition> pendingPartitions;
- /**
- * A list of PendingPartitions to be appended by the updateJob
- */
- private ArrayList<PendingPartition> updatePartitions;
- /**
- * Job that appends pending partitions to the document.
- */
- private QueueProcessingJob queueJob;
+ private final ArrayList<PendingPartition> pendingPartitions = new ArrayList<>();
+ /** Total length of pending partitions content. */
+ private int pendingSize;
+ /** Job that appends pending partitions to the document. */
+ private final QueueProcessingJob queueJob = new QueueProcessingJob();
/** Job that trims console content if it exceeds {@link #highWaterMark}. */
- private TrimJob trimJob = new TrimJob();
- /** The input stream attached to this document. */
- private IOConsoleInputStream inputStream;
+ private final TrimJob trimJob = new TrimJob();
/**
- * Flag to indicate that the updateJob is updating the document.
+ * Reason for document update. Set before changing document inside this
+ * partitioner to prevent that change is interpreted as user input.
+ * <p>
+ * Automatically reset to {@link DocUpdateType#INPUT} after every document
+ * change.
+ * </p>
*/
- private boolean updateInProgress;
+ private DocUpdateType updateType = DocUpdateType.INPUT;
+ private IRegion changedRegion;
/**
* A list of partitions containing input from the console, that have not been
* appended to the input stream yet. No guarantees on element order.
*/
private ArrayList<IOConsolePartition> inputPartitions;
/**
- * offset used by updateJob
- */
- private int firstOffset;
- /**
* A matcher to search for legal line delimiters in new input. Never
* <code>null</code> but match nothing if no document connected.
*/
private MultiStringMatcher legalLineDelimiterMatcher;
+ /**
+ * The high mark for console content trimming. If console content exceeds this
+ * length trimming is scheduled. Trimming is disabled if value is negative.
+ */
private int highWaterMark = -1;
private int lowWaterMark = -1;
- private boolean connected = false;
/** The partitioned {@link IOConsole}. */
private IOConsole console;
+ /** Set after console signaled that all streams are closed. */
+ private volatile boolean streamsClosed;
+
/**
- * Lock for appending to and removing from the document - used
- * to synchronize addition of new text/partitions in the update
- * job and handling buffer overflow/clearing of the console.
+ * Create new partitioner for an {@link IOConsole}.
+ * <p>
+ * The partitioner must be explicit {@link #connect(IDocument) connected} with
+ * the consoles {@link IDocument}.
+ * </p>
+ *
+ * @param console the partitioned console. Not <code>null</code>.
*/
- private Object overflowLock = new Object();
-
-
- private int fBuffer;
-
- public IOConsolePartitioner(IOConsoleInputStream inputStream, IOConsole console) {
- this.inputStream = inputStream;
- this.console = console;
+ public IOConsolePartitioner(IOConsole console) {
+ this.console = Objects.requireNonNull(console);
+ queueJob.setRule(console.getSchedulingRule());
trimJob.setRule(console.getSchedulingRule());
}
@@ -144,27 +164,63 @@ public class IOConsolePartitioner
@Override
public void connect(IDocument doc) {
- document = doc;
- document.setDocumentPartitioner(this);
- legalLineDelimiterMatcher = MultiStringMatcher.create(document.getLegalLineDelimiters());
- partitions = new ArrayList<>();
- pendingPartitions = new ArrayList<>();
- inputPartitions = new ArrayList<>();
- queueJob = new QueueProcessingJob();
- queueJob.setSystem(true);
- queueJob.setPriority(Job.INTERACTIVE);
- queueJob.setRule(console.getSchedulingRule());
- connected = true;
+ if (doc == document) {
+ return;
+ }
+ disconnect();
+ if (doc != null) {
+ synchronized (partitions) {
+ inputPartitions = new ArrayList<>();
+ document = doc;
+ legalLineDelimiterMatcher = MultiStringMatcher.create(document.getLegalLineDelimiters());
+ }
+ }
+ }
+
+ @Override
+ public void disconnect() {
+ synchronized (pendingPartitions) {
+ pendingPartitions.clear();
+ pendingSize = 0;
+ pendingPartitions.notifyAll();
+ }
+ synchronized (partitions) {
+ trimJob.cancel();
+ queueJob.cancel();
+ legalLineDelimiterMatcher = null;
+ document = null;
+ inputPartitions = null;
+ partitions.clear();
+ }
}
+ /**
+ * Get high water mark.
+ *
+ * @return the trim if exceeded mark
+ * @see IOConsole#getHighWaterMark()
+ */
public int getHighWaterMark() {
return highWaterMark;
}
+ /**
+ * Get low water mark.
+ *
+ * @return the trim to this length mark
+ * @see IOConsole#getLowWaterMark()
+ */
public int getLowWaterMark() {
return lowWaterMark;
}
+ /**
+ * Set low and high water marks.
+ *
+ * @param low the trim to this length mark
+ * @param high the trim if exceeded mark
+ * @see IOConsole#setWaterMarks(int, int)
+ */
public void setWaterMarks(int low, int high) {
lowWaterMark = low;
highWaterMark = high;
@@ -175,22 +231,30 @@ public class IOConsolePartitioner
* Notification from the console that all of its streams have been closed.
*/
public void streamsClosed() {
- consoleClosedPartition = new PendingPartition(null, null);
- synchronized (pendingPartitions) {
- pendingPartitions.add(consoleClosedPartition);
+ if (streamsClosed) {
+ log(IStatus.ERROR, "Streams are already closed."); //$NON-NLS-1$
+ return;
}
- queueJob.schedule(); //ensure that all pending partitions are processed.
+ streamsClosed = true;
+ checkFinished();
}
- @Override
- public void disconnect() {
- synchronized (overflowLock) {
- document = null;
- partitions.clear();
- connected = false;
- try {
- inputStream.close();
- } catch (IOException e) {
+ /**
+ * Check if partitioner is finished and does not expect any new data appended to
+ * document.
+ */
+ private void checkFinished() {
+ if (streamsClosed) {
+ // do not expect new data since all streams are closed
+ // check if pending data is queued
+ final boolean morePending;
+ synchronized (pendingPartitions) {
+ morePending = !pendingPartitions.isEmpty();
+ }
+ if (morePending) {
+ queueJob.schedule();
+ } else {
+ console.partitionerFinished();
}
}
}
@@ -320,15 +384,17 @@ public class IOConsolePartitioner
/**
* Enforces the buffer size.
+ * <p>
* When the number of lines in the document exceeds the high water mark, the
- * beginning of the document is trimmed until the number of lines equals the
- * low water mark.
+ * beginning of the document is trimmed until the number of lines equals the low
+ * water mark.
+ * </p>
*/
private void checkBufferSize() {
if (document != null && highWaterMark > 0) {
int length = document.getLength();
if (length > highWaterMark) {
- if (trimJob.getState() == Job.NONE) { //if the job isn't already running
+ if (trimJob.getState() == Job.NONE) { // if the job isn't already running
trimJob.setOffset(length - lowWaterMark);
trimJob.schedule();
}
@@ -337,57 +403,50 @@ public class IOConsolePartitioner
}
/**
- * Clears the console
+ * Clears the console content.
*/
public void clearBuffer() {
- synchronized (overflowLock) {
- trimJob.setOffset(-1);
- trimJob.schedule();
- }
+ trimJob.setOffset(-1);
+ trimJob.schedule();
}
@Override
public IRegion documentChanged2(DocumentEvent event) {
- if (document == null) {
- return null; //another thread disconnected the partitioner
- }
- if (document.getLength() == 0) { // document cleared
- synchronized (partitions) {
- partitions.clear();
- inputPartitions.clear();
- }
- return new Region(0, 0);
- }
-
- if (updateInProgress) {
- synchronized(partitions) {
- if (updatePartitions != null) {
- IOConsolePartition lastPartition = getPartitionByIndex(partitions.size() - 1);
- for (PendingPartition pp : updatePartitions) {
- if (pp == consoleClosedPartition) {
- continue;
- }
-
- int ppLen = pp.text.length();
- if (lastPartition != null && lastPartition.getOutputStream() == pp.stream) {
- int len = lastPartition.getLength();
- lastPartition.setLength(len + ppLen);
- } else {
- IOConsolePartition partition = new IOConsolePartition(firstOffset, pp.stream);
- partition.setLength(ppLen);
- lastPartition = partition;
- partitions.add(partition);
- }
- firstOffset += ppLen;
- }
+ try {
+ if (document != event.getDocument()) {
+ log(IStatus.WARNING, "IOConsolePartitioner is connected to wrong document."); //$NON-NLS-1$
+ return null;
+ }
+ if (document.getLength() == 0) { // document cleared
+ synchronized (partitions) {
+ partitions.clear();
+ inputPartitions.clear();
}
+ return new Region(0, 0);
}
- } else {
+
synchronized (partitions) {
- return applyUserInput(event);
+ switch (updateType) {
+ case INPUT:
+ return applyUserInput(event);
+
+ // update and trim jobs are triggered by this partitioner and all partitioning
+ // changes are applied separately
+ case OUTPUT:
+ return changedRegion;
+ case TRIM:
+ return null; // trim does not change partition types
+
+ default:
+ log(IStatus.ERROR, "Invalid enum value " + updateType); //$NON-NLS-1$
+ return null;
+ }
}
+ } finally {
+ // always reset type since all change events not triggered by this partitioner
+ // are interpreted as user input
+ updateType = DocUpdateType.INPUT;
}
- return new Region(event.fOffset, event.fText.length());
}
/**
@@ -404,10 +463,14 @@ public class IOConsolePartitioner
* @return the region of the document in which the partition type changed or
* <code>null</code>
*/
+ // Required for a false 'resource not closed' warning on inputStream.
+ // This input stream must not be closed by this method.
+ @SuppressWarnings("resource")
private IRegion applyUserInput(DocumentEvent event) {
final int eventTextLength = event.getText() != null ? event.getText().length() : 0;
final int offset = event.getOffset();
final int amountDeleted = event.getLength();
+ final IOConsoleInputStream inputStream = console.getInputStream(); // do not close this stream
if (amountDeleted == 0 && eventTextLength == 0) {
// event did not changed document
@@ -484,7 +547,9 @@ public class IOConsolePartitioner
if (ASSERT) {
Assert.isTrue(inputLine.length() > 0);
}
- inputStream.appendData(inputLine.toString());
+ if (inputStream != null) {
+ inputStream.appendData(inputLine.toString());
+ }
}
Assert.isTrue(newTextOffset > textOffset); // can prevent infinity loop
textOffset = newTextOffset;
@@ -558,10 +623,6 @@ public class IOConsolePartitioner
return newPartition;
}
- private void setUpdateInProgress(boolean b) {
- updateInProgress = b;
- }
-
/**
* A stream has been appended, add to pendingPartions list and schedule
* updateJob. updateJob is scheduled with a slight delay, this allows the
@@ -576,31 +637,34 @@ public class IOConsolePartitioner
if (document == null) {
throw new IOException("Document is closed"); //$NON-NLS-1$
}
- synchronized(pendingPartitions) {
- PendingPartition last = pendingPartitions.size() > 0 ? pendingPartitions.get(pendingPartitions.size()-1) : null;
- if (last != null && last.stream == stream) {
- last.append(s);
+ if (s == null) {
+ return;
+ }
+ synchronized (pendingPartitions) {
+ final PendingPartition lastPending = pendingPartitions.size() > 0
+ ? pendingPartitions.get(pendingPartitions.size() - 1)
+ : null;
+ if (lastPending != null && lastPending.stream == stream) {
+ lastPending.append(s);
} else {
pendingPartitions.add(new PendingPartition(stream, s));
- if (fBuffer > 1000) {
- queueJob.schedule();
- } else {
- queueJob.schedule(50);
- }
}
- if (fBuffer > 160000) {
- if(Display.getCurrent() == null){
+ if (pendingSize > 1000) {
+ queueJob.schedule();
+ } else {
+ queueJob.schedule(50);
+ }
+
+ if (pendingSize > 160000) {
+ if (Display.getCurrent() == null) {
try {
pendingPartitions.wait();
} catch (InterruptedException e) {
}
} else {
- /*
- * if we are in UI thread we cannot lock it, so process
- * queued output.
- */
- processQueue();
+ // if we are in UI thread we cannot lock it, so process queued output.
+ processPendingPartitions();
}
}
}
@@ -615,30 +679,30 @@ public class IOConsolePartitioner
PendingPartition(IOConsoleOutputStream stream, String text) {
this.stream = stream;
- if (text != null) {
- append(text);
- }
+ append(text);
}
void append(String moreText) {
text.append(moreText);
- fBuffer += moreText.length();
+ pendingSize += moreText.length();
}
}
/**
- * Updates the document. Will append everything that is available before
- * finishing.
+ * Updates the document and partitioning structure. Will append everything
+ * received from output streams that is available before finishing.
*/
private class QueueProcessingJob extends UIJob {
QueueProcessingJob() {
super("IOConsole Updater"); //$NON-NLS-1$
+ setSystem(true);
+ setPriority(Job.INTERACTIVE);
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
- processQueue();
+ processPendingPartitions();
if (ASSERT) {
checkPartitions();
}
@@ -646,74 +710,67 @@ public class IOConsolePartitioner
}
/*
- * Job will process as much as it can each time it's run, but it gets
- * scheduled everytime a PendingPartition is added to the list, meaning
- * that this job could get scheduled unnecessarily in cases of heavy output.
- * Note however, that schedule() will only reschedule a running/scheduled Job
- * once even if it's called many times.
+ * Job will process as much as it can each time it's run, but it gets scheduled
+ * everytime a PendingPartition is added to the list, meaning that this job
+ * could get scheduled unnecessarily in cases of heavy output. Note however,
+ * that schedule() will only reschedule a running/scheduled Job once even if
+ * it's called many times.
*/
@Override
public boolean shouldRun() {
- boolean shouldRun = connected && pendingPartitions != null && pendingPartitions.size() > 0;
- return shouldRun;
- }
- }
-
- void processQueue() {
- synchronized (overflowLock) {
- ArrayList<PendingPartition> pendingCopy = new ArrayList<>();
- StringBuilder buffer = null;
- boolean consoleClosed = false;
- synchronized(pendingPartitions) {
- pendingCopy.addAll(pendingPartitions);
- pendingPartitions.clear();
- fBuffer = 0;
- pendingPartitions.notifyAll();
- }
- // determine buffer size
- int size = 0;
- for (PendingPartition pp : pendingCopy) {
- if (pp != consoleClosedPartition) {
- size+= pp.text.length();
- }
- }
- buffer = new StringBuilder(size);
- for (PendingPartition pp : pendingCopy) {
- if (pp != consoleClosedPartition) {
- buffer.append(pp.text);
- } else {
- consoleClosed = true;
- }
+ synchronized (pendingPartitions) {
+ final boolean shouldRun = pendingPartitions.size() > 0;
+ return shouldRun;
}
- if (connected) {
- setUpdateInProgress(true);
- updatePartitions = pendingCopy;
- firstOffset = document.getLength();
- try {
- if (buffer != null) {
- document.replace(firstOffset, 0, buffer.toString());
- }
- } catch (BadLocationException e) {
+ }
+ }
+
+ /**
+ * Process {@link #pendingPartitions}, append their content to document and
+ * update partitioning.
+ */
+ private void processPendingPartitions() {
+ final List<PendingPartition> pendingCopy;
+ final int size;
+ synchronized (pendingPartitions) {
+ pendingCopy = new ArrayList<>(pendingPartitions);
+ size = pendingSize;
+ pendingPartitions.clear();
+ pendingSize = 0;
+ pendingPartitions.notifyAll();
+ }
+ synchronized (partitions) {
+ final StringBuilder addedContent = new StringBuilder(size);
+ IOConsolePartition lastPartition = getPartitionByIndex(partitions.size() - 1);
+ int nextOffset = document.getLength();
+ for (PendingPartition pendingPartition : pendingCopy) {
+ if (lastPartition == null || lastPartition.getOutputStream() != pendingPartition.stream) {
+ lastPartition = new IOConsolePartition(nextOffset, pendingPartition.stream);
+ partitions.add(lastPartition);
}
- updatePartitions = null;
- setUpdateInProgress(false);
+ final int pendingLength = pendingPartition.text.length();
+ lastPartition.setLength(lastPartition.getLength() + pendingLength);
+ nextOffset += pendingLength;
+ addedContent.append(pendingPartition.text);
}
- if (consoleClosed) {
- console.partitionerFinished();
+ try {
+ updateType = DocUpdateType.OUTPUT;
+ document.replace(document.getLength(), 0, addedContent.toString());
+ } catch (BadLocationException e) {
+ log(e);
}
- checkBufferSize();
}
-
+ checkBufferSize();
+ checkFinished();
}
/**
- * Job to trim the console document, runs in the UI thread.
+ * Job to trim the console document, runs in the UI thread.
*/
private class TrimJob extends WorkbenchJob {
/**
- * trims output up to the line containing the given offset,
- * or all output if -1.
+ * Trims output up to the line containing the given offset, or all output if -1.
*/
private int truncateOffset;
@@ -736,34 +793,31 @@ public class IOConsolePartitioner
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
- if (document == null) {
- return Status.OK_STATUS;
- }
+ synchronized (partitions) {
+ if (document == null) {
+ return Status.OK_STATUS;
+ }
- int length = document.getLength();
- if (truncateOffset < length) {
- synchronized (overflowLock) {
+ int length = document.getLength();
+ if (truncateOffset < length) {
try {
if (truncateOffset < 0) {
// clear
- setUpdateInProgress(true);
+ updateType = DocUpdateType.TRIM;
document.set(""); //$NON-NLS-1$
- setUpdateInProgress(false);
} else {
// overflow
int cutoffLine = document.getLineOfOffset(truncateOffset);
int cutOffset = document.getLineOffset(cutoffLine);
-
// set the new length of the first partition
IOConsolePartition partition = (IOConsolePartition) getPartition(cutOffset);
partition.setLength(partition.getOffset() + partition.getLength() - cutOffset);
- setUpdateInProgress(true);
+ updateType = DocUpdateType.TRIM;
document.replace(0, cutOffset, ""); //$NON-NLS-1$
- setUpdateInProgress(false);
- //remove partitions and reset Partition offsets
+ // remove partitions and reset Partition offsets
int index = partitions.indexOf(partition);
for (int i = 0; i < index; i++) {
partitions.remove(0);
@@ -794,9 +848,6 @@ public class IOConsolePartitioner
@Override
public StyleRange[] getStyleRanges(int offset, int length) {
- if (!connected) {
- return new StyleRange[0];
- }
final IOConsolePartition[] computedPartitions = computeIOPartitioning(offset, length);
final StyleRange[] styles = new StyleRange[computedPartitions.length];
for (int i = 0; i < computedPartitions.length; i++) {
@@ -869,7 +920,7 @@ public class IOConsolePartitioner
@Override
public int getPreviousOffsetByState(int offset, boolean searchWritable) {
- if (partitions != null) {
+ synchronized (partitions) {
int partitionIndex = findPartitionCandidate(offset);
for (; partitionIndex >= 0; partitionIndex--) {
final IOConsolePartition partition = partitions.get(partitionIndex);
@@ -883,7 +934,7 @@ public class IOConsolePartitioner
@Override
public int getNextOffsetByState(int offset, boolean searchWritable) {
- if (partitions != null) {
+ synchronized (partitions) {
int partitionIndex = findPartitionCandidate(offset);
if (partitionIndex >= 0) {
for (; partitionIndex < partitions.size(); partitionIndex++) {
@@ -921,27 +972,33 @@ public class IOConsolePartitioner
ConsolePlugin.log(t);
}
+ private static void log(int status, String msg) {
+ ConsolePlugin.log(new Status(status, ConsolePlugin.getUniqueIdentifier(), msg));
+ }
+
/**
* For debug purpose. Check if whole document is partitioned, partitions are
* ordered by offset, every partition has length greater 0 and all writable
* input partitions are listed in {@link #inputPartitions}.
*/
private void checkPartitions() {
- if (!connected) {
+ if (document == null) {
return;
}
- final List<IOConsolePartition> knownInputPartitions = new ArrayList<>(inputPartitions);
- int offset = 0;
- for (IOConsolePartition partition : partitions) {
- Assert.isTrue(offset == partition.getOffset());
- Assert.isTrue(partition.getLength() > 0);
- offset += partition.getLength();
-
- if (isInputPartition(partition) && !partition.isReadOnly()) {
- Assert.isTrue(knownInputPartitions.remove(partition));
+ synchronized (partitions) {
+ final List<IOConsolePartition> knownInputPartitions = new ArrayList<>(inputPartitions);
+ int offset = 0;
+ for (IOConsolePartition partition : partitions) {
+ Assert.isTrue(offset == partition.getOffset());
+ Assert.isTrue(partition.getLength() > 0);
+ offset += partition.getLength();
+
+ if (isInputPartition(partition) && !partition.isReadOnly()) {
+ Assert.isTrue(knownInputPartitions.remove(partition));
+ }
}
+ Assert.isTrue(offset == document.getLength());
+ Assert.isTrue(knownInputPartitions.isEmpty());
}
- Assert.isTrue(offset == document.getLength());
- Assert.isTrue(knownInputPartitions.isEmpty());
}
}

Back to the top