private boolean doQueue( @NotNull Document document, @NotNull Project project, CommitStage start, @NonNls @NotNull Object reason) { synchronized (documentsToCommit) { if (!changeCommitStage(document, start, CommitStage.QUEUED_TO_COMMIT, false)) return false; Object[] documentTasks = documentsToCommit.toArray(); for (Object o : documentTasks) { assert o != null : "Null element in:" + documentsToCommit; CommitTask task = (CommitTask) o; if (task.document == document) { ProgressIndicator current = document.getUserData(COMMIT_PROGRESS); if (current == null) { // already queued, not started yet return true; } else { // cancel current commit process to re-queue current.cancel(); removeCommitFromQueue(document); break; } } } ProgressIndicator indicator = new ProgressIndicatorBase(); indicator.start(); documentsToCommit.addLast(new CommitTask(document, project, indicator, reason)); log("Queued", document, false, reason); wakeUpQueue(); return true; } }
private void stopThread() { isDisposed = true; synchronized (documentsToCommit) { documentsToCommit.clear(); } cancel("Stop thread"); wakeUpQueue(); while (!threadFinished) { wakeUpQueue(); synchronized (documentsToCommit) { try { documentsToCommit.wait(10); } catch (InterruptedException ignored) { } } } }
@TestOnly public void clearQueue() { synchronized (documentsToCommit) { documentsToCommit.clear(); } clearLog(); disable("end of test"); wakeUpQueue(); }
private void removeCommitFromQueue(@NotNull Document document) { synchronized (documentsToCommit) { ProgressIndicator indicator = document.getUserData(COMMIT_PROGRESS); if (indicator != null && indicator.isRunning()) { indicator.stop(); // mark document as removed log("Removed from queue", document, false); } // let our thread know that queue must be polled again wakeUpQueue(); } }
public void enable(Object reason) { myEnabled = true; wakeUpQueue(); log("Enabled", null, false, reason); }
@Override public void run() { threadFinished = false; while (!isDisposed) { try { boolean success = false; Document document = null; Project project = null; ProgressIndicator indicator = null; try { CommitTask task; synchronized (documentsToCommit) { if (!myEnabled || documentsToCommit.isEmpty()) { documentsToCommit.wait(); continue; } task = documentsToCommit.pullFirst(); document = task.document; indicator = task.indicator; project = task.project; log("Pulled", document, false, indicator); CommitStage commitStage = getCommitStage(document); Document[] uncommitted = null; if (commitStage != CommitStage.QUEUED_TO_COMMIT || project.isDisposed() || !ArrayUtil.contains( document, uncommitted = PsiDocumentManager.getInstance(project).getUncommittedDocuments())) { List<Document> documents = uncommitted == null ? null : Arrays.asList(uncommitted); log("Abandon and proceeding to next", document, false, commitStage, documents); continue; } if (indicator.isRunning()) { useIndicator(indicator); document.putUserData(COMMIT_PROGRESS, indicator); } else { success = true; // document has been marked as removed, e.g. by synchronous commit } } Runnable finishRunnable = null; if (!success && !indicator.isCanceled()) { try { finishRunnable = commit(document, project, null, indicator, false, task.reason); success = finishRunnable != null; log("DCT.commit returned", document, false, finishRunnable, indicator); } finally { document.putUserData(COMMIT_PROGRESS, null); } } synchronized (documentsToCommit) { if (indicator.isCanceled()) { success = false; } if (success) { assert !ApplicationManager.getApplication().isDispatchThread(); UIUtil.invokeLaterIfNeeded(finishRunnable); log( "Invoked later finishRunnable", document, false, success, finishRunnable, indicator); } } } catch (ProcessCanceledException e) { cancel(e); // leave queue unchanged log("PCE", document, false, e); success = false; } catch (InterruptedException e) { // app must be closing int i = 0; log("IE", document, false, e); cancel(e); } catch (Throwable e) { LOG.error(e); cancel(e); } synchronized (documentsToCommit) { if (!success && indicator.isRunning()) { // running means sync commit has not intervened // reset status for queue back successfully changeCommitStage( document, CommitStage.WAITING_FOR_PSI_APPLY, CommitStage.QUEUED_TO_COMMIT, false); changeCommitStage(document, CommitStage.COMMITTED, CommitStage.QUEUED_TO_COMMIT, false); doQueue(document, project, CommitStage.QUEUED_TO_COMMIT, "re-added on failure"); } } } catch (Throwable e) { e.printStackTrace(); // LOG.error(e); } } threadFinished = true; // ping the thread waiting for close wakeUpQueue(); log("Good bye", null, false); }