mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-03-29 23:06:59 +00:00
Bump Cobalt version to enable single-threading
The latest version of Cobalt has several major changes, which I'm looking forward to taking advantage of in the coming months: - The Lua interpreter has been split up from the actual LuaClosure instance. It now runs multiple functions within one loop, handling pushing/popping and resuming method calls correctly. This means we have a theoretically infinite call depth, as we're no longer bounded by Java's stack size. In reality, this is limited to 32767 (Short.MAX_VALUE), as that's a mostly equivalent to the limits PUC Lua exposes. - The stack is no longer unwound in the event of errors. This both simplifies error handling (not that CC:T needs to care about that) but also means one can call debug.traceback on a now-dead coroutine (which is more useful for debugging than using xpcall). - Most significantly, coroutines are no longer each run on a dedicated thread. Instead, yielding or resuming throws an exception to unwind the Java stack and switches to a different coroutine. In order to preserve compatability with CC's assumption about LuaJ's threading model (namely that yielding blocks the thread), we also provide a yieldBlock method (which CC:T consumes). This suspends the current thread and switches execution to a new thread (see SquidDev/Cobalt@b5ddf164f1 for more details). While this does mean we need to use more than 1 thread, it's still /substantially/ less than would otherwise be needed. We've been running these changes on SwitchCraft for a few days now and haven't seen any issues. One nice thing to observe is that the number of CC thread has gone down from ~1.9k to ~100 (of those, ~70 are dedicated to running coroutines). Similarly, the server has gone from generating ~15k threads over its lifetime, to ~3k. While this is still a lot, it's a substantial improvement.
This commit is contained in:
parent
6c2db93cbd
commit
12e82afad2
@ -48,7 +48,7 @@ repositories {
|
||||
}
|
||||
maven {
|
||||
name = "squiddev"
|
||||
url = "https://dl.bintray.com/squiddev/maven"
|
||||
url = "https://squiddev.cc/maven"
|
||||
}
|
||||
|
||||
ivy { artifactPattern "https://asie.pl/files/mods/Charset/LibOnly/[module]-[revision](-[classifier]).[ext]" }
|
||||
@ -66,7 +66,7 @@ dependencies {
|
||||
|
||||
runtime "mezz.jei:jei_1.12.2:4.8.5.159"
|
||||
|
||||
shade 'org.squiddev:Cobalt:0.4.0'
|
||||
shade 'org.squiddev:Cobalt:0.5.0-SNAPSHOT'
|
||||
|
||||
testCompile 'junit:junit:4.11'
|
||||
|
||||
|
@ -16,6 +16,7 @@ import java.util.WeakHashMap;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public class ComputerThread
|
||||
{
|
||||
@ -216,22 +217,37 @@ public class ComputerThread
|
||||
// Interrupt the thread
|
||||
if( !done )
|
||||
{
|
||||
StringBuilder builder = new StringBuilder( "Terminating " );
|
||||
if( computer != null )
|
||||
if( ComputerCraft.logPeripheralErrors )
|
||||
{
|
||||
builder.append( "computer " ).append( computer.getID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append( "unknown computer" );
|
||||
}
|
||||
long time = System.nanoTime() - start;
|
||||
StringBuilder builder = new StringBuilder( "Terminating " );
|
||||
if( computer != null )
|
||||
{
|
||||
builder.append( "computer #" ).append( computer.getID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.append( "unknown computer" );
|
||||
}
|
||||
|
||||
builder.append( ". Thread is currently running" );
|
||||
for( StackTraceElement element : thread.getStackTrace() )
|
||||
{
|
||||
builder.append( "\n at " ).append( element );
|
||||
{
|
||||
builder.append( " due to timeout (running for " )
|
||||
.append( time / 1e9 )
|
||||
.append( " seconds). This is NOT a bug, but may mean a computer is misbehaving. " )
|
||||
.append( thread.getName() )
|
||||
.append( " is currently " )
|
||||
.append( thread.getState() );
|
||||
Object blocking = LockSupport.getBlocker( thread );
|
||||
if( blocking != null ) builder.append( "\n on " ).append( blocking );
|
||||
|
||||
for( StackTraceElement element : thread.getStackTrace() )
|
||||
{
|
||||
builder.append( "\n at " ).append( element );
|
||||
}
|
||||
}
|
||||
|
||||
ComputerCraft.log.warn( builder.toString() );
|
||||
}
|
||||
ComputerCraft.log.error( builder.toString() );
|
||||
|
||||
thread.interrupt();
|
||||
thread = null;
|
||||
|
@ -46,7 +46,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
{
|
||||
private static final ThreadPoolExecutor coroutines = new ThreadPoolExecutor(
|
||||
0, Integer.MAX_VALUE,
|
||||
60L, TimeUnit.SECONDS,
|
||||
5L, TimeUnit.MINUTES,
|
||||
new SynchronousQueue<>(),
|
||||
ThreadUtils.factory( "Coroutine" )
|
||||
);
|
||||
@ -74,12 +74,12 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
private boolean hasSoftAbort;
|
||||
|
||||
@Override
|
||||
public void onInstruction( DebugState ds, DebugFrame di, int pc, Varargs extras, int top ) throws LuaError
|
||||
public void onInstruction( DebugState ds, DebugFrame di, int pc ) throws LuaError, UnwindThrowable
|
||||
{
|
||||
int count = ++this.count;
|
||||
if( count > 100000 )
|
||||
{
|
||||
if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE );
|
||||
if( m_hardAbortMessage != null ) throw HardAbortError.INSTANCE;
|
||||
this.count = 0;
|
||||
}
|
||||
else
|
||||
@ -87,13 +87,13 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
handleSoftAbort();
|
||||
}
|
||||
|
||||
super.onInstruction( ds, di, pc, extras, top );
|
||||
super.onInstruction( ds, di, pc );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void poll() throws LuaError
|
||||
{
|
||||
if( m_hardAbortMessage != null ) LuaThread.yield( m_state, NONE );
|
||||
if( m_hardAbortMessage != null ) throw HardAbortError.INSTANCE;
|
||||
handleSoftAbort();
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
throw new LuaError( message );
|
||||
}
|
||||
} )
|
||||
.coroutineFactory( command -> {
|
||||
.yieldThreader( command -> {
|
||||
Tracking.addValue( m_computer, TrackingField.COROUTINES_CREATED, 1 );
|
||||
coroutines.execute( () -> {
|
||||
try
|
||||
@ -145,7 +145,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 );
|
||||
@ -222,26 +222,18 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
resumeArgs = varargsOf( valueOf( eventName ), toValues( arguments ) );
|
||||
}
|
||||
|
||||
Varargs results = m_mainRoutine.resume( resumeArgs );
|
||||
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 );
|
||||
m_eventFilter = filter.isString() ? filter.toString() : null;
|
||||
}
|
||||
Varargs results = LuaThread.run( m_mainRoutine, resumeArgs );
|
||||
if( m_hardAbortMessage != null ) throw new LuaError( m_hardAbortMessage );
|
||||
|
||||
LuaValue filter = results.first();
|
||||
m_eventFilter = filter.isString() ? filter.toString() : null;
|
||||
|
||||
if( m_mainRoutine.getStatus().equals( "dead" ) ) unload();
|
||||
}
|
||||
catch( LuaError e )
|
||||
catch( LuaError | HardAbortError e )
|
||||
{
|
||||
unload();
|
||||
ComputerCraft.log.warn( "Top level coroutine errored", e );
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -339,16 +331,12 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
{
|
||||
try
|
||||
{
|
||||
Varargs results = LuaThread.yield( state, toValues( yieldArgs ) );
|
||||
Varargs results = LuaThread.yieldBlocking( state, toValues( yieldArgs ) );
|
||||
return toObjects( results, 1 );
|
||||
}
|
||||
catch( OrphanedThread e )
|
||||
catch( LuaError e )
|
||||
{
|
||||
throw new InterruptedException();
|
||||
}
|
||||
catch( Throwable e )
|
||||
{
|
||||
throw new RuntimeException( e );
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
@ -699,7 +687,7 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
LuaValue s;
|
||||
try
|
||||
{
|
||||
s = OperationHelper.call( state, func );
|
||||
s = OperationHelper.noYield( state, () -> OperationHelper.call( state, func ) );
|
||||
}
|
||||
catch( LuaError e )
|
||||
{
|
||||
@ -731,4 +719,16 @@ public class CobaltLuaMachine implements ILuaMachine
|
||||
return bytes[offset++];
|
||||
}
|
||||
}
|
||||
|
||||
private static class HardAbortError extends Error
|
||||
{
|
||||
private static final long serialVersionUID = 7954092008586367501L;
|
||||
|
||||
public static final HardAbortError INSTANCE = new HardAbortError();
|
||||
|
||||
private HardAbortError()
|
||||
{
|
||||
super( "Hard Abort", null, true, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user