Allow a single low-priority worker thread when cause_lag_by_disabling_threads is enabled

On a system with few cores, we should still benefit from using one low-priority
background thread for worldgen, because it avoids the server thread stopping
to handle it itself. The thread will be blocked
from progressing while higher-priority work (e.g. rendering or server ticking)
is in progress.
This commit is contained in:
embeddedt 2026-03-28 21:43:09 -04:00
parent db13f39b30
commit 4ff7d4c554
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
3 changed files with 65 additions and 45 deletions

View File

@ -1,7 +1,7 @@
package org.embeddedt.modernfix.common.mixin.feature.cause_lag_by_disabling_threads;
import net.minecraft.Util;
import org.embeddedt.modernfix.util.DirectExecutorService;
import org.embeddedt.modernfix.util.SingleThreadedWorkerService;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
@ -12,5 +12,5 @@ import java.util.concurrent.ExecutorService;
@Mixin(Util.class)
public class UtilMixin {
@Shadow @Final @Mutable
private static final ExecutorService BACKGROUND_EXECUTOR = new DirectExecutorService();
private static final ExecutorService BACKGROUND_EXECUTOR = new SingleThreadedWorkerService();
}

View File

@ -1,43 +0,0 @@
package org.embeddedt.modernfix.util;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.TimeUnit;
public class DirectExecutorService extends AbstractExecutorService {
private boolean isShutdown;
@Override
public void shutdown() {
isShutdown = true;
}
@NotNull
@Override
public List<Runnable> shutdownNow() {
isShutdown = true;
return List.of();
}
@Override
public boolean isShutdown() {
return isShutdown;
}
@Override
public boolean isTerminated() {
return isShutdown;
}
@Override
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
return true;
}
@Override
public void execute(@NotNull Runnable command) {
command.run();
}
}

View File

@ -0,0 +1,63 @@
package org.embeddedt.modernfix.util;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* Like {@link Executors#newSingleThreadExecutor()}, but handles the case where the background executor schedules
* a task to itself and waits for it the way a direct executor would.
*/
public class SingleThreadedWorkerService extends AbstractExecutorService {
private final AtomicReference<Thread> thread = new AtomicReference<>();
private final ExecutorService executorService;
public SingleThreadedWorkerService() {
this.executorService = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r, "Worker-Main");
t.setPriority(Thread.MIN_PRIORITY);
thread.set(t);
return t;
});
}
@Override
public void shutdown() {
executorService.shutdown();
}
@NotNull
@Override
public List<Runnable> shutdownNow() {
return executorService.shutdownNow();
}
@Override
public boolean isShutdown() {
return executorService.isShutdown();
}
@Override
public boolean isTerminated() {
return executorService.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
return executorService.awaitTermination(timeout, unit);
}
@Override
public void execute(@NotNull Runnable command) {
if (Thread.currentThread() == thread.get()) {
command.run();
} else {
executorService.execute(command);
}
}
}