Correctly invalidate the ROM mount cache

Before it would remain the same across world reloads, and thus would be
out-of-date after leaving the first world. This architecture technically
allows for running multiple servers at once, though that's not going to
matter that soon.
This commit is contained in:
SquidDev 2019-11-26 18:16:49 +00:00
parent 642351af1a
commit 3b7300543a
5 changed files with 58 additions and 25 deletions

View File

@ -6,11 +6,9 @@
package dan200.computercraft;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.apis.http.websocket.Websocket;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.shared.Config;
import dan200.computercraft.shared.computer.blocks.BlockComputer;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
@ -194,13 +192,6 @@ public static String getVersion()
return "${version}";
}
static IMount createResourceMount( String domain, String subPath )
{
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();
ResourceMount mount = new ResourceMount( domain, subPath, manager );
return mount.exists( "" ) ? mount : null;
}
public static InputStream getResourceFile( String domain, String subPath )
{
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();

View File

@ -6,6 +6,7 @@
package dan200.computercraft;
import com.google.common.collect.MapMaker;
import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.api.filesystem.IWritableMount;
@ -20,20 +21,26 @@
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.ApiFactories;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.shared.*;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner;
import dan200.computercraft.shared.wired.CapabilityWiredElement;
import dan200.computercraft.shared.wired.WiredNode;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fml.server.ServerLifecycleHooks;
import javax.annotation.Nonnull;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Map;
public final class ComputerCraftAPIImpl implements IComputerCraftAPI
{
@ -43,6 +50,9 @@ private ComputerCraftAPIImpl()
{
}
private WeakReference<IReloadableResourceManager> currentResources;
private final Map<ResourceLocation, ResourceMount> mountCache = new MapMaker().weakValues().concurrencyLevel( 1 ).makeMap();
@Nonnull
@Override
public String getInstalledVersion()
@ -72,7 +82,9 @@ public IWritableMount createSaveDirMount( @Nonnull World world, @Nonnull String
@Override
public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath )
{
return ComputerCraft.createResourceMount( domain, subPath );
IReloadableResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager();
ResourceMount mount = ResourceMount.get( domain, subPath, manager );
return mount.exists( "" ) ? mount : null;
}
@Override

View File

@ -56,9 +56,6 @@ final class ComputerExecutor
{
private static final int QUEUE_LIMIT = 256;
private static IMount romMount;
private static final Object romMountLock = new Object();
private final Computer computer;
private final List<ILuaAPI> apis = new ArrayList<>();
final TimeoutState timeout = new TimeoutState();
@ -329,16 +326,10 @@ void tick()
private IMount getRomMount()
{
if( romMount != null ) return romMount;
synchronized( romMountLock )
{
if( romMount != null ) return romMount;
return romMount = computer.getComputerEnvironment().createResourceMount( "computercraft", "lua/rom" );
}
return computer.getComputerEnvironment().createResourceMount( "computercraft", "lua/rom" );
}
IWritableMount getRootMount()
private IWritableMount getRootMount()
{
if( rootMount == null )
{

View File

@ -8,13 +8,16 @@
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.MapMaker;
import com.google.common.io.ByteStreams;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.filesystem.IMount;
import dan200.computercraft.core.apis.handles.ArrayByteChannel;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResource;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.ResourceLocationException;
import net.minecraftforge.resource.IResourceType;
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
@ -29,7 +32,7 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
public class ResourceMount implements IMount
public final class ResourceMount implements IMount
{
/**
* Only cache files smaller than 1MiB.
@ -55,6 +58,13 @@ public class ResourceMount implements IMount
.<FileEntry, byte[]>weigher( ( k, v ) -> v.length )
.build();
private static final MapMaker CACHE_TEMPLATE = new MapMaker().weakValues().concurrencyLevel( 1 );
/**
* Maintain a cache of currently loaded resource mounts. This cache is invalidated when currentManager changes.
*/
private static final Map<IReloadableResourceManager, Map<ResourceLocation, ResourceMount>> MOUNT_CACHE = new WeakHashMap<>( 2 );
private final String namespace;
private final String subPath;
private final IReloadableResourceManager manager;
@ -62,7 +72,26 @@ public class ResourceMount implements IMount
@Nullable
private FileEntry root;
public ResourceMount( String namespace, String subPath, IReloadableResourceManager manager )
public static ResourceMount get( String namespace, String subPath, IReloadableResourceManager manager )
{
Map<ResourceLocation, ResourceMount> cache;
synchronized( MOUNT_CACHE )
{
cache = MOUNT_CACHE.get( manager );
if( cache == null ) MOUNT_CACHE.put( manager, cache = CACHE_TEMPLATE.makeMap() );
}
ResourceLocation path = new ResourceLocation( namespace, subPath );
synchronized( cache )
{
ResourceMount mount = cache.get( path );
if( mount == null ) cache.put( path, mount = new ResourceMount( namespace, subPath, manager ) );
return mount;
}
}
private ResourceMount( String namespace, String subPath, IReloadableResourceManager manager )
{
this.namespace = namespace;
this.subPath = subPath;
@ -119,7 +148,17 @@ private void create( FileEntry lastEntry, String path )
FileEntry nextEntry = lastEntry.children.get( part );
if( nextEntry == null )
{
lastEntry.children.put( part, nextEntry = new FileEntry( new ResourceLocation( namespace, subPath + "/" + path ) ) );
ResourceLocation childPath;
try
{
childPath = new ResourceLocation( namespace, subPath + "/" + path );
}
catch( ResourceLocationException e )
{
ComputerCraft.log.warn( "Cannot create resource location for {} ({})", part, e.getMessage() );
return;
}
lastEntry.children.put( part, nextEntry = new FileEntry( childPath ) );
}
lastEntry = nextEntry;

View File

@ -31,7 +31,7 @@ public void before()
SimpleReloadableResourceManager manager = new SimpleReloadableResourceManager( ResourcePackType.SERVER_DATA, null );
manager.addResourcePack( new FolderPack( new File( "src/main/resources" ) ) );
mount = new ResourceMount( "computercraft", "lua/rom", manager );
mount = ResourceMount.get( "computercraft", "lua/rom", manager );
}
@Test