1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-12-12 11:10:29 +00:00

Introduce IWorkMonitor into the public API

This effectively acts as a public interface to canExecuteExternal() and
consumeTime(). It's hopefully sufficiently general that we can mess
around with the backend as much as we like in the future.

One thing to note here is that this is based on a polling API, as it's
largely intended for people running work every tick. It would be
possible to adapt this with callbacks for when work is available,
etc..., but that was not needed immediately.

This also removes IComputerOwned, as Plethora no longer needs it.
This commit is contained in:
SquidDev 2019-03-20 21:26:56 +00:00
parent 853e2622a1
commit 5d05205d69
10 changed files with 138 additions and 104 deletions

View File

@ -9,6 +9,8 @@ package dan200.computercraft.api.peripheral;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -179,7 +181,7 @@ public interface IComputerAccess
} }
/** /**
* Get a reachable peripheral with the given attachement name. This is a equivalent to * Get a reachable peripheral with the given attachment name. This is a equivalent to
* {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more performant. * {@link #getAvailablePeripherals()}{@code .get(name)}, though may be more performant.
* *
* @param name The peripheral's attached name * @param name The peripheral's attached name
@ -191,4 +193,23 @@ public interface IComputerAccess
{ {
return null; return null;
} }
/**
* Get a {@link IWorkMonitor} for tasks your peripheral might execute on the main (server) thread.
*
* This should be used to ensure your peripheral integrates with ComputerCraft's monitoring and limiting of how much
* server time each computer consumes. You should not need to use this if you use
* {@link ILuaContext#issueMainThreadTask(ILuaTask)} - this is intended for mods with their own system for running
* work on the main thread.
*
* Please note that the returned implementation is <em>not</em> thread-safe, and should only be used from the main
* thread.
*
* @return The work monitor for the main thread, or {@code null} if this computer does not have one.
*/
@Nullable
default IWorkMonitor getMainThreadMonitor()
{
return null;
}
} }

View File

