hotswap: better implementation of modified classes discovery, does not require to postponed files in memory

This commit is contained in:
Eugene Zhuravlev
2012-06-24 01:04:09 +02:00
parent 242527187b
commit b500c9f1fb
4 changed files with 74 additions and 54 deletions
@@ -64,7 +64,10 @@ import com.sun.jdi.request.EventRequest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class DebuggerSession implements AbstractDebuggerSession {
private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.impl.DebuggerSession");
@@ -104,6 +107,8 @@ public class DebuggerSession implements AbstractDebuggerSession {
private final Set<ThreadReferenceProxyImpl> mySteppingThroughThreads = new HashSet<ThreadReferenceProxyImpl>();
protected final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
private boolean myModifiedClassesScanRequired = false;
public boolean isSteppingThrough(ThreadReferenceProxyImpl threadProxy) {
return mySteppingThroughThreads.contains(threadProxy);
}
@@ -114,6 +119,14 @@ public class DebuggerSession implements AbstractDebuggerSession {
return mySearchScope;
}
public boolean isModifiedClassesScanRequired() {
return myModifiedClassesScanRequired;
}
public void setModifiedClassesScanRequired(boolean modifiedClassesScanRequired) {
myModifiedClassesScanRequired = modifiedClassesScanRequired;
}
private class MyDebuggerStateManager extends DebuggerStateManager {
private DebuggerContextImpl myDebuggerContext;
@@ -192,21 +205,6 @@ public class DebuggerSession implements AbstractDebuggerSession {
return myDebugProcess;
}
private final Map<String, HotSwapFile> myDelayedHotswapFiles = new HashMap<String, HotSwapFile>();
public void addHotswapFiles(Map<String, HotSwapFile> files) {
myDelayedHotswapFiles.putAll(files);
}
@NotNull
public Map<String, HotSwapFile> getHotswapFiles() {
return Collections.unmodifiableMap(myDelayedHotswapFiles);
}
public void clearHotswapFiles() {
myDelayedHotswapFiles.clear();
}
private static class DebuggerSessionState {
final int myState;
final String myDescription;
@@ -35,7 +35,6 @@ import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -155,11 +154,10 @@ public class HotSwapManager extends AbstractProjectComponent {
return project.getComponent(HotSwapManager.class);
}
private void reloadClasses(DebuggerSession session, HotSwapProgress progress) {
private void reloadClasses(DebuggerSession session, Map<String, HotSwapFile> classesToReload, HotSwapProgress progress) {
final long newSwapTime = System.currentTimeMillis();
new ReloadClassesWorker(session, progress).reloadClasses();
new ReloadClassesWorker(session, progress).reloadClasses(classesToReload);
setTimeStamp(session, newSwapTime);
session.clearHotswapFiles();
}
public static Map<DebuggerSession, Map<String, HotSwapFile>> findModifiedClasses(List<DebuggerSession> sessions, Map<String, List<String>> generatedPaths) {
@@ -225,7 +223,7 @@ public class HotSwapManager extends AbstractProjectComponent {
return swapProgress.isCancelled() ? new HashMap<DebuggerSession, Map<String, HotSwapFile>>() : modifiedClasses;
}
public static void reloadModifiedClasses(final Collection<DebuggerSession> sessions, final HotSwapProgress reloadClassesProgress) {
public static void reloadModifiedClasses(final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses, final HotSwapProgress reloadClassesProgress) {
final MultiProcessCommand reloadClassesCommand = new MultiProcessCommand();
reloadClassesProgress.setCancelWorker(new Runnable() {
@@ -234,11 +232,13 @@ public class HotSwapManager extends AbstractProjectComponent {
}
});
for (final DebuggerSession debuggerSession : sessions) {
for (final DebuggerSession debuggerSession : modifiedClasses.keySet()) {
reloadClassesCommand.addCommand(debuggerSession.getProcess(), new DebuggerCommandImpl() {
protected void action() throws Exception {
reloadClassesProgress.setDebuggerSession(debuggerSession);
getInstance(reloadClassesProgress.getProject()).reloadClasses(debuggerSession, reloadClassesProgress);
getInstance(reloadClassesProgress.getProject()).reloadClasses(
debuggerSession, modifiedClasses.get(debuggerSession), reloadClassesProgress
);
}
});
}
@@ -90,12 +90,10 @@ class ReloadClassesWorker {
}
}
public void reloadClasses() {
public void reloadClasses(final Map<String, HotSwapFile> modifiedClasses) {
DebuggerManagerThreadImpl.assertIsManagerThread();
final Map<String, HotSwapFile> modifiedClasses = myDebuggerSession.getHotswapFiles();
if(modifiedClasses.isEmpty()) {
if(modifiedClasses == null || modifiedClasses.size() == 0) {
myProgress.addMessage(myDebuggerSession, MessageCategory.INFORMATION, DebuggerBundle.message("status.hotswap.loaded.classes.up.to.date"));
return;
}
@@ -149,47 +149,68 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
final boolean isOutOfProcessMode = CompilerWorkspaceConfiguration.getInstance(myProject).useOutOfProcessBuild();
final boolean shouldPerformScan = !isOutOfProcessMode || generatedPaths == null;
final HotSwapProgressImpl findClassesProgress = shouldPerformScan ? new HotSwapProgressImpl(myProject) : null;
final HotSwapProgressImpl findClassesProgress;
if (shouldPerformScan) {
findClassesProgress = new HotSwapProgressImpl(myProject);
}
else {
boolean createProgress = false;
for (DebuggerSession session : sessions) {
if (session.isModifiedClassesScanRequired()) {
createProgress = true;
break;
}
}
findClassesProgress = createProgress? new HotSwapProgressImpl(myProject) : null;
}
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
final Map<DebuggerSession, Map<String, HotSwapFile>> newlyModifiedClasses = shouldPerformScan?
scanForModifiedClassesWithProgress(sessions, findClassesProgress, !isOutOfProcessMode) :
HotSwapManager.findModifiedClasses(sessions, generatedPaths);
for (Map.Entry<DebuggerSession, Map<String, HotSwapFile>> entry : newlyModifiedClasses.entrySet()) {
final DebuggerSession session = entry.getKey();
if (shouldPerformScan) {
session.clearHotswapFiles();
}
session.addHotswapFiles(entry.getValue());
final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses;
if (shouldPerformScan) {
modifiedClasses = scanForModifiedClassesWithProgress(sessions, findClassesProgress, !isOutOfProcessMode);
}
boolean hasFilesToReload = false;
for (DebuggerSession session : sessions) {
if (!session.getHotswapFiles().isEmpty()) {
hasFilesToReload = true;
break;
else {
final List<DebuggerSession> toScan = new ArrayList<DebuggerSession>();
final List<DebuggerSession> toUseGenerated = new ArrayList<DebuggerSession>();
for (DebuggerSession session : sessions) {
(session.isModifiedClassesScanRequired()? toScan : toUseGenerated).add(session);
session.setModifiedClassesScanRequired(false);
}
modifiedClasses = new HashMap<DebuggerSession, Map<String, HotSwapFile>>();
if (!toUseGenerated.isEmpty()) {
modifiedClasses.putAll(HotSwapManager.findModifiedClasses(toUseGenerated, generatedPaths));
}
if (!toScan.isEmpty()) {
modifiedClasses.putAll(scanForModifiedClassesWithProgress(toScan, findClassesProgress, !isOutOfProcessMode));
}
}
if (!hasFilesToReload) {
final Application application = ApplicationManager.getApplication();
if (modifiedClasses.isEmpty()) {
final String message = DebuggerBundle.message("status.hotswap.uptodate");
HotSwapProgressImpl.NOTIFICATION_GROUP.createNotification(message, NotificationType.INFORMATION).notify(myProject);
return;
}
final Set<DebuggerSession> sessionsToReload = new HashSet<DebuggerSession>(sessions);
final Application application = ApplicationManager.getApplication();
application.invokeLater(new Runnable() {
public void run() {
if (shouldAskBeforeHotswap && !DebuggerSettings.RUN_HOTSWAP_ALWAYS.equals(runHotswap)) {
final RunHotswapDialog dialog = new RunHotswapDialog(myProject, sessions, shouldDisplayHangWarning);
dialog.show();
if (!dialog.isOK()) {
for (DebuggerSession session : modifiedClasses.keySet()) {
session.setModifiedClassesScanRequired(true);
}
return;
}
sessionsToReload.retainAll(dialog.getSessionsToReload());
final Set<DebuggerSession> toReload = new HashSet<DebuggerSession>(dialog.getSessionsToReload());
for (DebuggerSession session : modifiedClasses.keySet()) {
if (!toReload.contains(session)) {
session.setModifiedClassesScanRequired(true);
}
}
modifiedClasses.keySet().retainAll(toReload);
}
else {
if (shouldDisplayHangWarning) {
@@ -208,16 +229,19 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
}
);
if (answer == DialogWrapper.CANCEL_EXIT_CODE) {
for (DebuggerSession session : modifiedClasses.keySet()) {
session.setModifiedClassesScanRequired(true);
}
return;
}
}
}
if (!sessionsToReload.isEmpty()) {
if (!modifiedClasses.isEmpty()) {
final HotSwapProgressImpl progress = new HotSwapProgressImpl(myProject);
application.executeOnPooledThread(new Runnable() {
public void run() {
reloadModifiedClasses(sessionsToReload, progress);
reloadModifiedClasses(modifiedClasses, progress);
}
});
}
@@ -242,10 +266,10 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent{
return result.get();
}
private static void reloadModifiedClasses(final Collection<DebuggerSession> sessions, final HotSwapProgressImpl progress) {
private static void reloadModifiedClasses(final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses, final HotSwapProgressImpl progress) {
ProgressManager.getInstance().runProcess(new Runnable() {
public void run() {
HotSwapManager.reloadModifiedClasses(sessions, progress);
HotSwapManager.reloadModifiedClasses(modifiedClasses, progress);
progress.finished();
}
}, progress.getProgressIndicator());