1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-25 22:53:22 +00:00

Several improvements to the computer thread rework

- TimeoutState uses nanoseconds rather than milliseconds. While this is
   slightly less efficient on Windows, it's a) not the bottleneck of Lua
   execution and b) we need a monotonic counter, otherwise we could
   fail to terminate computers if the time changes.
 - Add an exception handler to all threads.
 - Document several classes a little better - I'm not sure how useful
   all of these are, but _hopefully_ it'll make the internals a little
   more accessible.
This commit is contained in:
SquidDev 2019-02-27 20:56:45 +00:00
parent 3e28f79ce9
commit c78adb2cdc
8 changed files with 85 additions and 29 deletions

View File

@ -225,7 +225,6 @@ void queueStop( boolean reboot, boolean close )
*/
void abort()
{
// TODO: We need to test this much more thoroughly.
ILuaMachine machine = this.machine;
if( machine != null ) machine.close();

View File

@ -18,7 +18,7 @@
import javax.annotation.Nullable;
/**
* Implementation of {@link IComputerAccess}/{@link IComputerSystem} for external APIs.
* Implementation of {@link IComputerAccess}/{@link IComputerSystem} for usage by externally registered APIs.
*
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
* @see ILuaAPIFactory

View File

@ -24,8 +24,6 @@
*
* This is split into two components: the {@link TaskRunner}s, which pull an executor from the queue and execute it, and
* a single {@link Monitor} which observes all runners and kills them if they are behaving badly.
*
* TODO: Flesh out the documentation here.
*/
public class ComputerThread
{
@ -95,7 +93,7 @@ static void start()
}
/**
* Attempt to stop the computer thread
* Attempt to stop the computer thread. This interrupts each runner, and clears the task queue.
*/
public static void stop()
{
@ -160,7 +158,7 @@ public void run()
// If we're still within normal execution times (TIMEOUT) or soft abort (ABORT_TIMEOUT),
// then we can let the Lua machine do its work.
long afterStart = executor.timeout.milliSinceStart();
long afterStart = executor.timeout.nanoSinceStart();
long afterHardAbort = afterStart - TIMEOUT - ABORT_TIMEOUT;
if( afterHardAbort < 0 ) continue;
@ -168,7 +166,7 @@ public void run()
executor.timeout.hardAbort();
executor.abort();
if( afterHardAbort >= ABORT_TIMEOUT + ABORT_TIMEOUT )
if( afterHardAbort >= ABORT_TIMEOUT * 2 )
{
// If we've hard aborted and interrupted, and we're still not dead, then mark the runner
// as dead, finish off the task, and spawn a new runner.
@ -206,6 +204,10 @@ public void run()
/**
* Pulls tasks from the {@link #computersActive} queue and runs them.
*
* This is responsible for running the {@link ComputerExecutor#work()}, {@link ComputerExecutor#beforeWork()} and
* {@link ComputerExecutor#afterWork()} functions. Everything else is either handled by the executor, timeout
* state or monitor.
*/
private static final class TaskRunner implements Runnable
{

View File

@ -6,10 +6,28 @@
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,6 +6,8 @@
package dan200.computercraft.core.computer;
import java.util.concurrent.TimeUnit;
/**
* Used to measure how long a computer has executed for, and thus the relevant timeout states.
*
@ -26,28 +28,22 @@
public final class TimeoutState
{
/**
* The total time a task is allowed to run before aborting in milliseconds
* The total time a task is allowed to run before aborting in nanoseconds
*/
static final long TIMEOUT = 7000;
static final long TIMEOUT = TimeUnit.MILLISECONDS.toNanos( 7000 );
/**
* The time the task is allowed to run after each abort in milliseconds
* The time the task is allowed to run after each abort in nanoseconds
*/
static final long ABORT_TIMEOUT = 1500;
static final long ABORT_TIMEOUT = TimeUnit.MILLISECONDS.toNanos( 1500 );
public static final String ABORT_MESSAGE = "Too long without yielding";
private volatile boolean softAbort;
private volatile boolean hardAbort;
private long milliTime;
private long nanoTime;
long milliSinceStart()
{
return System.currentTimeMillis() - milliTime;
}
long nanoSinceStart()
{
return System.nanoTime() - nanoTime;
@ -58,7 +54,7 @@ long nanoSinceStart()
*/
public boolean isSoftAborted()
{
return softAbort || (softAbort = (System.currentTimeMillis() - milliTime) >= TIMEOUT);
return softAbort || (softAbort = (System.nanoTime() - nanoTime) >= TIMEOUT);
}
/**
@ -83,7 +79,6 @@ void hardAbort()
void reset()
{
softAbort = hardAbort = false;
milliTime = System.currentTimeMillis();
nanoTime = System.nanoTime();
}
}

View File

@ -99,11 +99,8 @@ private void handleAbort() throws LuaError
return;
}
if( hasSoftAbort && !timeout.isHardAborted() )
{
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
return;
}
// If we have fired our soft abort, but we haven't been hard aborted then everything is OK.
if( hasSoftAbort ) return;
hasSoftAbort = true;
throw new LuaError( TimeoutState.ABORT_MESSAGE );
@ -157,7 +154,7 @@ private void handleAbort() throws LuaError
}
@Override
public void addAPI( ILuaAPI api )
public void addAPI( @Nonnull ILuaAPI api )
{
// Add the methods of an API to the global table
LuaTable table = wrapLuaObject( api );
@ -169,7 +166,7 @@ public void addAPI( ILuaAPI api )
}
@Override
public void loadBios( InputStream bios )
public void loadBios( @Nonnull InputStream bios )
{
// Begin executing a file (ie, the bios)
if( m_mainRoutine != null ) return;

View File

@ -7,18 +7,61 @@
package dan200.computercraft.core.lua;
import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaObject;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.InputStream;
/**
* Represents a machine which will execute Lua code. Technically this API is flexible enough to support many languages,
* but you'd need a way to provide alternative ROMs, BIOSes, etc...
*
* There should only be one concrete implementation at any one time, which is currently {@link CobaltLuaMachine}. If
* external mod authors are interested in registering their own machines, we can look into how we can provide some
* mechanism for registering these.
*
* This should provide implementations of {@link dan200.computercraft.api.lua.ILuaContext}, and the ability to convert
* {@link ILuaObject}s into something the VM understands, as well as handling method calls.
*/
public interface ILuaMachine
{
void addAPI( ILuaAPI api );
/**
* Inject an API into the global environment of this machine. This should construct an object, as it would for any
* {@link ILuaObject} and set it to all names in {@link ILuaAPI#getNames()}.
*
* Called before {@link #loadBios(InputStream)}.
*
* @param api The API to register.
*/
void addAPI( @Nonnull ILuaAPI api );
void loadBios( InputStream bios );
/**
* Create a function from the provided program, and set it up to run when {@link #handleEvent(String, Object[])} is
* called
*
* @param bios The stream containing the boot program.
*/
void loadBios( @Nonnull InputStream bios );
void handleEvent( String eventName, Object[] arguments );
/**
* Resume the machine, either starting or resuming the coroutine.
*
* @param eventName The name of the event. This is {@code null} when first starting the machine. Note, this may
* do nothing if it does not match the event filter.
* @param arguments The arguments for this event.
*/
void handleEvent( @Nullable String eventName, @Nullable Object[] arguments );
/**
* If this machine has finished executing, either due to an error or it just shutting down.
*
* @return If this machine is finished.
*/
boolean isFinished();
/**
* Close the Lua machine, aborting any running functions and deleting the internal state.
*/
void close();
}

View File

@ -7,6 +7,7 @@
package dan200.computercraft.shared.util;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import dan200.computercraft.ComputerCraft;
import java.util.concurrent.ThreadFactory;
@ -58,6 +59,7 @@ public static ThreadFactoryBuilder builder( String name )
return new ThreadFactoryBuilder()
.setDaemon( true )
.setNameFormat( group.getName().replace( "%", "%%" ) + "-%d" )
.setUncaughtExceptionHandler( ( t, e ) -> ComputerCraft.log.error( "Exception in thread " + t.getName(), e ) )
.setThreadFactory( x -> new Thread( group, x ) );
}