1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-25 22:53:22 +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 @@
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.ILuaTask;
import net.minecraft.world.World;
import javax.annotation.Nonnull;
@ -179,7 +181,7 @@ default Map<String, IPeripheral> getAvailablePeripherals()
}
/**
* 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.
*
* @param name The peripheral's attached name
@ -191,4 +193,23 @@ default IPeripheral getAvailablePeripheral( @Nonnull String name )
{
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 dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.IComputerOwned;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.filesystem.FileSystemException;
@ -21,7 +20,7 @@
import java.util.Objects;
import java.util.Set;
public abstract class ComputerAccess implements IComputerAccess, IComputerOwned
public abstract class ComputerAccess implements IComputerAccess
{
private final IAPIEnvironment m_environment;
private final Set<String> m_mounts = new HashSet<>();
@ -128,9 +127,9 @@ public void queueEvent( @Nonnull final String event, final Object[] arguments )
@Nullable
@Override
public Computer getComputer()
public IWorkMonitor getMainThreadMonitor()
{
return m_environment.getComputer();
return m_environment.getMainThreadMonitor();
}
private String findFreeLocation( String desiredLoc )

View File

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

View File

@ -9,6 +9,7 @@
import com.google.common.base.Objects;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal;
@ -126,30 +127,9 @@ public boolean queueMainThread( Runnable runnable )
return serverExecutor.enqueue( runnable );
}
/**
* 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()
public IWorkMonitor getMainThreadMonitor()
{
return MainThread.canExecute() && serverExecutor.canExecuteExternal();
}
/**
* 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 );
return serverExecutor;
}
public int getID()

View File

@ -7,6 +7,7 @@
package dan200.computercraft.core.computer;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.filesystem.FileSystem;
import dan200.computercraft.core.terminal.Terminal;
@ -58,13 +59,6 @@ public final class Environment implements IAPIEnvironment
this.computer = computer;
}
@Nonnull
@Override
public Computer getComputer()
{
return computer;
}
@Override
public int getComputerID()
{
@ -78,6 +72,13 @@ public IComputerEnvironment getComputerEnvironment()
return computer.getComputerEnvironment();
}
@Nonnull
@Override
public IWorkMonitor getMainThreadMonitor()
{
return computer.getMainThreadMonitor();
}
@Nonnull
@Override
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;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.shared.turtle.core.TurtleBrain;
import net.minecraft.tileentity.TileEntity;
import javax.annotation.Nonnull;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import static dan200.computercraft.core.computer.MainThread.MAX_COMPUTER_TIME;
@ -48,7 +51,7 @@
* @see Computer#queueMainThread(Runnable)
* @see Computer#afterExecuteMainThread(long)
*/
final class MainThreadExecutor
final class MainThreadExecutor implements IWorkMonitor
{
/**
* The maximum number of {@link MainThread} tasks allowed on the queue.
@ -100,7 +103,7 @@ final class MainThreadExecutor
/**
* The current state of this executor.
*
* @see #canExecuteExternal()
* @see #canWork()
*/
private State state = State.COOL;
@ -155,26 +158,23 @@ boolean afterExecute( long time )
}
}
/**
* 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}).
*
* @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 )

View File

@ -16,8 +16,7 @@
import dan200.computercraft.api.network.wired.IWiredSender;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.IComputerOwned;
import dan200.computercraft.api.peripheral.IWorkMonitor;
import dan200.computercraft.shared.peripheral.modem.ModemPeripheral;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import net.minecraft.world.World;
@ -267,7 +266,7 @@ private RemotePeripheralWrapper getWrapper( IComputerAccess computer, String rem
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 IPeripheral m_peripheral;
@ -376,6 +375,13 @@ public void queueEvent( @Nonnull String event, Object[] arguments )
m_computer.queueEvent( event, arguments );
}
@Nullable
@Override
public IWorkMonitor getMainThreadMonitor()
{
return m_computer.getMainThreadMonitor();
}
@Nonnull
@Override
public String getAttachmentName()
@ -402,12 +408,5 @@ public IPeripheral getAvailablePeripheral( @Nonnull String 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 javax.annotation.Nonnull;
import java.util.*;
import java.util.concurrent.TimeUnit;
public class TurtleBrain implements ITurtleAccess
{
@ -960,7 +961,7 @@ private void updateCommands()
// If we've got a computer, ensure that we're allowed to perform work.
ServerComputer computer = m_owner.getServerComputer();
if( computer != null && !computer.getComputer().canExecuteMainThread() ) return;
if( computer != null && !computer.getComputer().getMainThreadMonitor().canWork() ) return;
// Pull a new command
TurtleCommandQueueEntry nextCommand = m_commandQueue.poll();
@ -973,7 +974,7 @@ private void updateCommands()
// Dispatch the callback
if( computer == null ) return;
computer.getComputer().afterExecuteMainThread( end - start );
computer.getComputer().getMainThreadMonitor().trackWork( end - start, TimeUnit.NANOSECONDS );
int callbackID = nextCommand.callbackID;
if( callbackID < 0 ) return;