@ -0,0 +1,68 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.api.peripheral;
import javax.annotation.Nonnull;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* Monitors "work" associated with a computer, keeping track of how much a computer has done, and ensuring every
* computer receives a fair share of any processing time.
*
* This is primarily intended for work done by peripherals on the main thread (such as on a tile entity's tick), but
* could be used for other purposes (such as complex computations done on another thread).
*
* Before running a task, one should call {@link #canWork()} to determine if the computer is currently allowed to
* execute work. If that returns true, you should execute the task and use {@link #trackWork(long, TimeUnit)} to inform
* the monitor how long that task took.
*
* Alternatively, use {@link #runWork(Runnable)} to run and keep track of work.
*
* @see IComputerAccess#getMainThreadMonitor()
*/
public interface IWorkMonitor
{
/**
* If the owning computer is currently allowed to execute work.
*
* @return If we can execute work right now.
*/
boolean canWork();
/**
* Inform the monitor how long some piece of work took to execute.
*
* @param time The time some task took to run
* @param unit The unit that {@code time} was measured in.
*/
void trackWork( long time, @Nonnull TimeUnit unit );
/**
* Run a task if possible, and inform the monitor of how long it took.
*
* @param runnable The task to run.
* @return If the task was actually run (namely, {@link #canWork()} returned {@code true}).
*/
default boolean runWork( @Nonnull Runnable runnable )
{
Objects.requireNonNull( runnable, "runnable should not be null" );
if( !canWork() ) return false;
long start = System.nanoTime();
try
{
runnable.run();
}
finally
{
trackWork( System.nanoTime() - start, TimeUnit.NANOSECONDS );
}
return true;
}
}

View File

@ -10,8 +10,7 @@ import com.google.common.base.Preconditions;
import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.computer.IComputerOwned;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException; import dan200.computercraft.core.filesystem.FileSystemException;
@ -21,7 +20,7 @@ import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
public abstract class ComputerAccess implements IComputerAccess, IComputerOwned public abstract class ComputerAccess implements IComputerAccess
{ {
private final IAPIEnvironment m_environment; private final IAPIEnvironment m_environment;
private final Set<String> m_mounts = new HashSet<>(); private final Set<String> m_mounts = new HashSet<>();
@ -128,9 +127,9 @@ public abstract class ComputerAccess implements IComputerAccess, IComputerOwned
@Nullable @Nullable
@Override @Override
public Computer getComputer() public IWorkMonitor getMainThreadMonitor()
{ {
return m_environment.getComputer(); return m_environment.getMainThreadMonitor();
} }
private String findFreeLocation( String desiredLoc ) private String findFreeLocation( String desiredLoc )

View File

@ -7,9 +7,8 @@
package dan200.computercraft.core.apis; package dan200.computercraft.core.apis;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.computer.IComputerEnvironment; import dan200.computercraft.core.computer.IComputerEnvironment;
import dan200.computercraft.core.computer.IComputerOwned;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.core.tracking.TrackingField;
@ -17,7 +16,7 @@ import dan200.computercraft.core.tracking.TrackingField;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public interface IAPIEnvironment extends IComputerOwned public interface IAPIEnvironment
{ {
String[] SIDE_NAMES = new String[] { String[] SIDE_NAMES = new String[] {
"bottom", "top", "back", "front", "right", "left", "bottom", "top", "back", "front", "right", "left",
@ -30,15 +29,14 @@ public interface IAPIEnvironment extends IComputerOwned
void onPeripheralChanged( int side, @Nullable IPeripheral newPeripheral ); void onPeripheralChanged( int side, @Nullable IPeripheral newPeripheral );
} }
@Nonnull
@Override
Computer getComputer();
int getComputerID(); int getComputerID();
@Nonnull @Nonnull
IComputerEnvironment getComputerEnvironment(); IComputerEnvironment getComputerEnvironment();
@Nonnull
IWorkMonitor getMainThreadMonitor();
@Nonnull @Nonnull
Terminal getTerminal(); Terminal getTerminal();

View File

@ -9,6 +9,7 @@ package dan200.computercraft.core.computer;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
@ -126,30 +127,9 @@ public class Computer
return serverExecutor.enqueue( runnable ); return serverExecutor.enqueue( runnable );
} }
/** public IWorkMonitor getMainThreadMonitor()
* If this computer is allowed to execute work on the main thread.
*
* One only needs to use this if executing work outside of {@link #queueMainThread(Runnable)}.
*
* @return If we can execute work on the main thread this tick.
* @see #afterExecuteMainThread(long)
*/
public boolean canExecuteMainThread()
{ {
return MainThread.canExecute() && serverExecutor.canExecuteExternal(); return serverExecutor;
}
/**
* Increment the time taken to execute work this tick.
*
* One only needs to use this if executing work outside of {@link #queueMainThread(Runnable)}.
*
* @param time The time, in nanoseconds.
* @see #canExecuteMainThread()
*/
public void afterExecuteMainThread( long time )
{
serverExecutor.afterExecuteExternal( time );
} }
public int getID() public int getID()

View File

@ -7,6 +7,7 @@
package dan200.computercraft.core.computer; package dan200.computercraft.core.computer;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.apis.IAPIEnvironment; import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.filesystem.FileSystem; import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
@ -58,13 +59,6 @@ public final class Environment implements IAPIEnvironment
this.computer = computer; this.computer = computer;
} }
@Nonnull
@Override
public Computer getComputer()
{
return computer;
}
@Override @Override
public int getComputerID() public int getComputerID()
{ {
@ -78,6 +72,13 @@ public final class Environment implements IAPIEnvironment
return computer.getComputerEnvironment(); return computer.getComputerEnvironment();
} }
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor()
{
return computer.getMainThreadMonitor();
}
@Nonnull @Nonnull
@Override @Override
public Terminal getTerminal() public Terminal getTerminal()

View File

@ -1,33 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.computer;
import dan200.computercraft.api.peripheral.IComputerAccess;
import javax.annotation.Nullable;
/**
* {@link IComputerOwned} marks objects which are known to belong to a computer.
*
* The primary purpose of this is to allow Plethora (and potentially other mods) to run the various tracking methods
* on {@link Computer}.
*
* You can generally assume {@link IComputerAccess} implements this interface, though you should always check first.
*
* @see dan200.computercraft.core.apis.ComputerAccess
* @see dan200.computercraft.shared.peripheral.modem.wired.WiredModemPeripheral and the peripheral wrapper
*/
public interface IComputerOwned
{
/**
* Get the computer associated with this object
*
* @return The associated object, or {@code null} if none is known.
*/
@Nullable
Computer getComputer();
}

View File

@ -6,12 +6,15 @@
package dan200.computercraft.core.computer; package dan200.computercraft.core.computer;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.turtle.core.TurtleBrain;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import javax.annotation.Nonnull;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.TimeUnit;
import static dan200.computercraft.core.computer.MainThread.MAX_COMPUTER_TIME; import static dan200.computercraft.core.computer.MainThread.MAX_COMPUTER_TIME;
@ -48,7 +51,7 @@ import static dan200.computercraft.core.computer.MainThread.MAX_COMPUTER_TIME;
* @see Computer#queueMainThread(Runnable) * @see Computer#queueMainThread(Runnable)
* @see Computer#afterExecuteMainThread(long) * @see Computer#afterExecuteMainThread(long)
*/ */
final class MainThreadExecutor final class MainThreadExecutor implements IWorkMonitor
{ {
/** /**
* The maximum number of {@link MainThread} tasks allowed on the queue. * The maximum number of {@link MainThread} tasks allowed on the queue.
@ -100,7 +103,7 @@ final class MainThreadExecutor
/** /**
* The current state of this executor. * The current state of this executor.
* *
* @see #canExecuteExternal() * @see #canWork()
*/ */
private State state = State.COOL; private State state = State.COOL;
@ -155,26 +158,23 @@ final class MainThreadExecutor
} }
} }
/**
* Update the time taken to run an external task (one not part of {@link #tasks}), incrementing the appropriate
* statistics.
*
* @param time The time some task took to run
*/
void afterExecuteExternal( long time )
{
consumeTime( time );
MainThread.consumeTime( time );
}
/** /**
* Whether we should execute "external" tasks (ones not part of {@link #tasks}). * Whether we should execute "external" tasks (ones not part of {@link #tasks}).
* *
* @return Whether we can execute external tasks. * @return Whether we can execute external tasks.
*/ */
boolean canExecuteExternal() @Override
public boolean canWork()
{ {
return state != State.COOLING; return state != State.COOLING && MainThread.canExecute();
}
@Override
public void trackWork( long time, @Nonnull TimeUnit unit )
{
long nanoTime = unit.toNanos( time );
consumeTime( nanoTime );
MainThread.consumeTime( nanoTime );
} }
private void consumeTime( long time ) private void consumeTime( long time )

View File

@ -16,8 +16,7 @@ import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.network.wired.IWiredSender; import dan200.computercraft.api.network.wired.IWiredSender;
import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.computer.IComputerOwned;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral; import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -267,7 +266,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
return wrappers == null ? null : wrappers.get( remoteName ); return wrappers == null ? null : wrappers.get( remoteName );
} }
private static class RemotePeripheralWrapper implements IComputerAccess, IComputerOwned private static class RemotePeripheralWrapper implements IComputerAccess
{ {
private final WiredModemElement m_element; private final WiredModemElement m_element;
private final IPeripheral m_peripheral; private final IPeripheral m_peripheral;
@ -376,6 +375,13 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
m_computer.queueEvent( event, arguments ); m_computer.queueEvent( event, arguments );
} }
@Nullable
@Override
public IWorkMonitor getMainThreadMonitor()
{
return m_computer.getMainThreadMonitor();
}
@Nonnull @Nonnull
@Override @Override
public String getAttachmentName() public String getAttachmentName()
@ -402,12 +408,5 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW
return m_element.getRemotePeripherals().get( name ); return m_element.getRemotePeripherals().get( name );
} }
} }
@Nullable
@Override
public Computer getComputer()
{
return m_computer instanceof IComputerOwned ? ((IComputerOwned) m_computer).getComputer() : null;
}
} }
} }

