mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-29 21:02:59 +00:00 
			
		
		
		
	Add a basic implementation of the single-thread Lua
This commit is contained in:
		| @@ -42,6 +42,7 @@ minecraft { | ||||
| } | ||||
|  | ||||
| repositories { | ||||
|     mavenLocal() | ||||
|     maven { | ||||
|         name = "JEI" | ||||
|         url  = "http://dvs1.progwml6.com/files/maven" | ||||
| @@ -66,7 +67,7 @@ dependencies { | ||||
|  | ||||
|     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' | ||||
|  | ||||
|   | ||||
| @@ -13,24 +13,28 @@ import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.core.computer.Computer; | ||||
| import dan200.computercraft.core.computer.ITask; | ||||
| import dan200.computercraft.core.computer.MainThread; | ||||
| import org.squiddev.cobalt.LuaError; | ||||
| import org.squiddev.cobalt.LuaState; | ||||
| import org.squiddev.cobalt.LuaThread; | ||||
| import org.squiddev.cobalt.OrphanedThread; | ||||
| import org.squiddev.cobalt.Varargs; | ||||
|  | ||||
| import javax.annotation.Nonnull; | ||||
| import java.lang.ref.WeakReference; | ||||
|  | ||||
| class CobaltLuaContext implements ILuaContext | ||||
| { | ||||
|     private CobaltLuaMachine machine; | ||||
|     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.state = state; | ||||
|         this.thread = state.getCurrentThread().getReference(); | ||||
|     } | ||||
|  | ||||
|     @Nonnull | ||||
| @@ -56,19 +60,19 @@ class CobaltLuaContext implements ILuaContext | ||||
|     @Override | ||||
|     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 ) ); | ||||
|             return CobaltLuaMachine.toObjects( results, 1 ); | ||||
|         } | ||||
|         catch( OrphanedThread e ) | ||||
|         { | ||||
|             throw new InterruptedException(); | ||||
|         } | ||||
|         catch( Throwable e ) | ||||
|         { | ||||
|             throw new RuntimeException( e ); | ||||
|             if( thread.get() == null ) throw new InterruptedException( "Orphaned async task" ); | ||||
|         } | ||||
|  | ||||
|         return values; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -167,6 +171,5 @@ class CobaltLuaContext implements ILuaContext | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -70,7 +70,7 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                 int count = ++this.count; | ||||
|                 if( count > 100000 ) | ||||
|                 { | ||||
|                     if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE ); | ||||
|                     if( m_hardAbortMessage != null ) throw new LuaError( m_hardAbortMessage ); | ||||
|                     this.count = 0; | ||||
|                 } | ||||
|                 else | ||||
| @@ -84,7 +84,7 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|             @Override | ||||
|             public void poll() throws LuaError | ||||
|             { | ||||
|                 if( m_hardAbortMessage != null ) LuaThread.yield( state, NONE ); | ||||
|                 if( m_hardAbortMessage != null ) throw new LuaError( m_hardAbortMessage ); | ||||
|                 handleSoftAbort(); | ||||
|             } | ||||
|  | ||||
| @@ -119,7 +119,7 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|         if( ComputerCraft.debug_enable ) m_globals.load( state, new DebugLib() ); | ||||
|  | ||||
|         // 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 | ||||
|         m_globals.rawset( "collectgarbage", Constants.NIL ); | ||||
| @@ -210,18 +210,13 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|                 resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) ); | ||||
|             } | ||||
|  | ||||
|             Varargs results = m_mainRoutine.resume( resumeArgs ); | ||||
|             LuaValue filter = LuaThread.run( m_mainRoutine, resumeArgs ).first(); | ||||
|             if( m_hardAbortMessage != null ) | ||||
|             { | ||||
|                 throw new LuaError( m_hardAbortMessage ); | ||||
|             } | ||||
|             else if( !results.first().checkBoolean() ) | ||||
|             { | ||||
|                 throw new LuaError( results.arg( 2 ).checkString() ); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 LuaValue filter = results.arg( 2 ); | ||||
|                 if( filter.isString() ) | ||||
|                 { | ||||
|                     m_eventFilter = filter.toString(); | ||||
| @@ -530,13 +525,23 @@ public class CobaltLuaMachine implements ILuaMachine | ||||
|             if( remaining <= 0 ) | ||||
|             { | ||||
|                 LuaValue s; | ||||
|                 state.getCurrentThread().disableYield(); | ||||
|                 try | ||||
|                 { | ||||
|                     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 ); | ||||
|                 } | ||||
|                 finally | ||||
|                 { | ||||
|                     state.getCurrentThread().enableYield(); | ||||
|                 } | ||||
|  | ||||
|                 if( s.isNil() ) | ||||
|                 { | ||||
|   | ||||
| @@ -10,14 +10,32 @@ import dan200.computercraft.ComputerCraft; | ||||
| import dan200.computercraft.api.lua.ILuaObject; | ||||
| import dan200.computercraft.api.lua.LuaException; | ||||
| import dan200.computercraft.core.computer.Computer; | ||||
| import org.squiddev.cobalt.LuaError; | ||||
| import org.squiddev.cobalt.LuaState; | ||||
| import org.squiddev.cobalt.OrphanedThread; | ||||
| import org.squiddev.cobalt.Varargs; | ||||
| import org.squiddev.cobalt.*; | ||||
| import org.squiddev.cobalt.debug.DebugFrame; | ||||
| import org.squiddev.cobalt.debug.DebugHandler; | ||||
| import org.squiddev.cobalt.debug.DebugState; | ||||
| 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 Computer computer; | ||||
|  | ||||
| @@ -25,7 +43,7 @@ class CobaltWrapperFunction extends VarArgFunction | ||||
|     private final int method; | ||||
|     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.computer = computer; | ||||
| @@ -35,30 +53,96 @@ class CobaltWrapperFunction extends VarArgFunction | ||||
|     } | ||||
|  | ||||
|     @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[] results; | ||||
|         Object[] arguments = CobaltLuaMachine.toObjects( args, 1 ); | ||||
|         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 | ||||
|         { | ||||
|             results = delegate.callMethod( new CobaltLuaContext( machine, computer, state ), method, arguments ); | ||||
|             return handleResult( state, context ); | ||||
|         } | ||||
|         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" ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										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; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 SquidDev
					SquidDev