fix(gui): prevent UI thread freeze on plugin install/uninstall
This commit is contained in:
@@ -16,9 +16,12 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.ICodeWriter;
|
||||
@@ -504,6 +507,27 @@ public class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadFactory simpleThreadFactory(String name) {
|
||||
return new SimpleThreadFactory(name);
|
||||
}
|
||||
|
||||
private static final class SimpleThreadFactory implements ThreadFactory {
|
||||
private static final AtomicInteger POOL = new AtomicInteger(0);
|
||||
private final AtomicInteger number = new AtomicInteger(0);
|
||||
private final String name;
|
||||
|
||||
public SimpleThreadFactory(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(@NotNull Runnable r) {
|
||||
return new Thread(r, "jadx-" + name
|
||||
+ '-' + POOL.incrementAndGet()
|
||||
+ '-' + number.incrementAndGet());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated env vars shouldn't be used in core modules.
|
||||
* Prefer to parse in `app` (use JadxCommonEnv from 'app-commons') and set in jadx args.
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.JadxArgs;
|
||||
import jadx.api.utils.tasks.ITaskExecutor;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class TaskExecutor implements ITaskExecutor {
|
||||
@@ -99,7 +100,7 @@ public class TaskExecutor implements ITaskExecutor {
|
||||
running.set(true);
|
||||
progress.set(0);
|
||||
terminating.set(false);
|
||||
executor = Executors.newFixedThreadPool(1);
|
||||
executor = Executors.newFixedThreadPool(1, Utils.simpleThreadFactory("task-s"));
|
||||
executor.execute(this::runStages);
|
||||
executor.shutdown();
|
||||
}
|
||||
@@ -142,7 +143,8 @@ public class TaskExecutor implements ITaskExecutor {
|
||||
wrapTask(task);
|
||||
}
|
||||
} else {
|
||||
ExecutorService parallelExecutor = Executors.newFixedThreadPool(threads);
|
||||
ExecutorService parallelExecutor = Executors.newFixedThreadPool(
|
||||
threads, Utils.simpleThreadFactory("task-p"));
|
||||
for (Runnable task : stage.getTasks()) {
|
||||
parallelExecutor.execute(() -> wrapTask(task));
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.utils.tasks.ITaskExecutor;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.ui.MainWindow;
|
||||
@@ -104,7 +105,7 @@ public class BackgroundExecutor {
|
||||
}
|
||||
|
||||
private synchronized void reset() {
|
||||
taskQueueExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
|
||||
taskQueueExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1, Utils.simpleThreadFactory("bg"));
|
||||
taskRunning.clear();
|
||||
idSupplier.set(0);
|
||||
}
|
||||
@@ -162,6 +163,7 @@ public class BackgroundExecutor {
|
||||
} finally {
|
||||
taskComplete(id);
|
||||
progressPane.changeVisibility(this, false);
|
||||
removePropertyChangeListener(progressPane);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
|
||||
@@ -537,14 +537,19 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
|
||||
public void reopen() {
|
||||
synchronized (ReloadProject.EVENT) {
|
||||
saveAll();
|
||||
closeAll();
|
||||
loadFiles(() -> {
|
||||
menuBar.reloadShortcuts();
|
||||
events().send(ReloadSettingsWindow.INSTANCE);
|
||||
});
|
||||
}
|
||||
LOG.debug("starting reopen");
|
||||
UiUtils.bgRun(() -> {
|
||||
getBackgroundExecutor().waitForComplete();
|
||||
synchronized (ReloadProject.EVENT) {
|
||||
saveAll();
|
||||
closeAll();
|
||||
loadFiles(() -> {
|
||||
menuBar.reloadShortcuts();
|
||||
events().send(ReloadSettingsWindow.INSTANCE);
|
||||
LOG.debug("reopen complete");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void openProject(Path path, Runnable onFinish) {
|
||||
@@ -622,7 +627,7 @@ public class MainWindow extends JFrame {
|
||||
shortcutsController.reset();
|
||||
UiUtils.resetClipboardOwner();
|
||||
System.gc();
|
||||
update();
|
||||
UiUtils.uiRun(this::update);
|
||||
}
|
||||
|
||||
private void checkLoadedStatus() {
|
||||
|
||||
@@ -20,6 +20,8 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
@@ -79,6 +81,8 @@ public class UiUtils {
|
||||
}
|
||||
};
|
||||
|
||||
private static final ExecutorService BACKGROUND_THREAD = Executors.newSingleThreadExecutor(Utils.simpleThreadFactory("utils-bg"));
|
||||
|
||||
private UiUtils() {
|
||||
}
|
||||
|
||||
@@ -420,6 +424,14 @@ public class UiUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run task in background thread.
|
||||
* Uses single thread, so all tasks are ordered.
|
||||
*/
|
||||
public static void bgRun(Runnable runnable) {
|
||||
BACKGROUND_THREAD.submit(runnable);
|
||||
}
|
||||
|
||||
public static void uiThreadGuard() {
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
LOG.warn("Expect UI thread, got: {}", Thread.currentThread(), new JadxRuntimeException());
|
||||
|
||||
Reference in New Issue
Block a user