Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;
import com.github.minecraft_ta.totalDebugCompanion.notification.NotificationCenter;
import com.github.minecraft_ta.totalDebugCompanion.notification.NotificationCenter.Source;
import com.github.minecraft_ta.totalDebugCompanion.notification.NotificationCenter.Severity;
Expand Down Expand Up @@ -69,7 +70,6 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -799,7 +799,7 @@ private void restoreCatalog(ProjectScope scope) {
itemIcons.setItemLookup(itemId -> scope.catalog().index().flatMap(index -> index.itemIcon(itemId)));
// Independent tasks: unreadable icon archives must not keep the catalog from loading.
itemIcons.restore(scope.paths().previews());
CompletableFuture.runAsync(scope.catalog()::restore);
CompletableFuture.runAsync(scope.catalog()::restore, Workers.files());
}

private void handlePackCatalog(PreparedFilePayload message) {
Expand All @@ -811,7 +811,7 @@ private void handlePackCatalog(PreparedFilePayload message) {
if (scope == null || !scope.isActive()) return;
switch (message.state()) {
case PREPARING -> scope.catalog().capturing();
case READY -> scope.catalog().accept(message.inventoryId(), Path.of(message.file()), ForkJoinPool.commonPool());
case READY -> scope.catalog().accept(message.inventoryId(), Path.of(message.file()), Workers.files());
case FAILED -> scope.catalog().failed(message.detail());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
* When configuration edits take effect in the game, and the edits the running game has not applied yet. NeoForge
Expand All @@ -45,9 +38,6 @@ private record Key(Path file, String setting) {
private final Path workspace;
private final ChangeRecord record;
private final Map<Key, Pending> pending = new ConcurrentHashMap<>();
/** One write at a time for the project, so writes to the same file never interleave. */
private final ExecutorService writes = Executors.newSingleThreadExecutor(task ->
Thread.ofPlatform().daemon().name("Configuration writes").unstarted(task));
/** The game process the pending edits wait in, or 0 while it is unknown. */
private long gameProcess;

Expand All @@ -67,40 +57,6 @@ public GameLocation location() {
return this.location;
}

/** Runs {@code write} after the project's earlier writes; refused once the project closes. */
public <T> CompletableFuture<T> write(Supplier<T> write) {
try {
return CompletableFuture.supplyAsync(write, this.writes);
} catch (RejectedExecutionException closed) {
return CompletableFuture.failedFuture(new IOException("The project is closing; the change was not written"));
}
}

/**
* The project's writes to the game's files, one at a time, which the project finishes before its change record
* closes. Refuses work once the project closes.
*/
public Executor writes() {
return this.writes;
}

/**
* Stops taking writes and waits until those already taken have finished, so every file written is also recorded
* before the change record closes. An interruption does not cut the wait short; it is kept for the caller.
*/
public void close() {
this.writes.shutdown();
boolean interrupted = false;
while (true) {
try {
if (this.writes.awaitTermination(1, TimeUnit.MINUTES)) break;
} catch (InterruptedException interruption) {
interrupted = true;
}
}
if (interrupted) Thread.currentThread().interrupt();
}

/**
* The connected game's process. Another process than the one the pending edits wait in started after it, and read
* every file when it started.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public ChangeRecord record() {
* made, or whatever it holds where {@code expected} is null.
*/
public CompletableFuture<Saved> set(Target target, String expected, String literal) {
return this.changes.write(() -> {
return this.pipeline.write(() -> {
try {
ChangePipeline.Applied<ChangeRecord.Setting> applied = this.pipeline.write(this,
List.of(new ChangePipeline.Edit<>(target.recorded(), expected, new Value(literal)))).applied().getFirst();
Expand Down Expand Up @@ -141,7 +141,7 @@ public CompletableFuture<Saved> set(Target target, String expected, String liter
public CompletableFuture<Saved> saveText(FileTarget target, List<PackCatalog.ConfigSetting> settings, String base, String after,
boolean overwrite) {
String fileName = target.fileName().substring(target.fileName().lastIndexOf('/') + 1);
return this.changes.write(() -> {
return this.pipeline.write(() -> {
try {
// The text as a whole is what the edit was made against, and what is written, comments too.
String current = Files.readString(target.file(), StandardCharsets.UTF_8);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.change;

import java.util.function.Supplier;
import com.github.minecraft_ta.totalDebugCompanion.game.Access;
import com.github.minecraft_ta.totalDebugCompanion.game.GameLocation;
import com.github.minecraft_ta.totalDebugCompanion.game.GameState;
Expand Down Expand Up @@ -68,7 +69,7 @@ public record Outcome<T extends ChangeRecord.Target>(List<Applied<T>> applied, b
private final Map<Integer, CompletableFuture<ChangeResultPayload>> waiting = new ConcurrentHashMap<>();
private final Reloads reloads;

/** Changes the game {@code location} tells of; {@code writes} is the project's write queue. */
/** Changes the game {@code location} tells of; {@code writes} is the project's write queue ({@link WriteQueue}). */
public ChangePipeline(GameLocation location, ChangeRecord record, Executor writes) {
this.location = Objects.requireNonNull(location, "location");
this.record = Objects.requireNonNull(record, "record");
Expand All @@ -79,6 +80,20 @@ public ChangePipeline(GameLocation location, ChangeRecord record, Executor write
});
}

/** The project's write queue, where every write of the game's and the packs' files runs, one at a time. */
public Executor writes() {
return this.writes;
}

/** Runs {@code write} after the project's earlier writes; refused once the project closes. */
public <T> CompletableFuture<T> write(Supplier<T> write) {
try {
return CompletableFuture.supplyAsync(write, this.writes);
} catch (RejectedExecutionException closed) {
return CompletableFuture.failedFuture(new IOException("The project is closing; the change was not written"));
}
}

public GameLocation location() {
return this.location;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.github.minecraft_ta.totalDebugCompanion.change;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

/**
* The project's writes to the game's and the packs' files, one at a time, so writes to the same file never interleave
* (docs/SYSTEMS.md, section 5). The change pipeline writes through it; the project finishes it before its change record
* closes. It refuses work once closed.
*/
public final class WriteQueue implements Executor, AutoCloseable {
private final ExecutorService worker = Workers.projectWrites();
private boolean closed;

@Override
public synchronized void execute(Runnable write) {
if (this.closed) throw new RejectedExecutionException("The project is closing");
this.worker.execute(write);
}

/**
* Stops taking writes and waits until those already taken have finished, so every file written is also recorded
* before the change record closes. An interruption does not cut the wait short; it is kept for the caller.
*/
@Override
public void close() {
synchronized (this) {
this.closed = true;
this.worker.shutdown();
}
boolean interrupted = false;
while (true) {
try {
if (this.worker.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) break;
} catch (InterruptedException interruption) {
interrupted = true;
}
}
if (interrupted) Thread.currentThread().interrupt();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.inspection;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;
import com.github.minecraft_ta.totalDebugCompanion.jdt.JavaSnippetSource;
import com.github.minecraft_ta.totalDebugCompanion.script.ExecutionTextDisplay;
import com.github.minecraft_ta.totalDebugCompanion.script.ScriptCompilationService;
Expand Down Expand Up @@ -280,7 +281,7 @@ public CompletableFuture<Path> createTool(String name) {
} catch (Exception exception) {
throw new IllegalStateException(exception.getMessage(), exception);
}
}).whenComplete((path, failure) -> SwingUtilities.invokeLater(() -> {
}, Workers.files()).whenComplete((path, failure) -> SwingUtilities.invokeLater(() -> {
if (failure == null) refresh();
}));
}
Expand Down Expand Up @@ -365,7 +366,7 @@ private CompletableFuture<Void> runTools(Side selected) {
done.complete(null);
return done;
}
CompletableFuture.supplyAsync(this::loadTools).whenComplete((loaded, failure) -> SwingUtilities.invokeLater(() -> {
CompletableFuture.supplyAsync(this::loadTools, Workers.files()).whenComplete((loaded, failure) -> SwingUtilities.invokeLater(() -> {
if (this.disposed || current != this.toolRevision) {
done.complete(null);
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.inspection;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;
import com.github.minecraft_ta.totalDebugCompanion.util.Signal;
import com.github.minecraft_ta.totalDebugCompanion.catalog.CatalogIndex;
import com.github.minecraft_ta.totalDebugCompanion.itemrender.ItemModelId;
Expand All @@ -25,7 +26,6 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -89,7 +89,7 @@ public void accept(Path archive) {
* newest restore adopts its result, and a snapshot announced meanwhile supersedes it.
*/
public CompletableFuture<Void> restore(Path directory) {
return restore(directory, ForkJoinPool.commonPool());
return restore(directory, Workers.files());
}

CompletableFuture<Void> restore(Path directory, Executor reader) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.model;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;
import com.github.minecraft_ta.totalDebugCompanion.runtime.RuntimeBinding;
import com.github.minecraft_ta.totalDebugCompanion.ui.EditorContext;
import com.formdev.flatlaf.util.StringUtils;
Expand Down Expand Up @@ -56,7 +57,7 @@ public CodeView(EditorContext context, DecompiledSource source, int offset, Edit

public void reload(int offset) {
CompletableFuture<Void> task = CompletableFuture
.supplyAsync(() -> readCode(this.path))
.supplyAsync(() -> readCode(this.path), Workers.files())
.thenAcceptAsync(code -> {
this.codeViewPanel.setCode(code);
this.codeViewPanel.navigateToOffset(offset);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.navigation;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;
import com.github.minecraft_ta.totalDebugCompanion.catalog.ConfigSources;
import com.github.minecraft_ta.totalDebugCompanion.model.ChangesView;
import com.github.minecraft_ta.totalDebugCompanion.model.ConfigFileView;
Expand Down Expand Up @@ -585,7 +586,7 @@ private CompletableFuture<Void> openRuntimeSource(
public CompletableFuture<Void> relocatePreview(IEditorPanel previous, Path path) {
if (path.getFileName().toString().endsWith(ScriptView.FILE_EXTENSION)) {
EditorContext context = editors.get();
return CompletableFuture.supplyAsync(() -> new ScriptView(context, path)).thenComposeAsync(replacement -> {
return CompletableFuture.supplyAsync(() -> new ScriptView(context, path), Workers.files()).thenComposeAsync(replacement -> {
// A pending switch may still be vetoed by this active file operation.
if (project != context.project() || context.project().phase() == ProjectScope.Phase.RETIRED) {
replacement.dispose();
Expand All @@ -604,7 +605,7 @@ public CompletableFuture<Void> openCreatedScript(ProjectScope expected, Path pat
EditorContext context = editors.get();
if (context.project() != expected || project != expected || expected.phase() == ProjectScope.Phase.RETIRED)
return CompletableFuture.failedFuture(new CancellationException("Project changed while creating the script"));
return captureCurrentEntry().thenCompose(origin -> CompletableFuture.supplyAsync(() -> new ScriptView(context, path))
return captureCurrentEntry().thenCompose(origin -> CompletableFuture.supplyAsync(() -> new ScriptView(context, path), Workers.files())
.thenComposeAsync(script -> {
if (project != expected || expected.phase() == ProjectScope.Phase.RETIRED) {
script.dispose();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.project;

import com.github.minecraft_ta.totalDebugCompanion.change.WriteQueue;
import com.github.minecraft_ta.totalDebugCompanion.catalog.WorldReading;
import com.github.minecraft_ta.totaldebug.protocol.scnet.RelayFailedMessage;
import com.github.minecraft_ta.totaldebug.protocol.scnet.PlayingMessage;
Expand Down Expand Up @@ -71,6 +72,8 @@ public record PendingNavigation(NavigationTarget target, NavigationService.Activ
private final ChangeRecord changes;
/** What Companion changed in the pack, kept with the instance. */
public ChangeRecord changes() { return changes; }
/** The project's write queue, which its change pipeline writes through. */
private final WriteQueue writes = new WriteQueue();
private final ConfigChanges configChanges;
private final ConfigSettings configSettings;
public ConfigSettings configSettings() { return configSettings; }
Expand Down Expand Up @@ -113,14 +116,14 @@ public ProjectScope(Object lock, CompanionProfile profile, InstanceState state,
this.changes = Objects.requireNonNull(changes);
this.location = new GameLocation(profile.workspaceDirectory());
this.configChanges = new ConfigChanges(this.location, changes);
this.pipeline = new ChangePipeline(this.location, changes, this.configChanges.writes());
this.pipeline = new ChangePipeline(this.location, changes, this.writes);
this.configSettings = new ConfigSettings(this.configChanges, this.pipeline);
this.keyAssignments = new KeyAssignments(profile.workspaceDirectory().resolve("options.txt"));
this.keyBindings = new KeyBindingControl(this.pipeline, this.keyAssignments);
this.packs = new GamePacks(this.location);
this.world = new WorldReading(this.location, this.packs);
this.resources = new ResourceEdits(this.pipeline, this.packs, new ResourceOriginals(paths().originals()),
this.configChanges.writes(), state);
this.pipeline.writes(), state);
this.packSelections = new PackSelections(this.resources);
}

Expand Down Expand Up @@ -227,7 +230,7 @@ public List<PendingNavigation> drainNavigations() {
pending.clear();
}
// Writes still queued finish first, so each is recorded before the change record closes.
try { keyAssignments.close(); world.close(); resources.close(); configChanges.close(); closeRuntime(); } finally { try { state.close(); } finally { changes.close(); } }
try { keyAssignments.close(); world.close(); resources.close(); writes.close(); closeRuntime(); } finally { try { state.close(); } finally { changes.close(); } }
}

public String loadBreakpointScript(String name) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.minecraft_ta.totalDebugCompanion.script;

import com.github.minecraft_ta.totalDebugCompanion.util.Workers;
import com.github.minecraft_ta.totalDebugCompanion.debugger.DebugEngine;
import com.github.minecraft_ta.totalDebugCompanion.debugger.DebuggerCompletionProposal;
import com.github.minecraft_ta.totalDebugCompanion.jdt.JavaSnippetSource;
Expand Down Expand Up @@ -46,11 +47,11 @@ public CompletableFuture<List<DebuggerCompletionProposal>> complete(
int caret,
boolean explicit
) {
return CompletableFuture.supplyAsync(() -> completeNow(expression, caret));
return CompletableFuture.supplyAsync(() -> completeNow(expression, caret), Workers.files());
}

public CompletableFuture<List<DebugEngine.ExpressionToken>> tokens(String expression) {
return CompletableFuture.supplyAsync(() -> tokensNow(expression));
return CompletableFuture.supplyAsync(() -> tokensNow(expression), Workers.files());
}

public synchronized void accepted(DebuggerCompletionProposal proposal) {
Expand Down
Loading