mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-28 18:04:47 +00:00
Convert MainThread into a priority queue
This uses a similar approach to ComputerThread: executors store how long they've spent executing tasks. We then use that time to prioritise executors. One should note that we use the current runtime at the point of adding to the queue - external tasks will not contribute towards it until a later execution.
This commit is contained in:
parent
245bf26480
commit
7799b8d4cb
@ -34,6 +34,17 @@ public interface IWorkMonitor
|
||||
*/
|
||||
boolean canWork();
|
||||
|
||||
/**
|
||||
* If the owning computer is currently allowed to execute work, and has ample time to do so.
|
||||
*
|
||||
* This is effectively a more restrictive form of {@link #canWork()}. One should use that in order to determine if
|
||||
* you may do an initial piece of work, and {@link #shouldWork()} to determine if any additional task may be
|
||||
* performed.
|
||||
*
|
||||
* @return If we should execute work right now.
|
||||
*/
|
||||
boolean shouldWork();
|
||||
|
||||
/**
|
||||
* Inform the monitor how long some piece of work took to execute.
|
||||
*
|
||||
|
@ -10,9 +10,8 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.ILuaTask;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Queue;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
@ -41,7 +40,14 @@ public class MainThread
|
||||
/**
|
||||
* The queue of {@link MainThreadExecutor}s with tasks to perform.
|
||||
*/
|
||||
private static final Queue<MainThreadExecutor> executors = new ArrayDeque<>();
|
||||
private static final TreeSet<MainThreadExecutor> executors = new TreeSet<>( ( a, b ) -> {
|
||||
if( a == b ) return 0; // Should never happen, but let's be consistent here
|
||||
|
||||
long at = a.virtualTime, bt = b.virtualTime;
|
||||
if( at == bt ) return Integer.compare( a.hashCode(), b.hashCode() );
|
||||
return at < bt ? -1 : 1;
|
||||
} );
|
||||
;
|
||||
|
||||
/**
|
||||
* The set of executors which went over budget in a previous tick, and are waiting for their time to run down.
|
||||
@ -71,17 +77,29 @@ public class MainThread
|
||||
*/
|
||||
private static boolean canExecute = true;
|
||||
|
||||
private static long minimumTime = 0;
|
||||
|
||||
public static long getUniqueTaskID()
|
||||
{
|
||||
return lastTaskId.incrementAndGet();
|
||||
}
|
||||
|
||||
static void queue( @Nonnull MainThreadExecutor executor )
|
||||
static void queue( @Nonnull MainThreadExecutor executor, boolean sleeper )
|
||||
{
|
||||
synchronized( executors )
|
||||
{
|
||||
if( executor.onQueue ) throw new IllegalStateException( "Cannot queue already queued executor" );
|
||||
executor.onQueue = true;
|
||||
executor.updateTime();
|
||||
|
||||
// We're not currently on the queue, so update its current execution time to
|
||||
// ensure its at least as high as the minimum.
|
||||
long newRuntime = minimumTime;
|
||||
|
||||
// Slow down new computers a little bit.
|
||||
if( executor.virtualTime == 0 ) newRuntime += ComputerCraft.maxMainComputerTime;
|
||||
|
||||
executor.virtualTime = Math.max( newRuntime, executor.virtualTime );
|
||||
|
||||
executors.add( executor );
|
||||
}
|
||||
@ -131,7 +149,7 @@ public class MainThread
|
||||
MainThreadExecutor executor;
|
||||
synchronized( executors )
|
||||
{
|
||||
executor = executors.poll();
|
||||
executor = executors.pollFirst();
|
||||
}
|
||||
if( executor == null ) break;
|
||||
|
||||
@ -139,12 +157,19 @@ public class MainThread
|
||||
executor.execute();
|
||||
|
||||
long taskStop = System.nanoTime();
|
||||
if( executor.afterExecute( taskStop - taskStart ) )
|
||||
synchronized( executors )
|
||||
{
|
||||
synchronized( executors )
|
||||
if( executor.afterExecute( taskStop - taskStart ) ) executors.add( executor );
|
||||
|
||||
// Compute the new minimum time (including the next task on the queue too). Note that this may also include
|
||||
// time spent in external tasks.
|
||||
long newMinimum = executor.virtualTime;
|
||||
if( !executors.isEmpty() )
|
||||
{
|
||||
executors.add( executor );
|
||||
MainThreadExecutor next = executors.first();
|
||||
if( next.virtualTime < newMinimum ) newMinimum = next.virtualTime;
|
||||
}
|
||||
minimumTime = Math.max( minimumTime, newMinimum );
|
||||
}
|
||||
|
||||
if( taskStop >= deadline ) break;
|
||||
@ -158,6 +183,7 @@ public class MainThread
|
||||
currentTick = 0;
|
||||
budget = 0;
|
||||
canExecute = true;
|
||||
minimumTime = 0;
|
||||
lastTaskId.set( 0 );
|
||||
cooling.clear();
|
||||
synchronized( executors )
|
||||
|
@ -106,6 +106,10 @@ final class MainThreadExecutor implements IWorkMonitor
|
||||
*/
|
||||
private State state = State.COOL;
|
||||
|
||||
private long pendingTime;
|
||||
|
||||
long virtualTime;
|
||||
|
||||
MainThreadExecutor( Computer computer )
|
||||
{
|
||||
this.computer = computer;
|
||||
@ -122,7 +126,7 @@ final class MainThreadExecutor implements IWorkMonitor
|
||||
synchronized( queueLock )
|
||||
{
|
||||
if( tasks.size() >= MAX_TASKS || !tasks.offer( runnable ) ) return false;
|
||||
if( !onQueue && state == State.COOL ) MainThread.queue( this );
|
||||
if( !onQueue && state == State.COOL ) MainThread.queue( this, true );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -152,6 +156,8 @@ final class MainThreadExecutor implements IWorkMonitor
|
||||
|
||||
synchronized( queueLock )
|
||||
{
|
||||
virtualTime += time;
|
||||
updateTime();
|
||||
if( state != State.COOL || tasks.isEmpty() ) return onQueue = false;
|
||||
return true;
|
||||
}
|
||||
@ -168,10 +174,21 @@ final class MainThreadExecutor implements IWorkMonitor
|
||||
return state != State.COOLING && MainThread.canExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldWork()
|
||||
{
|
||||
return state == State.COOL && MainThread.canExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackWork( long time, @Nonnull TimeUnit unit )
|
||||
{
|
||||
long nanoTime = unit.toNanos( time );
|
||||
synchronized( queueLock )
|
||||
{
|
||||
pendingTime += nanoTime;
|
||||
}
|
||||
|
||||
consumeTime( nanoTime );
|
||||
MainThread.consumeTime( nanoTime );
|
||||
}
|
||||
@ -213,11 +230,17 @@ final class MainThreadExecutor implements IWorkMonitor
|
||||
state = State.COOL;
|
||||
synchronized( queueLock )
|
||||
{
|
||||
if( !tasks.isEmpty() && !onQueue ) MainThread.queue( this );
|
||||
if( !tasks.isEmpty() && !onQueue ) MainThread.queue( this, false );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateTime()
|
||||
{
|
||||
virtualTime += pendingTime;
|
||||
pendingTime = 0;
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
COOL,
|
||||
|
Loading…
Reference in New Issue
Block a user