1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-11 18:00:29 +00:00

Add support for running multiple computers at the same time

- ComputerThread constructs multiple threads instead of just one,
   depending on a config options.
 - The synchronized blocks of PeripheralAPI.PeripheralWrapper have been
   shifted a little to ensure no deadlocks occur.
This commit is contained in:
SquidDev 2017-11-15 13:30:40 +00:00
parent ed8e9d7817
commit dd5698241b
3 changed files with 86 additions and 66 deletions

View File

@ -127,6 +127,7 @@ public class ComputerCraft
public static boolean disable_lua51_features = false; public static boolean disable_lua51_features = false;
public static String default_computer_settings = ""; public static String default_computer_settings = "";
public static boolean debug_enable = false; public static boolean debug_enable = false;
public static int computer_threads = 1;
public static boolean logPeripheralErrors = false; public static boolean logPeripheralErrors = false;
public static boolean enableCommandBlock = false; public static boolean enableCommandBlock = false;
@ -208,6 +209,7 @@ public class ComputerCraft
public static Property disable_lua51_features; public static Property disable_lua51_features;
public static Property default_computer_settings; public static Property default_computer_settings;
public static Property debug_enable; public static Property debug_enable;
public static Property computer_threads;
public static Property logPeripheralErrors; public static Property logPeripheralErrors;
public static Property enableCommandBlock; public static Property enableCommandBlock;
@ -308,6 +310,13 @@ public class ComputerCraft
Config.debug_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "debug_enable", debug_enable ); Config.debug_enable = Config.config.get( Configuration.CATEGORY_GENERAL, "debug_enable", debug_enable );
Config.debug_enable.setComment( "Enable Lua's debug library. Whilst this should be safe for general use, it may allow players to interact with other computers. Enable at your own risk." ); Config.debug_enable.setComment( "Enable Lua's debug library. Whilst this should be safe for general use, it may allow players to interact with other computers. Enable at your own risk." );
Config.computer_threads = Config.config.get( Configuration.CATEGORY_GENERAL, "computer_threads", computer_threads );
Config.computer_threads
.setMinValue( 1 )
.setRequiresWorldRestart( true )
.setComment( "Set the number of threads computers can run on. A higher number means more computers can run at once, but may induce lag.\n" +
"Please note that some mods may not work with a thread count higher than 1. Use with caution." );
Config.logPeripheralErrors = Config.config.get( Configuration.CATEGORY_GENERAL, "logPeripheralErrors", logPeripheralErrors ); Config.logPeripheralErrors = Config.config.get( Configuration.CATEGORY_GENERAL, "logPeripheralErrors", logPeripheralErrors );
Config.logPeripheralErrors.setComment( "Log exceptions thrown by peripherals and other Lua objects.\n" + Config.logPeripheralErrors.setComment( "Log exceptions thrown by peripherals and other Lua objects.\n" +
"This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." ); "This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." );
@ -378,6 +387,7 @@ public class ComputerCraft
disable_lua51_features = Config.disable_lua51_features.getBoolean(); disable_lua51_features = Config.disable_lua51_features.getBoolean();
default_computer_settings = Config.default_computer_settings.getString(); default_computer_settings = Config.default_computer_settings.getString();
debug_enable = Config.debug_enable.getBoolean(); debug_enable = Config.debug_enable.getBoolean();
computer_threads = Config.computer_threads.getInt();
logPeripheralErrors = Config.logPeripheralErrors.getBoolean(); logPeripheralErrors = Config.logPeripheralErrors.getBoolean();
enableCommandBlock = Config.enableCommandBlock.getBoolean(); enableCommandBlock = Config.enableCommandBlock.getBoolean();

View File

