1616import org .eclipse .core .runtime .Assert ;
1717import org .eclipse .core .runtime .IProgressMonitor ;
1818import org .eclipse .core .runtime .NullProgressMonitor ;
19+ import org .eclipse .core .runtime .Status ;
1920import org .eclipse .core .runtime .jobs .Job ;
2021
2122import org .eclipse .jface .text .DocumentEvent ;
5051 */
5152abstract public class AbstractReconciler implements IReconciler {
5253
54+
55+ /**
56+ * Background thread for the reconciling activity.
57+ */
58+ class BackgroundThread extends Thread {
59+
60+ /** Has the reconciler been canceled. */
61+ private boolean fCanceled = false ;
62+ /** Has the reconciler been reset. */
63+ private boolean fReset = false ;
64+ /** Some changes need to be processed. */
65+ private boolean fIsDirty = false ;
66+ /** Is a reconciling strategy active. */
67+ private boolean fIsActive = false ;
68+
69+ private boolean fStarted ;
70+
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 );
81+ }
82+
83+ /**
84+ * Returns whether a reconciling strategy is active right now.
85+ *
86+ * @return <code>true</code> if a activity is active
87+ */
88+ public boolean isActive () {
89+ return fIsActive ;
90+ }
91+
92+ /**
93+ * Returns whether some changes need to be processed.
94+ *
95+ * @return <code>true</code> if changes wait to be processed
96+ * @since 3.0
97+ */
98+ public synchronized boolean isDirty () {
99+ return fIsDirty ;
100+ }
101+
102+ /**
103+ * Cancels the background thread.
104+ */
105+ public void cancel () {
106+ fCanceled = true ;
107+ IProgressMonitor pm = fProgressMonitor ;
108+ if (pm != null )
109+ pm .setCanceled (true );
110+ synchronized (fDirtyRegionQueue ) {
111+ fDirtyRegionQueue .notifyAll ();
112+ }
113+ }
114+
115+ /**
116+ * Suspends the caller of this method until this background thread has
117+ * emptied the dirty region queue.
118+ */
119+ public void suspendCallerWhileDirty () {
120+ AbstractReconciler .this .signalWaitForFinish ();
121+ boolean isDirty ;
122+ do {
123+ synchronized (fDirtyRegionQueue ) {
124+ isDirty = fDirtyRegionQueue .getSize () > 0 ;
125+ if (isDirty ) {
126+ try {
127+ fDirtyRegionQueue .wait ();
128+ } catch (InterruptedException x ) {
129+ }
130+ }
131+ }
132+ } while (isDirty );
133+ }
134+
135+ /**
136+ * Reset the background thread as the text viewer has been changed,
137+ */
138+ public void reset () {
139+
140+ if (fDelay > 0 ) {
141+
142+ synchronized (this ) {
143+ fIsDirty = true ;
144+ fReset = true ;
145+ }
146+ synchronized (fDirtyRegionQueue ) {
147+ fDirtyRegionQueue .notifyAll (); // wake up wait(fDelay);
148+ }
149+
150+ } else {
151+
152+ synchronized (this ) {
153+ fIsDirty = true ;
154+ }
155+
156+ synchronized (fDirtyRegionQueue ) {
157+ fDirtyRegionQueue .notifyAll ();
158+ }
159+ }
160+
161+ informNotFinished ();
162+ reconcilerReset ();
163+ }
164+
165+ /**
166+ * The background activity. Waits until there is something in the
167+ * queue managing the changes that have been applied to the text viewer.
168+ * Removes the first change from the queue and process it.
169+ * <p>
170+ * Calls {@link AbstractReconciler#initialProcess()} on entrance.
171+ * </p>
172+ */
173+ @ Override
174+ public void run () {
175+
176+ delay ();
177+
178+ if (fCanceled )
179+ return ;
180+
181+ initialProcess ();
182+
183+ while (!fCanceled ) {
184+
185+ delay ();
186+
187+ if (fCanceled )
188+ break ;
189+
190+ if (!isDirty ()) {
191+ waitFinish = false ; //signalWaitForFinish() was called but nothing todo
192+ continue ;
193+ }
194+
195+ synchronized (this ) {
196+ if (fReset ) {
197+ fReset = false ;
198+ continue ;
199+ }
200+ }
201+
202+ DirtyRegion r = null ;
203+ synchronized (fDirtyRegionQueue ) {
204+ r = fDirtyRegionQueue .removeNextDirtyRegion ();
205+ }
206+
207+ fIsActive = true ;
208+
209+ fProgressMonitor .setCanceled (false );
210+
211+ process (r );
212+
213+ synchronized (fDirtyRegionQueue ) {
214+ if (0 == fDirtyRegionQueue .getSize ()) {
215+ synchronized (this ) {
216+ fIsDirty = fProgressMonitor .isCanceled ();
217+ }
218+ fDirtyRegionQueue .notifyAll ();
219+ }
220+ }
221+
222+ fIsActive = false ;
223+ }
224+ }
225+
226+ public void startReconciling () {
227+ if (!isAlive ()) {
228+ if (fStarted ) {
229+ return ;
230+ }
231+ 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.
241+ return Status .CANCEL_STATUS ;
242+ }
243+ }).schedule ();
244+ } else {
245+ reset ();
246+ }
247+
248+ }
249+ }
250+
53251 /**
54252 * Internal document listener and text input listener.
55253 */
@@ -63,7 +261,7 @@ public void documentAboutToBeChanged(DocumentEvent e) {
63261 public void documentChanged (DocumentEvent e ) {
64262
65263 if (fThread .isActive () || !fThread .isDirty () && fThread .isAlive ()) {
66- if (!fIsAllowedToModifyDocument && isRunningInReconcilerThread () )
264+ if (!fIsAllowedToModifyDocument && Thread . currentThread () == fThread )
67265 throw new UnsupportedOperationException ("The reconciler thread is not allowed to modify the document" ); //$NON-NLS-1$
68266 aboutToBeReconciledInternal ();
69267 }
@@ -126,14 +324,13 @@ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
126324 }
127325
128326 /** Queue to manage the changes applied to the text viewer. */
129- DirtyRegionQueue fDirtyRegionQueue ;
327+ private DirtyRegionQueue fDirtyRegionQueue ;
130328 /** The background thread. */
131- private ReconcilerJob fThread ;
329+ private BackgroundThread fThread ;
132330 /** Internal document and text input listener. */
133331 private Listener fListener ;
134-
135332 /** The background thread delay. */
136- int fDelay = 500 ;
333+ private int fDelay = 500 ;
137334 /** Signal that the the background thread should not delay. */
138335 volatile boolean waitFinish ;
139336 /** Are there incremental reconciling strategies? */
@@ -280,7 +477,7 @@ public void install(ITextViewer textViewer) {
280477 synchronized (this ) {
281478 if (fThread != null )
282479 return ;
283- fThread = new ReconcilerJob (getClass ().getName (), this );
480+ fThread = new BackgroundThread (getClass ().getName ());
284481 }
285482
286483 fDirtyRegionQueue = new DirtyRegionQueue ();
@@ -314,9 +511,9 @@ public void uninstall() {
314511
315512 synchronized (this ) {
316513 // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135
317- ReconcilerJob bt = fThread ;
514+ BackgroundThread bt = fThread ;
318515 fThread = null ;
319- bt .doCancel ();
516+ bt .cancel ();
320517 }
321518 }
322519 }
@@ -383,7 +580,7 @@ public void signalWaitForFinish() {
383580 }
384581 }
385582
386- void informNotFinished () {
583+ private void informNotFinished () {
387584 waitFinish = false ;
388585 aboutToWork ();
389586 }
@@ -394,7 +591,7 @@ private void aboutToBeReconciledInternal() {
394591 }
395592
396593
397- void delay () {
594+ private void delay () {
398595 synchronized (fDirtyRegionQueue ) {
399596 if (waitFinish ) {
400597 return ; // do not delay when waiting;
@@ -444,11 +641,7 @@ protected synchronized void startReconciling() {
444641 if (fThread == null )
445642 return ;
446643
447- if (!fThread .isAlive ()) {
448- fThread .start ();
449- } else {
450- fThread .reset ();
451- }
644+ fThread .startReconciling ();
452645 }
453646
454647 /**
@@ -464,10 +657,7 @@ protected void reconcilerReset() {
464657 * @return <code>true</code> if running in this reconciler's background thread
465658 * @since 3.4
466659 */
467- protected synchronized boolean isRunningInReconcilerThread () {
468- if (fThread == null ) {
469- return false ;
470- }
471- return Job .getJobManager ().currentJob () == fThread ;
660+ protected boolean isRunningInReconcilerThread () {
661+ return Thread .currentThread () == fThread ;
472662 }
473- }
663+ }
0 commit comments