View File

@ -40,6 +40,7 @@ import net.minecraftforge.items.IItemHandlerModifiable;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
public class TurtleBrain implements ITurtleAccess public class TurtleBrain implements ITurtleAccess
{ {
@ -960,7 +961,7 @@ public class TurtleBrain implements ITurtleAccess
// If we've got a computer, ensure that we're allowed to perform work. // If we've got a computer, ensure that we're allowed to perform work.
ServerComputer computer = m_owner.getServerComputer(); ServerComputer computer = m_owner.getServerComputer();
if( computer != null && !computer.getComputer().canExecuteMainThread() ) return; if( computer != null && !computer.getComputer().getMainThreadMonitor().canWork() ) return;
// Pull a new command // Pull a new command
TurtleCommandQueueEntry nextCommand = m_commandQueue.poll(); TurtleCommandQueueEntry nextCommand = m_commandQueue.poll();
@ -973,7 +974,7 @@ public class TurtleBrain implements ITurtleAccess
// Dispatch the callback // Dispatch the callback
if( computer == null ) return; if( computer == null ) return;
computer.getComputer().afterExecuteMainThread( end - start ); computer.getComputer().getMainThreadMonitor().trackWork( end - start, TimeUnit.NANOSECONDS );
int callbackID = nextCommand.callbackID; int callbackID = nextCommand.callbackID;
if( callbackID < 0 ) return; if( callbackID < 0 ) return;