@ -34,74 +34,77 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
private String[] m_methods; private String[] m_methods;
private Map<String, Integer> m_methodMap; private Map<String, Integer> m_methodMap;
private boolean m_attached; private boolean m_attached;
private Set<String> m_mounts; private Set<String> m_mounts;
public PeripheralWrapper( IPeripheral peripheral, String side ) public PeripheralWrapper( IPeripheral peripheral, String side )
{ {
m_side = side; m_side = side;
m_peripheral = peripheral; m_peripheral = peripheral;
m_attached = false; m_attached = false;
m_type = peripheral.getType(); m_type = peripheral.getType();
m_methods = peripheral.getMethodNames(); m_methods = peripheral.getMethodNames();
assert( m_type != null ); assert( m_type != null );
assert( m_methods != null ); assert( m_methods != null );
m_methodMap = new HashMap<>(); m_methodMap = new HashMap<>();
for(int i=0; i<m_methods.length; ++i ) { for(int i=0; i<m_methods.length; ++i ) {
if( m_methods[i] != null ) { if( m_methods[i] != null ) {
m_methodMap.put( m_methods[i], i ); m_methodMap.put( m_methods[i], i );
} }
} }
m_mounts = new HashSet<>(); m_mounts = new HashSet<>();
} }
public IPeripheral getPeripheral() public IPeripheral getPeripheral()
{ {
return m_peripheral; return m_peripheral;
} }
public String getType() public String getType()
{ {
return m_type; return m_type;
} }
public String[] getMethods() public String[] getMethods()
{ {
return m_methods; return m_methods;
} }
public synchronized boolean isAttached() public synchronized boolean isAttached()
{ {
return m_attached; return m_attached;
} }
public synchronized void attach() public synchronized void attach()
{ {
m_attached = true; m_attached = true;
m_peripheral.attach( this ); m_peripheral.attach( this );
} }
public synchronized void detach() public void detach()
{ {
// Call detach // Call detach
m_peripheral.detach( this ); m_peripheral.detach( this );
m_attached = false;
synchronized( this )
// Unmount everything the detach function forgot to do
for( String m_mount : m_mounts )
{ {
m_fileSystem.unmount( m_mount ); m_attached = false;
// Unmount everything the detach function forgot to do
for( String m_mount : m_mounts )
{
m_fileSystem.unmount( m_mount );
}
m_mounts.clear();
} }
m_mounts.clear();
} }
public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException public Object[] call( ILuaContext context, String methodName, Object[] arguments ) throws LuaException, InterruptedException
{ {
int method = -1; int method = -1;
synchronized( this ) synchronized( this )
{ {
if( m_methodMap.containsKey( methodName ) ) if( m_methodMap.containsKey( methodName ) )
{ {
@ -133,7 +136,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{ {
throw new RuntimeException( "You are not attached to this Computer" ); throw new RuntimeException( "You are not attached to this Computer" );
} }
// Mount the location // Mount the location
String location; String location;
synchronized( m_fileSystem ) synchronized( m_fileSystem )
@ -151,7 +154,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
if( location != null ) if( location != null )
{ {
m_mounts.add( location ); m_mounts.add( location );
} }
return location; return location;
} }
@ -168,7 +171,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{ {
throw new RuntimeException( "You are not attached to this Computer" ); throw new RuntimeException( "You are not attached to this Computer" );
} }
// Mount the location // Mount the location
String location; String location;
synchronized( m_fileSystem ) synchronized( m_fileSystem )
@ -186,49 +189,49 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
if( location != null ) if( location != null )
{ {
m_mounts.add( location ); m_mounts.add( location );
} }
return location; return location;
} }
@Override @Override
public synchronized void unmount( String location ) public synchronized void unmount( String location )
{ {
if( !m_attached ) { if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" ); throw new RuntimeException( "You are not attached to this Computer" );
} }
if( location != null ) if( location != null )
{ {
if( !m_mounts.contains( location ) ) { if( !m_mounts.contains( location ) ) {
throw new RuntimeException( "You didn't mount this location" ); throw new RuntimeException( "You didn't mount this location" );
} }
m_fileSystem.unmount( location ); m_fileSystem.unmount( location );
m_mounts.remove( location ); m_mounts.remove( location );
} }
} }
@Override @Override
public synchronized int getID() public int getID()
{ {
if( !m_attached ) { if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" ); throw new RuntimeException( "You are not attached to this Computer" );
} }
return m_environment.getComputerID(); return m_environment.getComputerID();
} }
@Override @Override
public synchronized void queueEvent( @Nonnull final String event, final Object[] arguments ) public void queueEvent( @Nonnull final String event, final Object[] arguments )
{ {
if( !m_attached ) { if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" ); throw new RuntimeException( "You are not attached to this Computer" );
} }
m_environment.queueEvent( event, arguments ); m_environment.queueEvent( event, arguments );
} }
@Nonnull @Nonnull
@Override @Override
public synchronized String getAttachmentName() public String getAttachmentName()
{ {
if( !m_attached ) { if( !m_attached ) {
throw new RuntimeException( "You are not attached to this Computer" ); throw new RuntimeException( "You are not attached to this Computer" );
@ -236,7 +239,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
return m_side; return m_side;
} }
} }
private final IAPIEnvironment m_environment; private final IAPIEnvironment m_environment;
private FileSystem m_fileSystem; private FileSystem m_fileSystem;
private final PeripheralWrapper[] m_peripherals; private final PeripheralWrapper[] m_peripherals;
@ -246,16 +249,16 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{ {
m_environment = _environment; m_environment = _environment;
m_environment.setPeripheralChangeListener( this ); m_environment.setPeripheralChangeListener( this );
m_peripherals = new PeripheralWrapper[6]; m_peripherals = new PeripheralWrapper[6];
for(int i=0; i<6; ++i) for(int i=0; i<6; ++i)
{ {
m_peripherals[i] = null; m_peripherals[i] = null;
} }
m_running = false; m_running = false;
} }
// IPeripheralChangeListener // IPeripheralChangeListener
@Override @Override
@ -282,11 +285,11 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
} }
}, null); }, null);
// Queue a detachment event // Queue a detachment event
m_environment.queueEvent( "peripheral_detach", new Object[] { Computer.s_sideNames[side] } ); m_environment.queueEvent( "peripheral_detach", new Object[] { Computer.s_sideNames[side] } );
} }
// Assign the new peripheral // Assign the new peripheral
if( newPeripheral != null ) if( newPeripheral != null )
{ {
@ -296,7 +299,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
{ {
m_peripherals[side] = null; m_peripherals[side] = null;
} }
if( m_peripherals[side] != null ) if( m_peripherals[side] != null )
{ {
// Queue an attachment // Queue an attachment
@ -318,7 +321,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
} }
}, null ); }, null );
// Queue an attachment event // Queue an attachment event
m_environment.queueEvent( "peripheral", new Object[] { Computer.s_sideNames[side] } ); m_environment.queueEvent( "peripheral", new Object[] { Computer.s_sideNames[side] } );
} }
@ -326,7 +329,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
// ILuaAPI implementation // ILuaAPI implementation
@Override @Override
public String[] getNames() public String[] getNames()
{ {
@ -352,12 +355,12 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
} }
} }
@Override @Override
public void advance( double _dt ) public void advance( double _dt )
{ {
} }
@Override @Override
public void shutdown( ) public void shutdown( )
{ {
@ -464,8 +467,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
// call // call
int side = parseSide( args ); int side = parseSide( args );
String methodName = getString( args, 1 ); String methodName = getString( args, 1 );
Object[] methodArgs = trimArray( args, 2 ); Object[] methodArgs = Arrays.copyOfRange( args, 2, args.length );
if( side >= 0 ) if( side >= 0 )
{ {
PeripheralWrapper p; PeripheralWrapper p;
@ -486,13 +489,8 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
} }
} }
// Privates
private Object[] trimArray( Object[] array, int skip ) // Privates
{
return Arrays.copyOfRange( array, skip, array.length );
}
private int parseSide( Object[] args ) throws LuaException private int parseSide( Object[] args ) throws LuaException
{ {
@ -506,7 +504,7 @@ public class PeripheralAPI implements ILuaAPI, IAPIEnvironment.IPeripheralChange
} }
return -1; return -1;
} }
private String findFreeLocation( String desiredLoc ) private String findFreeLocation( String desiredLoc )
{ {
try try

View File

@ -53,9 +53,10 @@ public class ComputerThread
/** /**
* The thread tasks execute on * The thread tasks execute on
*/ */
private static Thread s_thread = null; private static Thread[] s_threads = null;
private static final AtomicInteger s_counter = new AtomicInteger( 1 ); private static final AtomicInteger s_ManagerCounter = new AtomicInteger( 1 );
private static final AtomicInteger s_DelegateCounter = new AtomicInteger( 1 );
/** /**
* Start the computer thread * Start the computer thread
@ -65,14 +66,22 @@ public class ComputerThread
synchronized( s_stateLock ) synchronized( s_stateLock )
{ {
s_stopped = false; s_stopped = false;
if( s_thread == null || !s_thread.isAlive() ) if( s_threads == null || s_threads.length != ComputerCraft.computer_threads )
{ {
SecurityManager manager = System.getSecurityManager(); s_threads = new Thread[ ComputerCraft.computer_threads ];
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup(); }
Thread thread = s_thread = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager" ); SecurityManager manager = System.getSecurityManager();
thread.setDaemon( true ); final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
thread.start(); for( int i = 0; i < s_threads.length; i++ )
{
Thread thread = s_threads[ i ];
if( thread == null || !thread.isAlive() )
{
thread = s_threads[ i ] = new Thread( group, new TaskExecutor(), "ComputerCraft-Computer-Manager-" + s_ManagerCounter.getAndIncrement() );
thread.setDaemon( true );
thread.start();
}
} }
} }
} }
@ -84,12 +93,15 @@ public class ComputerThread
{ {
synchronized( s_stateLock ) synchronized( s_stateLock )
{ {
if( s_thread != null ) if( s_threads != null )
{ {
s_stopped = true; s_stopped = true;
if( s_thread.isAlive() ) for( Thread thread : s_threads )
{ {
s_thread.interrupt(); if( thread != null && thread.isAlive() )
{
thread.interrupt();
}
} }
} }
} }
@ -178,7 +190,7 @@ public class ComputerThread
SecurityManager manager = System.getSecurityManager(); SecurityManager manager = System.getSecurityManager();
final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup(); final ThreadGroup group = manager == null ? Thread.currentThread().getThreadGroup() : manager.getThreadGroup();
Thread thread = this.thread = new Thread( group, runner, "ComputerCraft-Computer-Runner" + s_counter.getAndIncrement() ); Thread thread = this.thread = new Thread( group, runner, "ComputerCraft-Computer-Runner" + s_DelegateCounter.getAndIncrement() );
thread.setDaemon( true ); thread.setDaemon( true );
thread.start(); thread.start();
} }