/* * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.progress.impl; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressIndicatorProvider; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.UserDataHolder; import com.intellij.openapi.wm.ex.ProgressIndicatorEx; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * @author peter */ public class ProgressSuspender { private static final Key<ProgressSuspender> PROGRESS_SUSPENDER = Key.create("PROGRESS_SUSPENDER"); private final Object myLock = new Object(); private final Thread myThread; private static final Application ourApp = ApplicationManager.getApplication(); private volatile boolean mySuspended; private final CoreProgressManager.CheckCanceledHook myHook = this::freezeIfNeeded; private ProgressSuspender(@NotNull ProgressIndicatorEx progress) { assert progress.isRunning(); assert ProgressIndicatorProvider.getGlobalProgressIndicator() == progress; myThread = Thread.currentThread(); ((UserDataHolder) progress).putUserData(PROGRESS_SUSPENDER, this); new ProgressIndicatorListenerAdapter() { @Override public void cancelled() { setSuspended(false); } }.installToProgress(progress); } public static void markSuspendable(@NotNull ProgressIndicator indicator) { new ProgressSuspender((ProgressIndicatorEx)indicator); } @Nullable public static ProgressSuspender getSuspender(@NotNull ProgressIndicator indicator) { return indicator instanceof UserDataHolder ? ((UserDataHolder)indicator).getUserData(PROGRESS_SUSPENDER) : null; } public boolean isSuspended() { return mySuspended; } public void setSuspended(boolean suspended) { synchronized (myLock) { if (suspended == mySuspended) return; mySuspended = suspended; ProgressManagerImpl manager = (ProgressManagerImpl)ProgressManager.getInstance(); if (suspended) { manager.addCheckCanceledHook(myHook); } else { manager.removeCheckCanceledHook(myHook); myLock.notifyAll(); } } } private boolean freezeIfNeeded(@Nullable ProgressIndicator current) { if (current == null || ourApp.isReadAccessAllowed() || !CoreProgressManager.isThreadUnderIndicator(current, myThread)) { return false; } synchronized (myLock) { while (mySuspended) { try { myLock.wait(10000); } catch (InterruptedException ignore) { } } return true; } } }