1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-07-08 04:52:52 +00:00

Add a basic implementation of the single-thread Lua

This commit is contained in:
SquidDev 2018-09-01 18:29:18 +01:00
parent 0f70d68d0d
commit 7cc77cb1ed
5 changed files with 179 additions and 48 deletions

View File

@ -42,6 +42,7 @@ minecraft {
} }
repositories { repositories {
mavenLocal()
maven { maven {
name = "JEI" name = "JEI"
url = "http://dvs1.progwml6.com/files/maven" url = "http://dvs1.progwml6.com/files/maven"
@ -66,7 +67,7 @@ dependencies {
runtime "mezz.jei:jei_1.12.2:4.8.5.159" runtime "mezz.jei:jei_1.12.2:4.8.5.159"
shade 'org.squiddev:Cobalt:0.3.2' shade 'org.squiddev:Cobalt:0.3.2-nothread'
testCompile 'junit:junit:4.11' testCompile 'junit:junit:4.11'

View File

@ -13,24 +13,28 @@ import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import dan200.computercraft.core.computer.ITask; import dan200.computercraft.core.computer.ITask;
import dan200.computercraft.core.computer.MainThread; import dan200.computercraft.core.computer.MainThread;
import org.squiddev.cobalt.LuaError;
import org.squiddev.cobalt.LuaState; import org.squiddev.cobalt.LuaState;
import org.squiddev.cobalt.LuaThread; import org.squiddev.cobalt.LuaThread;
import org.squiddev.cobalt.OrphanedThread;
import org.squiddev.cobalt.Varargs;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.lang.ref.WeakReference;
class CobaltLuaContext implements ILuaContext class CobaltLuaContext implements ILuaContext
{ {
private CobaltLuaMachine machine;
private final Computer computer; private final Computer computer;
private final LuaState state;
public CobaltLuaContext( CobaltLuaMachine machine, Computer computer, LuaState state ) boolean done = false;
Object[] values;
LuaError exception;
final Semaphore yield = new Semaphore();
final Semaphore resume = new Semaphore();
private WeakReference<LuaThread> thread;
public CobaltLuaContext( Computer computer, LuaState state )
{ {
this.machine = machine;
this.computer = computer; this.computer = computer;
this.state = state; this.thread = state.getCurrentThread().getReference();
} }
@Nonnull @Nonnull
@ -56,19 +60,19 @@ class CobaltLuaContext implements ILuaContext
@Override @Override
public Object[] yield( Object[] yieldArgs ) throws InterruptedException public Object[] yield( Object[] yieldArgs ) throws InterruptedException
{ {
try if( done ) throw new IllegalStateException( "Cannot yield when complete" );
values = yieldArgs;
yield.signal();
// Every 30 seconds check to see if the coroutine has been GCed
// if so then abort this task.
while( !resume.await( 30000 ) )
{ {
Varargs results = LuaThread.yield( state, machine.toValues( yieldArgs ) ); if( thread.get() == null ) throw new InterruptedException( "Orphaned async task" );
return CobaltLuaMachine.toObjects( results, 1 );
}
catch( OrphanedThread e )
{
throw new InterruptedException();
}
catch( Throwable e )
{
throw new RuntimeException( e );
} }
return values;
} }
@Override @Override
@ -167,6 +171,5 @@ class CobaltLuaContext implements ILuaContext
} }
} }
} }
} }
} }

View File

