diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java index 511dd77305c..e4ad7855bf6 100644 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.text.DocumentEvent; @@ -50,6 +51,203 @@ */ abstract public class AbstractReconciler implements IReconciler { + + /** + * Background thread for the reconciling activity. + */ + class BackgroundThread extends Thread { + + /** Has the reconciler been canceled. */ + private boolean fCanceled= false; + /** Has the reconciler been reset. */ + private boolean fReset= false; + /** Some changes need to be processed. */ + private boolean fIsDirty= false; + /** Is a reconciling strategy active. */ + private boolean fIsActive= false; + + private boolean fStarted; + + /** + * Creates a new background thread. The thread + * runs with minimal priority. + * + * @param name the thread's name + */ + public BackgroundThread(String name) { + super(name); + setPriority(Thread.MIN_PRIORITY); + setDaemon(true); + } + + /** + * Returns whether a reconciling strategy is active right now. + * + * @return true if a activity is active + */ + public boolean isActive() { + return fIsActive; + } + + /** + * Returns whether some changes need to be processed. + * + * @return true if changes wait to be processed + * @since 3.0 + */ + public synchronized boolean isDirty() { + return fIsDirty; + } + + /** + * Cancels the background thread. + */ + public void cancel() { + fCanceled= true; + IProgressMonitor pm= fProgressMonitor; + if (pm != null) + pm.setCanceled(true); + synchronized (fDirtyRegionQueue) { + fDirtyRegionQueue.notifyAll(); + } + } + + /** + * Suspends the caller of this method until this background thread has + * emptied the dirty region queue. + */ + public void suspendCallerWhileDirty() { + AbstractReconciler.this.signalWaitForFinish(); + boolean isDirty; + do { + synchronized (fDirtyRegionQueue) { + isDirty= fDirtyRegionQueue.getSize() > 0; + if (isDirty) { + try { + fDirtyRegionQueue.wait(); + } catch (InterruptedException x) { + } + } + } + } while (isDirty); + } + + /** + * Reset the background thread as the text viewer has been changed, + */ + public void reset() { + + if (fDelay > 0) { + + synchronized (this) { + fIsDirty= true; + fReset= true; + } + synchronized (fDirtyRegionQueue) { + fDirtyRegionQueue.notifyAll(); // wake up wait(fDelay); + } + + } else { + + synchronized (this) { + fIsDirty= true; + } + + synchronized (fDirtyRegionQueue) { + fDirtyRegionQueue.notifyAll(); + } + } + + informNotFinished(); + reconcilerReset(); + } + + /** + * The background activity. Waits until there is something in the + * queue managing the changes that have been applied to the text viewer. + * Removes the first change from the queue and process it. + *

+ * Calls {@link AbstractReconciler#initialProcess()} on entrance. + *

+ */ + @Override + public void run() { + + delay(); + + if (fCanceled) + return; + + initialProcess(); + + while (!fCanceled) { + + delay(); + + if (fCanceled) + break; + + if (!isDirty()) { + waitFinish= false; //signalWaitForFinish() was called but nothing todo + continue; + } + + synchronized (this) { + if (fReset) { + fReset= false; + continue; + } + } + + DirtyRegion r= null; + synchronized (fDirtyRegionQueue) { + r= fDirtyRegionQueue.removeNextDirtyRegion(); + } + + fIsActive= true; + + fProgressMonitor.setCanceled(false); + + process(r); + + synchronized (fDirtyRegionQueue) { + if (0 == fDirtyRegionQueue.getSize()) { + synchronized (this) { + fIsDirty= fProgressMonitor.isCanceled(); + } + fDirtyRegionQueue.notifyAll(); + } + } + + fIsActive= false; + } + } + + public void startReconciling() { + if (!isAlive()) { + if (fStarted) { + return; + } + fStarted= true; + Job.createSystem("Delayed Reconciler startup", m -> { //$NON-NLS-1$ + try { + start(); + return Status.OK_STATUS; + } catch (IllegalThreadStateException e) { + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=40549 + // This is the only instance where the thread is started; since + // we checked that it is not alive, it must be dead already due + // to a run-time exception or error. Exit. + return Status.CANCEL_STATUS; + } + }).schedule(); + } else { + reset(); + } + + } + } + /** * Internal document listener and text input listener. */ @@ -63,7 +261,7 @@ public void documentAboutToBeChanged(DocumentEvent e) { public void documentChanged(DocumentEvent e) { if (fThread.isActive() || !fThread.isDirty() && fThread.isAlive()) { - if (!fIsAllowedToModifyDocument && isRunningInReconcilerThread()) + if (!fIsAllowedToModifyDocument && Thread.currentThread() == fThread) throw new UnsupportedOperationException("The reconciler thread is not allowed to modify the document"); //$NON-NLS-1$ aboutToBeReconciledInternal(); } @@ -126,14 +324,13 @@ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { } /** Queue to manage the changes applied to the text viewer. */ - DirtyRegionQueue fDirtyRegionQueue; + private DirtyRegionQueue fDirtyRegionQueue; /** The background thread. */ - private ReconcilerJob fThread; + private BackgroundThread fThread; /** Internal document and text input listener. */ private Listener fListener; - /** The background thread delay. */ - int fDelay= 500; + private int fDelay= 500; /** Signal that the the background thread should not delay. */ volatile boolean waitFinish; /** Are there incremental reconciling strategies? */ @@ -280,7 +477,7 @@ public void install(ITextViewer textViewer) { synchronized (this) { if (fThread != null) return; - fThread= new ReconcilerJob(getClass().getName(), this); + fThread= new BackgroundThread(getClass().getName()); } fDirtyRegionQueue= new DirtyRegionQueue(); @@ -314,9 +511,9 @@ public void uninstall() { synchronized (this) { // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135 - ReconcilerJob bt= fThread; + BackgroundThread bt= fThread; fThread= null; - bt.doCancel(); + bt.cancel(); } } } @@ -383,7 +580,7 @@ public void signalWaitForFinish() { } } - void informNotFinished() { + private void informNotFinished() { waitFinish= false; aboutToWork(); } @@ -394,7 +591,7 @@ private void aboutToBeReconciledInternal() { } - void delay() { + private void delay() { synchronized (fDirtyRegionQueue) { if (waitFinish) { return; // do not delay when waiting; @@ -444,11 +641,7 @@ protected synchronized void startReconciling() { if (fThread == null) return; - if (!fThread.isAlive()) { - fThread.start(); - } else { - fThread.reset(); - } + fThread.startReconciling(); } /** @@ -464,10 +657,7 @@ protected void reconcilerReset() { * @return true if running in this reconciler's background thread * @since 3.4 */ - protected synchronized boolean isRunningInReconcilerThread() { - if (fThread == null) { - return false; - } - return Job.getJobManager().currentJob() == fThread; + protected boolean isRunningInReconcilerThread() { + return Thread.currentThread() == fThread; } -} \ No newline at end of file +} diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/ReconcilerJob.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/ReconcilerJob.java deleted file mode 100644 index f9c7db1e4bd..00000000000 --- a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/ReconcilerJob.java +++ /dev/null @@ -1,220 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2025 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - * Christoph Läubrich - extract into own class - *******************************************************************************/ -package org.eclipse.jface.text.reconciler; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; - -/** - * Background thread for the reconciling activity. - */ -class ReconcilerJob extends Job { - - /** Has the reconciler been canceled. */ - private boolean fCanceled= false; - - /** Has the reconciler been reset. */ - private boolean fReset= false; - - /** Some changes need to be processed. */ - private boolean fIsDirty= false; - - /** Is a reconciling strategy active. */ - private boolean fIsActive= false; - - private volatile boolean fIsAlive; - - private boolean started; - - private AbstractReconciler fReconciler; - - /** - * Creates a new background thread. The thread runs with minimal priority. - * - * @param name the thread's name - */ - public ReconcilerJob(String name, AbstractReconciler reconciler) { - super(name); - fReconciler= reconciler; - setPriority(Job.DECORATE); - setSystem(true); - } - - /** - * Returns whether a reconciling strategy is active right now. - * - * @return true if a activity is active - */ - public boolean isActive() { - return fIsActive; - } - - /** - * Returns whether some changes need to be processed. - * - * @return true if changes wait to be processed - * @since 3.0 - */ - public synchronized boolean isDirty() { - return fIsDirty; - } - - /** - * Cancels the background thread. - */ - public void doCancel() { - fCanceled= true; - IProgressMonitor pm= fReconciler.getProgressMonitor(); - if (pm != null) - pm.setCanceled(true); - synchronized (fReconciler.fDirtyRegionQueue) { - fReconciler.fDirtyRegionQueue.notifyAll(); - } - } - - /** - * Suspends the caller of this method until this background thread has emptied the dirty region - * queue. - */ - public void suspendCallerWhileDirty() { - fReconciler.signalWaitForFinish(); - boolean isDirty; - do { - synchronized (fReconciler.fDirtyRegionQueue) { - isDirty= fReconciler.fDirtyRegionQueue.getSize() > 0; - if (isDirty) { - try { - fReconciler.fDirtyRegionQueue.wait(); - } catch (InterruptedException x) { - } - } - } - } while (isDirty); - } - - /** - * Reset the background thread as the text viewer has been changed, - */ - public void reset() { - - if (fReconciler.fDelay > 0) { - - synchronized (this) { - fIsDirty= true; - fReset= true; - } - synchronized (fReconciler.fDirtyRegionQueue) { - fReconciler.fDirtyRegionQueue.notifyAll(); // wake up wait(fDelay); - } - - } else { - - synchronized (this) { - fIsDirty= true; - } - - synchronized (fReconciler.fDirtyRegionQueue) { - fReconciler.fDirtyRegionQueue.notifyAll(); - } - } - synchronized (this) { - started= false; - } - fReconciler.informNotFinished(); - fReconciler.reconcilerReset(); - } - - /** - * The background activity. Waits until there is something in the queue managing the changes - * that have been applied to the text viewer. Removes the first change from the queue and - * process it. - *

- * Calls {@link AbstractReconciler#initialProcess()} on entrance. - *

- */ - @Override - public IStatus run(IProgressMonitor monitor) { - fIsAlive= true; - fReconciler.delay(); - - if (fCanceled) - return Status.CANCEL_STATUS; - - fReconciler.initialProcess(); - - while (!fCanceled) { - - fReconciler.delay(); - - if (fCanceled) - break; - - if (!isDirty()) { - fReconciler.waitFinish= false; //signalWaitForFinish() was called but nothing todo - continue; - } - - synchronized (this) { - if (fReset) { - fReset= false; - continue; - } - } - - DirtyRegion r= null; - synchronized (fReconciler.fDirtyRegionQueue) { - r= fReconciler.fDirtyRegionQueue.removeNextDirtyRegion(); - } - - fIsActive= true; - - fReconciler.getProgressMonitor().setCanceled(false); - - fReconciler.process(r); - - synchronized (fReconciler.fDirtyRegionQueue) { - if (0 == fReconciler.fDirtyRegionQueue.getSize()) { - synchronized (this) { - fIsDirty= fReconciler.getProgressMonitor().isCanceled(); - } - fReconciler.fDirtyRegionQueue.notifyAll(); - } - } - - fIsActive= false; - } - fIsAlive= false; - return Status.OK_STATUS; - } - - public boolean isAlive() { - return fIsAlive; - } - - public synchronized void start() { - if (!started) { - started= true; - schedule(); - } - } - - @Override - public boolean belongsTo(Object family) { - return family == fReconciler.getTextViewer() || AbstractReconciler.class == family; - } - -}