/* * Copyright 2000-2016 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.vfs.newvfs; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.*; import com.intellij.openapi.application.ex.ApplicationEx; import com.intellij.openapi.diagnostic.FrequentEventDetector; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.vfs.VfsBundle; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.util.concurrency.BoundedTaskExecutor; import com.intellij.util.io.storage.HeavyProcessLatch; import gnu.trove.TLongObjectHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.ide.PooledThreadExecutor; import java.util.Collections; import java.util.concurrent.ExecutorService; /** * @author max */ public class RefreshQueueImpl extends RefreshQueue implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.newvfs.RefreshQueueImpl"); private final ExecutorService myQueue = new BoundedTaskExecutor("RefreshQueue pool", PooledThreadExecutor.INSTANCE, 1, this); private final ProgressIndicator myRefreshIndicator = RefreshProgress.create(VfsBundle.message("file.synchronize.progress")); private final TLongObjectHashMap<RefreshSession> mySessions = new TLongObjectHashMap<>(); private final FrequentEventDetector myEventCounter = new FrequentEventDetector(100, 100, FrequentEventDetector.Level.WARN); public void execute(@NotNull RefreshSessionImpl session) { if (session.isAsynchronous()) { queueSession(session, session.getTransaction()); } else { Application app = ApplicationManager.getApplication(); if (app.isDispatchThread()) { ((TransactionGuardImpl)TransactionGuard.getInstance()).assertWriteActionAllowed(); doScan(session); session.fireEvents(); } else { if (((ApplicationEx)app).holdsReadLock()) { LOG.error("Do not call synchronous refresh under read lock (except from EDT) - " + "this will cause a deadlock if there are any events to fire."); return; } queueSession(session, TransactionGuard.getInstance().getContextTransaction()); session.waitFor(); } } } private void queueSession(@NotNull RefreshSessionImpl session, @Nullable TransactionId transaction) { myQueue.submit(() -> { myRefreshIndicator.start(); try (AccessToken ignored = HeavyProcessLatch.INSTANCE.processStarted("Doing file refresh. " + session)) { doScan(session); } finally { myRefreshIndicator.stop(); TransactionGuard.getInstance().submitTransaction(ApplicationManager.getApplication(), transaction, session::fireEvents); } }); myEventCounter.eventHappened(session); } private void doScan(RefreshSessionImpl session) { try { updateSessionMap(session, true); session.scan(); } finally { updateSessionMap(session, false); } } private void updateSessionMap(RefreshSession session, boolean add) { long id = session.getId(); if (id != 0) { synchronized (mySessions) { if (add) { mySessions.put(id, session); } else { mySessions.remove(id); } } } } @Override public void cancelSession(long id) { RefreshSession session; synchronized (mySessions) { session = mySessions.get(id); } if (session instanceof RefreshSessionImpl) { ((RefreshSessionImpl)session).cancel(); } } @NotNull @Override public RefreshSession createSession(boolean async, boolean recursively, @Nullable Runnable finishRunnable, @NotNull ModalityState state) { return new RefreshSessionImpl(async, recursively, finishRunnable, ((TransactionGuardImpl)TransactionGuard.getInstance()).getModalityTransaction(state)); } @Override public void processSingleEvent(@NotNull VFileEvent event) { new RefreshSessionImpl(Collections.singletonList(event)).launch(); } public static boolean isRefreshInProgress() { RefreshQueueImpl refreshQueue = (RefreshQueueImpl)RefreshQueue.getInstance(); synchronized (refreshQueue.mySessions) { return !refreshQueue.mySessions.isEmpty(); } } @Override public void dispose() { } }