Threading stuff, locking stuff, hopefully fix for section tracker data race??
This commit is contained in:
@@ -36,8 +36,12 @@ public class VoxyConfig implements OptionStorage<VoxyConfig> {
|
|||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
try (FileReader reader = new FileReader(path.toFile())) {
|
try (FileReader reader = new FileReader(path.toFile())) {
|
||||||
var conf = GSON.fromJson(reader, VoxyConfig.class);
|
var conf = GSON.fromJson(reader, VoxyConfig.class);
|
||||||
|
if (conf != null) {
|
||||||
conf.save();
|
conf.save();
|
||||||
return conf;
|
return conf;
|
||||||
|
} else {
|
||||||
|
Logger.error("Failed to load voxy config, resetting");
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Logger.error("Could not parse config",e);
|
Logger.error("Could not parse config",e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.cortex.voxy.client.core.model;
|
|||||||
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||||
import me.cortex.voxy.client.TimingStatistics;
|
import me.cortex.voxy.client.TimingStatistics;
|
||||||
import me.cortex.voxy.client.core.gl.GlFramebuffer;
|
import me.cortex.voxy.client.core.gl.GlFramebuffer;
|
||||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||||
@@ -16,6 +17,7 @@ import java.lang.invoke.VarHandle;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.StampedLock;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
|
import static org.lwjgl.opengl.ARBFramebufferObject.GL_COLOR_ATTACHMENT0;
|
||||||
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
|
||||||
@@ -103,7 +105,16 @@ public class ModelBakerySubsystem {
|
|||||||
this.storage.free();
|
this.storage.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//This is on this side only and done like this as only worker threads call this code
|
||||||
|
private final StampedLock seenIdsLock = new StampedLock();
|
||||||
|
private final IntOpenHashSet seenIds = new IntOpenHashSet(6000);
|
||||||
public void requestBlockBake(int blockId) {
|
public void requestBlockBake(int blockId) {
|
||||||
|
long stamp = this.seenIdsLock.writeLock();
|
||||||
|
if (!this.seenIds.add(blockId)) {
|
||||||
|
this.seenIdsLock.unlockWrite(stamp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.seenIdsLock.unlockWrite(stamp);
|
||||||
this.blockIdQueue.add(blockId);
|
this.blockIdQueue.add(blockId);
|
||||||
this.blockIdCount.incrementAndGet();
|
this.blockIdCount.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ public class RenderService<T extends AbstractSectionRenderer<J, ?>, J extends Vi
|
|||||||
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
|
this.viewportSelector = new ViewportSelector<>(this.sectionRenderer::createViewport);
|
||||||
this.renderGen = new RenderGenerationService(world, this.modelService, serviceThreadPool,
|
this.renderGen = new RenderGenerationService(world, this.modelService, serviceThreadPool,
|
||||||
this.geometryUpdateQueue::push, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets,
|
this.geometryUpdateQueue::push, this.sectionRenderer.getGeometryManager() instanceof IUsesMeshlets,
|
||||||
()->this.geometryUpdateQueue.count()<1000 && this.modelService.getProcessingCount()< 1000);
|
()->this.geometryUpdateQueue.count()<1000);
|
||||||
|
|
||||||
router.setCallbacks(this.renderGen::enqueueTask, section -> {
|
router.setCallbacks(this.renderGen::enqueueTask, section -> {
|
||||||
section.acquire();
|
section.acquire();
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.util.function.Supplier;
|
|||||||
//TODO: to add remove functionallity add a "defunked" variable to the build task and set it to true on remove
|
//TODO: to add remove functionallity add a "defunked" variable to the build task and set it to true on remove
|
||||||
// and process accordingly
|
// and process accordingly
|
||||||
public class RenderGenerationService {
|
public class RenderGenerationService {
|
||||||
|
private static final int MAX_HOLDING_SECTION_COUNT = 1000;
|
||||||
private static final AtomicInteger COUNTER = new AtomicInteger();
|
private static final AtomicInteger COUNTER = new AtomicInteger();
|
||||||
private static final class BuildTask {
|
private static final class BuildTask {
|
||||||
WorldSection section;
|
WorldSection section;
|
||||||
@@ -51,6 +52,8 @@ public class RenderGenerationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final AtomicInteger holdingSectionCount = new AtomicInteger();//Used to limit section holding
|
||||||
|
|
||||||
private final AtomicInteger taskQueueCount = new AtomicInteger();
|
private final AtomicInteger taskQueueCount = new AtomicInteger();
|
||||||
private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(320000, (a,b)-> Long.compareUnsigned(a.priority, b.priority));
|
private final PriorityBlockingQueue<BuildTask> taskQueue = new PriorityBlockingQueue<>(320000, (a,b)-> Long.compareUnsigned(a.priority, b.priority));
|
||||||
private final StampedLock taskMapLock = new StampedLock();
|
private final StampedLock taskMapLock = new StampedLock();
|
||||||
@@ -181,6 +184,9 @@ public class RenderGenerationService {
|
|||||||
if (task.hasDoneModelRequestOuter) {
|
if (task.hasDoneModelRequestOuter) {
|
||||||
other.hasDoneModelRequestOuter = true;
|
other.hasDoneModelRequestOuter = true;
|
||||||
}
|
}
|
||||||
|
if (task.section != null) {
|
||||||
|
this.holdingSectionCount.decrementAndGet();
|
||||||
|
}
|
||||||
task.section = null;
|
task.section = null;
|
||||||
shouldFreeSection = true;
|
shouldFreeSection = true;
|
||||||
task = null;
|
task = null;
|
||||||
@@ -227,8 +233,15 @@ public class RenderGenerationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Keep the lock on the section, and attach it to the task, this prevents needing to re-aquire it later
|
//Keep the lock on the section, and attach it to the task, this prevents needing to re-aquire it later
|
||||||
|
if (task.section == null) {
|
||||||
|
if (this.holdingSectionCount.get() < MAX_HOLDING_SECTION_COUNT) {
|
||||||
|
this.holdingSectionCount.incrementAndGet();
|
||||||
task.section = section;
|
task.section = section;
|
||||||
shouldFreeSection = false;
|
shouldFreeSection = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shouldFreeSection = false;
|
||||||
|
}
|
||||||
|
|
||||||
task.updatePriority();
|
task.updatePriority();
|
||||||
this.taskQueue.add(task);
|
this.taskQueue.add(task);
|
||||||
@@ -241,6 +254,9 @@ public class RenderGenerationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shouldFreeSection) {
|
if (shouldFreeSection) {
|
||||||
|
if (task != null && task.section != null) {
|
||||||
|
this.holdingSectionCount.decrementAndGet();
|
||||||
|
}
|
||||||
section.release();
|
section.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +301,7 @@ public class RenderGenerationService {
|
|||||||
var task = this.taskQueue.remove();
|
var task = this.taskQueue.remove();
|
||||||
if (task.section != null) {
|
if (task.section != null) {
|
||||||
task.section.release();
|
task.section.release();
|
||||||
|
this.holdingSectionCount.decrementAndGet();
|
||||||
}
|
}
|
||||||
if (this.taskMap.remove(task.position) != task) {
|
if (this.taskMap.remove(task.position) != task) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@@ -304,6 +321,7 @@ public class RenderGenerationService {
|
|||||||
this.taskQueueCount.decrementAndGet();
|
this.taskQueueCount.decrementAndGet();
|
||||||
if (task.section != null) {
|
if (task.section != null) {
|
||||||
task.section.release();
|
task.section.release();
|
||||||
|
this.holdingSectionCount.decrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
long stamp = this.taskMapLock.writeLock();
|
long stamp = this.taskMapLock.writeLock();
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ public abstract class ScanMesher2D {
|
|||||||
|
|
||||||
private static final int MAX_SIZE = 16;
|
private static final int MAX_SIZE = 16;
|
||||||
|
|
||||||
|
|
||||||
// is much faster if implemented inline into parent
|
// is much faster if implemented inline into parent
|
||||||
private final long[] rowData = new long[32];
|
private final long[] rowData = new long[32];
|
||||||
private final int[] rowLength = new int[32];//How long down does a row entry go
|
private final int[] rowLength = new int[32];//How long down does a row entry go
|
||||||
@@ -28,7 +27,7 @@ public abstract class ScanMesher2D {
|
|||||||
//If the previous data is not zero, that means it was not merge-able, so emit it at the pos
|
//If the previous data is not zero, that means it was not merge-able, so emit it at the pos
|
||||||
if (this.currentData!=0) {
|
if (this.currentData!=0) {
|
||||||
if ((this.rowBitset&(1<<31))!=0) {
|
if ((this.rowBitset&(1<<31))!=0) {
|
||||||
emitQuad(31, ((this.currentIndex-1)>>5)-1, this.rowLength[31], this.rowDepth[31], this.rowData[31]);
|
this.emitQuad(31, ((this.currentIndex-1)>>5)-1, this.rowLength[31], this.rowDepth[31], this.rowData[31]);
|
||||||
}
|
}
|
||||||
this.rowBitset |= 1<<31;
|
this.rowBitset |= 1<<31;
|
||||||
this.rowLength[31] = this.currentSum;
|
this.rowLength[31] = this.currentSum;
|
||||||
@@ -84,14 +83,14 @@ public abstract class ScanMesher2D {
|
|||||||
private void emitRanged(int msk) {
|
private void emitRanged(int msk) {
|
||||||
{//Emit quads that cover the previous indices
|
{//Emit quads that cover the previous indices
|
||||||
int rowSet = this.rowBitset&msk;
|
int rowSet = this.rowBitset&msk;
|
||||||
|
this.rowBitset &= ~msk;
|
||||||
while (rowSet!=0) {//Need to emit quads that would have skipped, note that this does not include the current index
|
while (rowSet!=0) {//Need to emit quads that would have skipped, note that this does not include the current index
|
||||||
int index = Integer.numberOfTrailingZeros(rowSet);
|
int index = Integer.numberOfTrailingZeros(rowSet);
|
||||||
rowSet &= ~Integer.lowestOneBit(rowSet);
|
rowSet &= ~Integer.lowestOneBit(rowSet);
|
||||||
|
|
||||||
//Emit the quad, dont need to clear the data since it not existing in the bitmask is implicit no data
|
//Emit the quad, dont need to clear the data since it not existing in the bitmask is implicit no data
|
||||||
this.emitQuad(index, ((this.currentIndex-1)>>5)-1, this.rowLength[index], this.rowDepth[index], this.rowData[index]);
|
this.emitQuad(index, (this.currentIndex>>5)-1, this.rowLength[index], this.rowDepth[index], this.rowData[index]);
|
||||||
}
|
}
|
||||||
this.rowBitset &= ~msk;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,10 +106,14 @@ public abstract class ScanMesher2D {
|
|||||||
this.currentIndex += count;
|
this.currentIndex += count;
|
||||||
*/
|
*/
|
||||||
if (count == 0) return;
|
if (count == 0) return;
|
||||||
|
if (this.currentData!=0) {
|
||||||
this.putNext(0);
|
this.putNext(0);
|
||||||
if (1<count) {
|
count--;
|
||||||
this.emitRanged(((1 << (Math.min(count, 32)-1)) - 1) << (this.currentIndex & 31));
|
}
|
||||||
this.currentIndex += count - 1;
|
if (0<count) {
|
||||||
|
int msk = (int) ((1L<<Math.min(32, count))-1) << (this.currentIndex & 31);
|
||||||
|
this.emitRanged(msk);
|
||||||
|
this.currentIndex += count;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.cortex.voxy.client.mixin.minecraft;
|
package me.cortex.voxy.client.mixin.minecraft;
|
||||||
|
|
||||||
import me.cortex.voxy.client.GPUSelectorWindows2;
|
import me.cortex.voxy.client.GPUSelectorWindows2;
|
||||||
|
import me.cortex.voxy.common.util.ThreadUtils;
|
||||||
import net.minecraft.client.WindowEventHandler;
|
import net.minecraft.client.WindowEventHandler;
|
||||||
import net.minecraft.client.WindowSettings;
|
import net.minecraft.client.WindowSettings;
|
||||||
import net.minecraft.client.util.MonitorTracker;
|
import net.minecraft.client.util.MonitorTracker;
|
||||||
@@ -19,5 +20,8 @@ public class MixinWindow {
|
|||||||
if (!prop.equals("NO")) {
|
if (!prop.equals("NO")) {
|
||||||
GPUSelectorWindows2.doSelector(Integer.parseInt(prop));
|
GPUSelectorWindows2.doSelector(Integer.parseInt(prop));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Force the current thread priority to be realtime
|
||||||
|
ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_PRIORITY_TIME_CRITICAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package me.cortex.voxy.common.thread;
|
|||||||
|
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.Pair;
|
import me.cortex.voxy.common.util.Pair;
|
||||||
|
import me.cortex.voxy.common.util.ThreadUtils;
|
||||||
|
|
||||||
import java.lang.invoke.VarHandle;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
@@ -17,12 +18,6 @@ import java.util.function.Supplier;
|
|||||||
//TODO: could also probably replace all of this with just VirtualThreads and a Executors.newThreadPerTaskExecutor with a fixed thread pool
|
//TODO: could also probably replace all of this with just VirtualThreads and a Executors.newThreadPerTaskExecutor with a fixed thread pool
|
||||||
// it is probably better anyway
|
// it is probably better anyway
|
||||||
public class ServiceThreadPool {
|
public class ServiceThreadPool {
|
||||||
|
|
||||||
private static final ThreadMXBean THREAD_BEAN = ManagementFactory.getThreadMXBean();
|
|
||||||
static {
|
|
||||||
THREAD_BEAN.setThreadCpuTimeEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private volatile boolean running = true;
|
private volatile boolean running = true;
|
||||||
private volatile boolean releaseNow = false;
|
private volatile boolean releaseNow = false;
|
||||||
private Thread[] workers = new Thread[0];
|
private Thread[] workers = new Thread[0];
|
||||||
@@ -138,20 +133,25 @@ public class ServiceThreadPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void worker(int threadId) {
|
private void worker(int threadId) {
|
||||||
long seed = 1234342;
|
ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_PRIORITY_LOWEST);
|
||||||
int revolvingSelector = 0;
|
//ThreadUtils.SetSelfThreadPriorityWin32(ThreadUtils.WIN32_THREAD_MODE_BACKGROUND_BEGIN);
|
||||||
|
|
||||||
|
long[] seed = new long[]{1234342^(threadId*124987198651981L+215987981111L)};
|
||||||
|
int[] revolvingSelector = new int[1];
|
||||||
|
|
||||||
double rollRuntimeRatio = 0;
|
|
||||||
double rollCpuTimeDelta = 0;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
this.jobCounter.acquireUninterruptibly();
|
this.jobCounter.acquireUninterruptibly();
|
||||||
if (!this.running) {
|
if (!this.running) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
//This is because of JIT moment (it cant really replace methods while they are executing afak)
|
||||||
|
this.worker_work(threadId, seed, revolvingSelector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void worker_work(int threadId, long[] seedIO, int[] revolvingSelectorIO) {
|
||||||
final int ATTEMPT_COUNT = 50;
|
final int ATTEMPT_COUNT = 50;
|
||||||
int attempts = ATTEMPT_COUNT;
|
int attempts = ATTEMPT_COUNT;
|
||||||
outer:
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (attempts < ATTEMPT_COUNT-2) {
|
if (attempts < ATTEMPT_COUNT-2) {
|
||||||
try {
|
try {
|
||||||
@@ -184,9 +184,10 @@ public class ServiceThreadPool {
|
|||||||
this.jobCounter.release();//Release the job we acquired
|
this.jobCounter.release();//Release the job we acquired
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
long seed = seedIO[0]*1984691871L+1497210975L;
|
||||||
seed = (seed ^ seed >>> 30) * -4658895280553007687L;
|
seed = (seed ^ (seed >>> 30)) * -4658895280553007687L;
|
||||||
seed = (seed ^ seed >>> 27) * -7723592293110705685L;
|
seed = (seed ^ (seed >>> 27)) * -7723592293110705685L;
|
||||||
|
seedIO[0] = seed;
|
||||||
long clamped = seed&((1L<<63)-1);
|
long clamped = seed&((1L<<63)-1);
|
||||||
long weight = this.totalJobWeight.get();
|
long weight = this.totalJobWeight.get();
|
||||||
if (weight == 0) {
|
if (weight == 0) {
|
||||||
@@ -194,16 +195,28 @@ public class ServiceThreadPool {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServiceSlice service = ref[0];
|
ServiceSlice service = null;
|
||||||
for (int i = 0; i < ref.length; i++) {
|
for (int i = 0; i < ref.length; i++) {
|
||||||
service = ref[(int) ((clamped+i) % ref.length)];
|
var service2 = ref[(int) ((clamped+i) % ref.length)];
|
||||||
if (service.workConditionMet()) {
|
if (service2.hasJobs() && service2.workConditionMet()) {
|
||||||
|
service = service2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (service == null) {
|
||||||
|
Logger.warn("No available jobs, sleeping releasing returning");
|
||||||
|
try {
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
this.jobCounter.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
//1 in 64 chance just to pick a service that has a task, in a cycling manor, this is to keep at least one service from overloading all services constantly
|
//1 in 64 chance just to pick a service that has a task, in a cycling manor, this is to keep at least one service from overloading all services constantly
|
||||||
if (((seed>>10)&63) == 0) {
|
if (((seed>>10)&63) == 0) {
|
||||||
|
int revolvingSelector = revolvingSelectorIO[0];
|
||||||
for (int i = 0; i < ref.length; i++) {
|
for (int i = 0; i < ref.length; i++) {
|
||||||
int idx = (i+revolvingSelector)%ref.length;
|
int idx = (i+revolvingSelector)%ref.length;
|
||||||
var slice = ref[idx];
|
var slice = ref[idx];
|
||||||
@@ -213,7 +226,7 @@ public class ServiceThreadPool {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
revolvingSelectorIO[0] = revolvingSelector;
|
||||||
} else {
|
} else {
|
||||||
long chosenNumber = clamped % weight;
|
long chosenNumber = clamped % weight;
|
||||||
for (var slice : ref) {
|
for (var slice : ref) {
|
||||||
@@ -224,40 +237,12 @@ public class ServiceThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
VarHandle.fullFence();
|
|
||||||
long realTimeStart = System.nanoTime();
|
|
||||||
long cpuTimeStart = THREAD_BEAN.getCurrentThreadCpuTime();
|
|
||||||
VarHandle.fullFence();
|
|
||||||
*/
|
|
||||||
|
|
||||||
//Run the job
|
//Run the job
|
||||||
if (!service.doRun(threadId)) {
|
if (!service.doRun(threadId)) {
|
||||||
//Didnt consume the job, find a new job
|
//Didnt consume the job, find a new job
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
VarHandle.fullFence();
|
|
||||||
long cpuTimeEnd = THREAD_BEAN.getCurrentThreadCpuTime();
|
|
||||||
long realTimeEnd = System.nanoTime();
|
|
||||||
VarHandle.fullFence();
|
|
||||||
|
|
||||||
long realTimeDelta = realTimeEnd - realTimeStart;
|
|
||||||
long cpuTimeDelta = cpuTimeEnd - cpuTimeStart;
|
|
||||||
|
|
||||||
//Realtime should always be bigger or equal to cpu time
|
|
||||||
double runtimeRatio = ((double)cpuTimeDelta)/((double)realTimeDelta);
|
|
||||||
rollRuntimeRatio = (rollRuntimeRatio*0.95)+runtimeRatio*0.05;
|
|
||||||
rollCpuTimeDelta = (rollCpuTimeDelta*0.95)+cpuTimeDelta*0.05;
|
|
||||||
//Attempt to self balance cpu load
|
|
||||||
VarHandle.fullFence();
|
|
||||||
try {
|
|
||||||
if (rollRuntimeRatio > 0.8) {
|
|
||||||
Thread.sleep(Math.max((long) ((rollRuntimeRatio - 0.5) * (rollCpuTimeDelta / (1000 * 1000))), 1));
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//Consumed a job from the service, decrease weight by the amount
|
//Consumed a job from the service, decrease weight by the amount
|
||||||
if (this.totalJobWeight.addAndGet(-service.weightPerJob)<0) {
|
if (this.totalJobWeight.addAndGet(-service.weightPerJob)<0) {
|
||||||
@@ -266,7 +251,6 @@ public class ServiceThreadPool {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
private void handleUncaughtException(Thread thread, Throwable throwable) {
|
||||||
System.err.println("Service worker thread has exploded unexpectedly! this is really not good very very bad.");
|
System.err.println("Service worker thread has exploded unexpectedly! this is really not good very very bad.");
|
||||||
|
|||||||
32
src/main/java/me/cortex/voxy/common/util/ThreadUtils.java
Normal file
32
src/main/java/me/cortex/voxy/common/util/ThreadUtils.java
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package me.cortex.voxy.common.util;
|
||||||
|
|
||||||
|
import org.lwjgl.system.JNI;
|
||||||
|
import org.lwjgl.system.Platform;
|
||||||
|
import org.lwjgl.system.windows.Kernel32;
|
||||||
|
|
||||||
|
//Platform specific code to assist in thread utilities
|
||||||
|
public class ThreadUtils {
|
||||||
|
public static final int WIN32_THREAD_PRIORITY_TIME_CRITICAL = 15;
|
||||||
|
public static final int WIN32_THREAD_PRIORITY_LOWEST = -2;
|
||||||
|
public static final int WIN32_THREAD_MODE_BACKGROUND_BEGIN = 0x00010000;
|
||||||
|
public static final int WIN32_THREAD_MODE_BACKGROUND_END = 0x00020000;
|
||||||
|
private static final boolean isWindows = Platform.get() == Platform.WINDOWS;
|
||||||
|
private static final long SetThreadPriority;
|
||||||
|
static {
|
||||||
|
if (isWindows) {
|
||||||
|
SetThreadPriority = Kernel32.getLibrary().getFunctionAddress("SetThreadPriority");
|
||||||
|
} else {
|
||||||
|
SetThreadPriority = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean SetSelfThreadPriorityWin32(int priority) {
|
||||||
|
if (SetThreadPriority == 0 || !isWindows) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (JNI.callPI(Kernel32.GetCurrentThread(), priority, SetThreadPriority)==0) {
|
||||||
|
throw new IllegalStateException("Operation failed");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,14 +125,18 @@ public class ActiveSectionTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
section.acquire();
|
section.acquire();
|
||||||
|
VarHandle.fullFence();//Do not reorder setting this object
|
||||||
holder.obj = section;
|
holder.obj = section;
|
||||||
|
VarHandle.fullFence();
|
||||||
if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell
|
if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell
|
||||||
section.release();
|
section.release();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return section;
|
return section;
|
||||||
} else {
|
} else {
|
||||||
|
VarHandle.fullFence();
|
||||||
while ((section = holder.obj) == null) {
|
while ((section = holder.obj) == null) {
|
||||||
|
VarHandle.fullFence();
|
||||||
Thread.onSpinWait();
|
Thread.onSpinWait();
|
||||||
Thread.yield();
|
Thread.yield();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ public class VoxyInstance {
|
|||||||
public void addDebug(List<String> debug) {
|
public void addDebug(List<String> debug) {
|
||||||
debug.add("Voxy Core: " + VoxyCommon.MOD_VERSION);
|
debug.add("Voxy Core: " + VoxyCommon.MOD_VERSION);
|
||||||
debug.add("MemoryBuffer, Count/Size (mb): " + MemoryBuffer.getCount() + "/" + (MemoryBuffer.getTotalSize()/1_000_000));
|
debug.add("MemoryBuffer, Count/Size (mb): " + MemoryBuffer.getCount() + "/" + (MemoryBuffer.getTotalSize()/1_000_000));
|
||||||
debug.add("I/S: " + this.ingestService.getTaskCount() + "/" + this.savingService.getTaskCount());
|
debug.add("I/S/AWSC: " + this.ingestService.getTaskCount() + "/" + this.savingService.getTaskCount() + "/[" + this.activeWorlds.stream().map(a->""+a.getActiveSectionCount()).collect(Collectors.joining(", ")) + "]");//Active world section count
|
||||||
debug.add("AWSC: [" + this.activeWorlds.stream().map(a->""+a.getActiveSectionCount()).collect(Collectors.joining(", ")) + "]");//Active world section count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
|
|||||||
Reference in New Issue
Block a user