fix(task): wrong cancellation implementation. Closes #1035.
This commit is contained in:
@@ -57,6 +57,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
|
||||||
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
import static org.jackhuang.hmcl.ui.versions.VersionPage.wrap;
|
||||||
@@ -166,9 +167,13 @@ public class DownloadPage extends BorderPane implements DecoratorPage {
|
|||||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(file.getFile().getUrl()), dest.toFile());
|
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(file.getFile().getUrl()), dest.toFile());
|
||||||
task.setName(file.getName());
|
task.setName(file.getName());
|
||||||
return task;
|
return task;
|
||||||
}).whenComplete(exception -> {
|
}).whenComplete(Schedulers.javafx(), exception -> {
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
Controllers.dialog(DownloadProviders.localizeErrorMessage(exception), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR);
|
if (exception instanceof CancellationException) {
|
||||||
|
Controllers.showToast(i18n("message.cancelled"));
|
||||||
|
} else {
|
||||||
|
Controllers.dialog(DownloadProviders.localizeErrorMessage(exception), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Controllers.showToast(i18n("install.success"));
|
Controllers.showToast(i18n("install.success"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ logwindow.export_game_crash_logs=Export game crash info
|
|||||||
|
|
||||||
main_page=Home
|
main_page=Home
|
||||||
|
|
||||||
|
message.cancelled=Operation was cancelled
|
||||||
message.confirm=Confirm
|
message.confirm=Confirm
|
||||||
message.copied=Copied to clipboard
|
message.copied=Copied to clipboard
|
||||||
message.doing=Please wait
|
message.doing=Please wait
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ logwindow.export_game_crash_logs=導出遊戲崩潰訊息
|
|||||||
|
|
||||||
main_page=首頁
|
main_page=首頁
|
||||||
|
|
||||||
|
message.cancelled=操作被取消
|
||||||
message.confirm=提示
|
message.confirm=提示
|
||||||
message.copied=已複製到剪貼板
|
message.copied=已複製到剪貼板
|
||||||
message.doing=請耐心等待
|
message.doing=請耐心等待
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ logwindow.export_game_crash_logs=导出游戏崩溃信息
|
|||||||
|
|
||||||
main_page=主页
|
main_page=主页
|
||||||
|
|
||||||
|
message.cancelled=操作被取消
|
||||||
message.confirm=提示
|
message.confirm=提示
|
||||||
message.copied=已复制到剪贴板
|
message.copied=已复制到剪贴板
|
||||||
message.doing=请耐心等待
|
message.doing=请耐心等待
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cancelled.set(true);
|
cancelled.set(true);
|
||||||
future.cancel(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompletableFuture<?> executeTasksExceptionally(Task<?> parentTask, Collection<Task<?>> tasks) {
|
private CompletableFuture<?> executeTasksExceptionally(Task<?> parentTask, Collection<Task<?>> tasks) {
|
||||||
@@ -229,13 +228,15 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
.thenComposeAsync(dependentsException -> {
|
.thenComposeAsync(dependentsException -> {
|
||||||
boolean isDependentsSucceeded = dependentsException == null;
|
boolean isDependentsSucceeded = dependentsException == null;
|
||||||
|
|
||||||
if (!isDependentsSucceeded && task.isRelyingOnDependents()) {
|
if (isDependentsSucceeded) {
|
||||||
task.setException(dependentsException);
|
|
||||||
rethrow(dependentsException);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDependentsSucceeded)
|
|
||||||
task.setDependentsSucceeded();
|
task.setDependentsSucceeded();
|
||||||
|
} else {
|
||||||
|
task.setException(dependentsException);
|
||||||
|
|
||||||
|
if (task.isRelyingOnDependents()) {
|
||||||
|
rethrow(dependentsException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CompletableFuture.runAsync(wrap(() -> {
|
return CompletableFuture.runAsync(wrap(() -> {
|
||||||
task.setState(Task.TaskState.RUNNING);
|
task.setState(Task.TaskState.RUNNING);
|
||||||
@@ -263,10 +264,12 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
.thenApplyAsync(dependenciesException -> {
|
.thenApplyAsync(dependenciesException -> {
|
||||||
boolean isDependenciesSucceeded = dependenciesException == null;
|
boolean isDependenciesSucceeded = dependenciesException == null;
|
||||||
|
|
||||||
if (!isDependenciesSucceeded && task.isRelyingOnDependencies()) {
|
if (!isDependenciesSucceeded) {
|
||||||
Logging.LOG.severe("Subtasks failed for " + task.getName());
|
Logging.LOG.severe("Subtasks failed for " + task.getName());
|
||||||
task.setException(dependenciesException);
|
task.setException(dependenciesException);
|
||||||
rethrow(dependenciesException);
|
if (task.isRelyingOnDependencies()) {
|
||||||
|
rethrow(dependenciesException);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkCancellation();
|
checkCancellation();
|
||||||
@@ -285,23 +288,20 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
.exceptionally(throwable -> {
|
.exceptionally(throwable -> {
|
||||||
Throwable resolved = resolveException(throwable);
|
Throwable resolved = resolveException(throwable);
|
||||||
if (resolved instanceof Exception) {
|
if (resolved instanceof Exception) {
|
||||||
Exception e = (Exception) resolved;
|
Exception e = convertInterruptedException((Exception) resolved);
|
||||||
if (e instanceof InterruptedException || e instanceof CancellationException) {
|
task.setException(e);
|
||||||
task.setException(null);
|
exception = e;
|
||||||
|
if (e instanceof CancellationException) {
|
||||||
if (task.getSignificance().shouldLog()) {
|
if (task.getSignificance().shouldLog()) {
|
||||||
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName());
|
Logging.LOG.log(Level.FINE, "Task aborted: " + task.getName());
|
||||||
}
|
}
|
||||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
|
||||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
|
||||||
} else {
|
} else {
|
||||||
task.setException(e);
|
|
||||||
exception = e;
|
|
||||||
if (task.getSignificance().shouldLog()) {
|
if (task.getSignificance().shouldLog()) {
|
||||||
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
|
Logging.LOG.log(Level.FINE, "Task failed: " + task.getName(), e);
|
||||||
}
|
}
|
||||||
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
|
||||||
taskListeners.forEach(it -> it.onFailed(task, e));
|
|
||||||
}
|
}
|
||||||
|
task.onDone().fireEvent(new TaskEvent(this, task, true));
|
||||||
|
taskListeners.forEach(it -> it.onFailed(task, e));
|
||||||
|
|
||||||
task.setState(Task.TaskState.FAILED);
|
task.setState(Task.TaskState.FAILED);
|
||||||
}
|
}
|
||||||
@@ -331,6 +331,14 @@ public final class AsyncTaskExecutor extends TaskExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Exception convertInterruptedException(Exception e) {
|
||||||
|
if (e instanceof InterruptedException) {
|
||||||
|
return new CancellationException(e.getMessage());
|
||||||
|
} else {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler = null;
|
private static Thread.UncaughtExceptionHandler uncaughtExceptionHandler = null;
|
||||||
|
|
||||||
public static void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
|
public static void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler uncaughtExceptionHandler) {
|
||||||
|
|||||||
@@ -665,14 +665,14 @@ public abstract class Task<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
if (isDependentsSucceeded() != (Task.this.getException() == null))
|
if (isDependentsSucceeded() != (Task.this.getException() == null))
|
||||||
throw new AssertionError("When dependents succeeded, Task.exception must be nonnull.");
|
throw new AssertionError("When whenComplete succeeded, Task.exception must be null.");
|
||||||
|
|
||||||
action.execute(Task.this.getException());
|
action.execute(Task.this.getException());
|
||||||
|
|
||||||
if (!isDependentsSucceeded()) {
|
if (!isDependentsSucceeded()) {
|
||||||
setSignificance(TaskSignificance.MINOR);
|
setSignificance(TaskSignificance.MINOR);
|
||||||
if (Task.this.getException() == null)
|
if (Task.this.getException() == null)
|
||||||
throw new CancellationException();
|
throw new AssertionError("When failed, exception cannot be null");
|
||||||
else
|
else
|
||||||
throw Task.this.getException();
|
throw Task.this.getException();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user