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:
parent
0f70d68d0d
commit
7cc77cb1ed
@ -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'
|
||||||
|
|
||||||
|
@ -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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() )
|
||||||
{
|
{
|
||||||
|
@ -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
|
try
|
||||||
{
|
{
|
||||||
results = delegate.callMethod( new CobaltLuaContext( machine, computer, state ), method, arguments );
|
context.values = delegate.callMethod( context, method, arguments );
|
||||||
}
|
|
||||||
catch( InterruptedException e )
|
|
||||||
{
|
|
||||||
throw new OrphanedThread();
|
|
||||||
}
|
}
|
||||||
catch( LuaException e )
|
catch( LuaException e )
|
||||||
{
|
{
|
||||||
throw new LuaError( e.getMessage(), e.getLevel() );
|
context.exception = new LuaError( e );
|
||||||
}
|
}
|
||||||
catch( Throwable t )
|
catch( InterruptedException e )
|
||||||
{
|
{
|
||||||
if( ComputerCraft.logPeripheralErrors )
|
if( ComputerCraft.logPeripheralErrors )
|
||||||
{
|
{
|
||||||
ComputerCraft.log.error( "Error calling " + methodName + " on " + delegate, t );
|
ComputerCraft.log.error( "Error calling " + methodName + " on " + delegate, e );
|
||||||
}
|
}
|
||||||
throw new LuaError( "Java Exception Thrown: " + t.toString(), 0 );
|
context.exception = new LuaError( "Java Exception Thrown: " + e.toString(), 0 );
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
context.done = true;
|
||||||
|
context.yield.signal();
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return handleResult( state, context );
|
||||||
|
}
|
||||||
|
catch( InterruptedException e )
|
||||||
|
{
|
||||||
|
throw new LuaError( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Varargs resume( LuaState state, CobaltLuaContext context, Varargs value ) throws LuaError, UnwindThrowable
|
||||||
|
{
|
||||||
|
context.values = CobaltLuaMachine.toObjects( value, 0 );
|
||||||
|
context.resume.signal();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return handleResult( state, context );
|
||||||
|
}
|
||||||
|
catch( InterruptedException e )
|
||||||
|
{
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
context.exception.fillTraceback( state );
|
||||||
|
state.debug.onReturn();
|
||||||
|
throw context.exception;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.debug.onReturn();
|
||||||
|
return machine.toValues( context.values );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LuaThread.yield( state, machine.toValues( context.values ) );
|
||||||
|
throw new AssertionError( "Unreachable code" );
|
||||||
}
|
}
|
||||||
return machine.toValues( results );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
src/main/java/dan200/computercraft/core/lua/Semaphore.java
Normal file
38
src/main/java/dan200/computercraft/core/lua/Semaphore.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user