Skip to content

Commit 0e4942b

Browse files
committed
Perform initial work in the job
Currently the first thing a reconciler after startup does is to sleep for a while then call initialProcess then start the processing loop (that sleeps again...). This now moves this initial work into the start job so we have a plain processing loop in the thread to make further improvements easier.
1 parent f41ea06 commit 0e4942b

File tree

3 files changed

+96
-86
lines changed

3 files changed

+96
-86
lines changed

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/AbstractReconciler.java

Lines changed: 91 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -55,29 +55,27 @@ abstract public class AbstractReconciler implements IReconciler {
5555
/**
5656
* Background thread for the reconciling activity.
5757
*/
58-
class BackgroundThread extends Thread {
58+
class BackgroundWorker implements Runnable {
5959

6060
/** Has the reconciler been canceled. */
61-
private boolean fCanceled= false;
61+
private boolean fCanceled;
6262
/** Has the reconciler been reset. */
63-
private boolean fReset= false;
63+
private boolean fReset;
6464
/** Some changes need to be processed. */
65-
private boolean fIsDirty= false;
65+
private boolean fIsDirty;
6666
/** Is a reconciling strategy active. */
67-
private boolean fIsActive= false;
67+
private boolean fIsActive;
6868

6969
private boolean fStarted;
7070

71-
/**
72-
* Creates a new background thread. The thread
73-
* runs with minimal priority.
74-
*
75-
* @param name the thread's name
76-
*/
77-
public BackgroundThread(String name) {
78-
super(name);
79-
setPriority(Thread.MIN_PRIORITY);
80-
setDaemon(true);
71+
private String fName;
72+
73+
private boolean fIsAlive;
74+
75+
private volatile Thread fThread;
76+
77+
public BackgroundWorker(String name) {
78+
fName= name;
8179
}
8280

8381
/**
@@ -166,80 +164,86 @@ public void reset() {
166164
* The background activity. Waits until there is something in the
167165
* queue managing the changes that have been applied to the text viewer.
168166
* Removes the first change from the queue and process it.
169-
* <p>
170-
* Calls {@link AbstractReconciler#initialProcess()} on entrance.
171-
* </p>
172167
*/
173168
@Override
174169
public void run() {
170+
try {
171+
while (!fCanceled) {
175172

176-
delay();
177-
178-
if (fCanceled)
179-
return;
180-
181-
initialProcess();
182-
183-
while (!fCanceled) {
184-
185-
delay();
186-
187-
if (fCanceled)
188-
break;
173+
delay();
189174

190-
if (!isDirty()) {
191-
waitFinish= false; //signalWaitForFinish() was called but nothing todo
192-
continue;
193-
}
175+
if (fCanceled)
176+
break;
194177

195-
synchronized (this) {
196-
if (fReset) {
197-
fReset= false;
178+
if (!isDirty()) {
179+
waitFinish= false; //signalWaitForFinish() was called but nothing todo
198180
continue;
199181
}
200-
}
201182

202-
DirtyRegion r= null;
203-
synchronized (fDirtyRegionQueue) {
204-
r= fDirtyRegionQueue.removeNextDirtyRegion();
205-
}
183+
synchronized (this) {
184+
if (fReset) {
185+
fReset= false;
186+
continue;
187+
}
188+
}
189+
190+
DirtyRegion r= null;
191+
synchronized (fDirtyRegionQueue) {
192+
r= fDirtyRegionQueue.removeNextDirtyRegion();
193+
}
206194

207-
fIsActive= true;
195+
fIsActive= true;
208196

209-
fProgressMonitor.setCanceled(false);
197+
fProgressMonitor.setCanceled(false);
210198

211-
process(r);
199+
process(r);
212200

213-
synchronized (fDirtyRegionQueue) {
214-
if (0 == fDirtyRegionQueue.getSize()) {
215-
synchronized (this) {
216-
fIsDirty= fProgressMonitor.isCanceled();
201+
synchronized (fDirtyRegionQueue) {
202+
if (fDirtyRegionQueue.isEmpty()) {
203+
synchronized (this) {
204+
fIsDirty= fProgressMonitor.isCanceled();
205+
}
206+
fDirtyRegionQueue.notifyAll();
217207
}
218-
fDirtyRegionQueue.notifyAll();
219208
}
209+
fIsActive= false;
220210
}
221-
222-
fIsActive= false;
211+
} finally {
212+
fIsAlive= false;
223213
}
224214
}
225215

216+
boolean isAlive() {
217+
return fIsAlive;
218+
}
219+
220+
/**
221+
* Star the reconciling if not running (and calls
222+
* {@link AbstractReconciler#initialProcess()}) or {@link #reset()} otherwise.
223+
*/
226224
public void startReconciling() {
227-
if (!isAlive()) {
228-
if (fStarted) {
229-
return;
230-
}
225+
if (!fStarted) {
226+
fIsAlive= true;
231227
fStarted= true;
232-
Job.createSystem("Delayed Reconciler startup", m -> { //$NON-NLS-1$
233-
try {
234-
start();
235-
return Status.OK_STATUS;
236-
} catch (IllegalThreadStateException e) {
237-
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=40549
238-
// This is the only instance where the thread is started; since
239-
// we checked that it is not alive, it must be dead already due
240-
// to a run-time exception or error. Exit.
228+
Job.createSystem("Delayed Reconciler startup for " + fName, m -> { //$NON-NLS-1$
229+
//Until we process some code from the job, the reconciler thread is the current thread
230+
fThread= Thread.currentThread();
231+
delay();
232+
if (fCanceled) {
233+
return Status.CANCEL_STATUS;
234+
}
235+
initialProcess();
236+
if (fCanceled) {
241237
return Status.CANCEL_STATUS;
242238
}
239+
Thread thread= new Thread(this);
240+
thread.setName(fName);
241+
thread.setPriority(Thread.MIN_PRIORITY);
242+
thread.setDaemon(true);
243+
//we will no longer process any code here, so hand over to the worker thread.
244+
fThread= thread;
245+
thread.start();
246+
return Status.OK_STATUS;
243247
}).schedule();
244248
} else {
245249
reset();
@@ -260,8 +264,8 @@ public void documentAboutToBeChanged(DocumentEvent e) {
260264
@Override
261265
public void documentChanged(DocumentEvent e) {
262266

263-
if (fThread.isActive() || !fThread.isDirty() && fThread.isAlive()) {
264-
if (!fIsAllowedToModifyDocument && Thread.currentThread() == fThread)
267+
if (fWorker.isActive() || !fWorker.isDirty() && fWorker.isAlive()) {
268+
if (!fIsAllowedToModifyDocument && isRunningInReconcilerThread())
265269
throw new UnsupportedOperationException("The reconciler thread is not allowed to modify the document"); //$NON-NLS-1$
266270
aboutToBeReconciledInternal();
267271
}
@@ -270,13 +274,13 @@ public void documentChanged(DocumentEvent e) {
270274
* The second OR condition handles the case when the document
271275
* gets changed while still inside initialProcess().
272276
*/
273-
if (fThread.isActive() || fThread.isDirty() && fThread.isAlive())
277+
if (fWorker.isActive() || fWorker.isDirty() && fWorker.isAlive())
274278
fProgressMonitor.setCanceled(true);
275279

276280
if (fIsIncrementalReconciler)
277281
createDirtyRegion(e);
278282

279-
fThread.reset();
283+
fWorker.reset();
280284

281285
}
282286

@@ -292,11 +296,11 @@ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput
292296
synchronized (fDirtyRegionQueue) {
293297
fDirtyRegionQueue.purgeQueue();
294298
}
295-
if (fDocument != null && fDocument.getLength() > 0 && fThread.isDirty() && fThread.isAlive()) {
299+
if (fDocument != null && fDocument.getLength() > 0 && fWorker.isDirty() && fWorker.isAlive()) {
296300
DocumentEvent e= new DocumentEvent(fDocument, 0, fDocument.getLength(), ""); //$NON-NLS-1$
297301
createDirtyRegion(e);
298-
fThread.reset();
299-
fThread.suspendCallerWhileDirty();
302+
fWorker.reset();
303+
fWorker.suspendCallerWhileDirty();
300304
}
301305
}
302306

@@ -316,7 +320,7 @@ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
316320

317321
fDocument.addDocumentListener(this);
318322

319-
if (!fThread.isDirty())
323+
if (!fWorker.isDirty())
320324
aboutToBeReconciledInternal();
321325

322326
startReconciling();
@@ -326,7 +330,7 @@ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
326330
/** Queue to manage the changes applied to the text viewer. */
327331
private DirtyRegionQueue fDirtyRegionQueue;
328332
/** The background thread. */
329-
private BackgroundThread fThread;
333+
private BackgroundWorker fWorker;
330334
/** Internal document and text input listener. */
331335
private Listener fListener;
332336
/** The background thread delay. */
@@ -475,9 +479,9 @@ public void install(ITextViewer textViewer) {
475479
fViewer= textViewer;
476480

477481
synchronized (this) {
478-
if (fThread != null)
482+
if (fWorker != null)
479483
return;
480-
fThread= new BackgroundThread(getClass().getName());
484+
fWorker= new BackgroundWorker(getClass().getName());
481485
}
482486

483487
fDirtyRegionQueue= new DirtyRegionQueue();
@@ -511,8 +515,8 @@ public void uninstall() {
511515

512516
synchronized (this) {
513517
// http://dev.eclipse.org/bugs/show_bug.cgi?id=19135
514-
BackgroundThread bt= fThread;
515-
fThread= null;
518+
BackgroundWorker bt= fWorker;
519+
fWorker= null;
516520
bt.cancel();
517521
}
518522
}
@@ -618,10 +622,10 @@ protected void forceReconciling() {
618622

619623
if (fDocument != null) {
620624

621-
if (!fThread.isDirty()&& fThread.isAlive())
625+
if (!fWorker.isDirty()&& fWorker.isAlive())
622626
aboutToBeReconciledInternal();
623627

624-
if (fThread.isActive())
628+
if (fWorker.isActive())
625629
fProgressMonitor.setCanceled(true);
626630

627631
if (fIsIncrementalReconciler) {
@@ -638,10 +642,10 @@ protected void forceReconciling() {
638642
* Clients may extend this method.
639643
*/
640644
protected synchronized void startReconciling() {
641-
if (fThread == null)
645+
if (fWorker == null)
642646
return;
643647

644-
fThread.startReconciling();
648+
fWorker.startReconciling();
645649
}
646650

647651
/**
@@ -657,7 +661,9 @@ protected void reconcilerReset() {
657661
* @return <code>true</code> if running in this reconciler's background thread
658662
* @since 3.4
659663
*/
660-
protected boolean isRunningInReconcilerThread() {
661-
return Thread.currentThread() == fThread;
664+
protected synchronized boolean isRunningInReconcilerThread() {
665+
if (fWorker == null)
666+
return false;
667+
return Thread.currentThread() == fWorker.fThread;
662668
}
663669
}

bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/reconciler/DirtyRegionQueue.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ public int getSize() {
8585
return fDirtyRegions.size();
8686
}
8787

88+
public boolean isEmpty() {
89+
return fDirtyRegions.isEmpty();
90+
}
91+
8892
/**
8993
* Throws away all entries in the queue.
9094
*/

tests/org.eclipse.jface.text.tests/src/org/eclipse/jface/text/tests/reconciler/AbstractReconcilerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public IReconcilingStrategy getReconcilingStrategy(String contentType) {
190190
fReconciler.install(fViewer);
191191

192192
fAccessor= new Accessor(fReconciler, AbstractReconciler.class);
193-
Object object= fAccessor.get("fThread");
193+
Object object= fAccessor.get("fWorker");
194194
fAccessor= new Accessor(object, object.getClass());
195195
}
196196

0 commit comments

Comments
 (0)