@ -70,7 +70,7 @@ public class CobaltLuaMachine implements ILuaMachine
int count = ++this.count; int count = ++this.count;
if( count > 100000 ) if( count > 100000 )
{ {
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE ); if( m_hardAbortMessage != null ) throw new LuaError( m_hardAbortMessage );
this.count = 0; this.count = 0;
} }
else else
@ -84,7 +84,7 @@ public class CobaltLuaMachine implements ILuaMachine
@Override @Override
public void poll() throws LuaError public void poll() throws LuaError
{ {
if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE ); if( m_hardAbortMessage != null ) throw new LuaError( m_hardAbortMessage );
handleSoftAbort(); handleSoftAbort();
} }
@ -119,7 +119,7 @@ public class CobaltLuaMachine implements ILuaMachine
if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() ); if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() );
// Register custom load/loadstring provider which automatically adds prefixes. // Register custom load/loadstring provider which automatically adds prefixes.
LibFunction.bind( state, m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } ); LibFunction.bind( m_globals, PrefixLoader.class, new String[]{ "load", "loadstring" } );
// Remove globals we don't want to expose // Remove globals we don't want to expose
m_globals.rawset( "collectgarbage", Constants.NIL ); m_globals.rawset( "collectgarbage", Constants.NIL );
@ -210,18 +210,13 @@ public class CobaltLuaMachine implements ILuaMachine
resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) ); resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) );
} }
Varargs results = m_mainRoutine.resume( resumeArgs ); LuaValue filter = LuaThread.run( m_mainRoutine, resumeArgs ).first();
if( m_hardAbortMessage != null ) if( m_hardAbortMessage != null )
{ {
throw new LuaError( m_hardAbortMessage ); throw new LuaError( m_hardAbortMessage );
} }
else if( !results.first().checkBoolean() )
{
throw new LuaError( results.arg( 2 ).checkString() );
}
else else
{ {
LuaValue filter = results.arg( 2 );
if( filter.isString() ) if( filter.isString() )
{ {
m_eventFilter = filter.toString(); m_eventFilter = filter.toString();
@ -530,13 +525,23 @@ public class CobaltLuaMachine implements ILuaMachine
if( remaining <= 0 ) if( remaining <= 0 )
{ {
LuaValue s; LuaValue s;
state.getCurrentThread().disableYield();
try try
{ {
s = OperationHelper.call( state, func ); s = OperationHelper.call( state, func );
} catch (LuaError e) }
catch( UnwindThrowable e )
{
throw new IOException( "Impossible yield within load" );
}
catch( LuaError e )
{ {
throw new IOException( e ); throw new IOException( e );
} }
finally
{
state.getCurrentThread().enableYield();
}
if( s.isNil() ) if( s.isNil() )
{ {

View File

@ -10,14 +10,32 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.ILuaObject; import dan200.computercraft.api.lua.ILuaObject;
import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.Computer;
import org.squiddev.cobalt.LuaError; import org.squiddev.cobalt.*;
import org.squiddev.cobalt.LuaState; import org.squiddev.cobalt.debug.DebugFrame;
import org.squiddev.cobalt.OrphanedThread; import org.squiddev.cobalt.debug.DebugHandler;
import org.squiddev.cobalt.Varargs; import org.squiddev.cobalt.debug.DebugState;
import org.squiddev.cobalt.function.VarArgFunction; import org.squiddev.cobalt.function.VarArgFunction;
class CobaltWrapperFunction extends VarArgFunction import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
class CobaltWrapperFunction extends VarArgFunction implements Resumable<CobaltLuaContext>
{ {
private static final ThreadGroup group = new ThreadGroup( "ComputerCraft-Lua" );
private static final AtomicInteger threadCounter = new AtomicInteger();
private static final ExecutorService threads = new ThreadPoolExecutor(
4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(),
task -> {
Thread thread = new Thread( group, task, group.getName() + "-" + threadCounter.incrementAndGet() );
if( !thread.isDaemon() ) thread.setDaemon( true );
if( thread.getPriority() != Thread.NORM_PRIORITY ) thread.setPriority( Thread.NORM_PRIORITY );
return thread;
}
);
private final CobaltLuaMachine machine; private final CobaltLuaMachine machine;
private final Computer computer; private final Computer computer;
@ -25,7 +43,7 @@ class CobaltWrapperFunction extends VarArgFunction
private final int method; private final int method;
private final String methodName; private final String methodName;
public CobaltWrapperFunction( CobaltLuaMachine machine, Computer computer, ILuaObject delegate, int method, String methodName ) CobaltWrapperFunction( CobaltLuaMachine machine, Computer computer, ILuaObject delegate, int method, String methodName )
{ {
this.machine = machine; this.machine = machine;
this.computer = computer; this.computer = computer;
@ -35,30 +53,96 @@ class CobaltWrapperFunction extends VarArgFunction
} }
@Override @Override
public Varargs invoke( final LuaState state, Varargs _args ) throws LuaError public Varargs invoke( final LuaState state, Varargs args ) throws LuaError, UnwindThrowable
{ {
Object[] arguments = CobaltLuaMachine.toObjects( _args, 1 ); Object[] arguments = CobaltLuaMachine.toObjects( args, 1 );
Object[] results; CobaltLuaContext context = new CobaltLuaContext( computer, state );
DebugHandler handler = state.debug;
DebugState ds = handler.getDebugState();
DebugFrame di = handler.onCall( ds, this );
di.state = context;
threads.submit( () -> {
try
{
context.values = delegate.callMethod( context, method, arguments );
}
catch( LuaException e )
{
context.exception = new LuaError( e );
}
catch( InterruptedException e )
{
if( ComputerCraft.logPeripheralErrors )
{
ComputerCraft.log.error( "Error calling " + methodName + " on " + delegate, e );
}
context.exception = new LuaError( "Java Exception Thrown: " + e.toString(), 0 );
}
finally
{
context.done = true;
context.yield.signal();
}
} );
try try
{ {
results = delegate.callMethod( new CobaltLuaContext( machine, computer, state ), method, arguments ); return handleResult( state, context );
} }
catch( InterruptedException e ) catch( InterruptedException e )
{ {
throw new OrphanedThread(); throw new LuaError( e );
} }
catch( LuaException e ) }
@Override
public Varargs resume( LuaState state, CobaltLuaContext context, Varargs value ) throws LuaError, UnwindThrowable
{
context.values = CobaltLuaMachine.toObjects( value, 0 );
context.resume.signal();
try
{ {
throw new LuaError( e.getMessage(), e.getLevel() ); return handleResult( state, context );
} }
catch( Throwable t ) catch( InterruptedException e )
{ {
if( ComputerCraft.logPeripheralErrors ) throw new LuaError( e );
}
}
@Override
public Varargs resumeError( LuaState state, CobaltLuaContext context, LuaError error ) throws LuaError
{
DebugHandler handler = state.debug;
handler.onReturn( handler.getDebugState() );
throw error;
}
private Varargs handleResult( LuaState state, CobaltLuaContext context ) throws InterruptedException, LuaError, UnwindThrowable
{
// We may be done if we yield when handling errors
if( !context.done ) context.yield.await();
if( context.done )
{
if( context.exception != null )
{ {
ComputerCraft.log.error( "Error calling " + methodName + " on " + delegate, t ); context.exception.fillTraceback( state );
state.debug.onReturn();
throw context.exception;
}
else
{
state.debug.onReturn();
return machine.toValues( context.values );
} }
throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 );
} }
return machine.toValues( results ); else
{
LuaThread.yield( state, machine.toValues( context.values ) );
throw new AssertionError( "Unreachable code" );
}
} }
} }

View File

@ -0,0 +1,38 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2018. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.lua;
/**
* A trivial way of signalling
*/
public final class Semaphore
{
private volatile boolean state = false;
public synchronized void signal()
{
state = true;
notify();
}
public synchronized void await() throws InterruptedException
{
while( !state ) wait();
state = false;
}
public synchronized boolean await( long timeout ) throws InterruptedException
{
if( !state )
{
wait( timeout );
if( !state ) return false;
}
state = false;
return true;
}
}