Archiviert
13
0

Ensure that the structure compiler is thread safe.

Dieser Commit ist enthalten in:
Kristian S. Stangeland 2012-10-01 00:57:14 +02:00
Ursprung e6de9ae705
Commit 46d9a6e975
3 geänderte Dateien mit 92 neuen und 10 gelöschten Zeilen

Datei anzeigen

@ -17,12 +17,17 @@
package com.comphenix.protocol.injector; package com.comphenix.protocol.injector;
import java.util.HashMap; import java.util.HashSet;
import java.util.Map; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import net.minecraft.server.Packet; import net.minecraft.server.Packet;
import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
import com.comphenix.protocol.reflect.compiler.CompileListener;
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
/** /**
* Caches structure modifiers. * Caches structure modifiers.
@ -30,7 +35,10 @@ import com.comphenix.protocol.reflect.StructureModifier;
*/ */
public class StructureCache { public class StructureCache {
// Structure modifiers // Structure modifiers
private static Map<Integer, StructureModifier<Object>> structureModifiers = new HashMap<Integer, StructureModifier<Object>>(); private static ConcurrentMap<Integer, StructureModifier<Object>> structureModifiers =
new ConcurrentHashMap<Integer, StructureModifier<Object>>();
private static Set<Integer> compiling = new HashSet<Integer>();
/** /**
* Creates an empty Minecraft packet of the given ID. * Creates an empty Minecraft packet of the given ID.
@ -53,15 +61,51 @@ public class StructureCache {
* @return A structure modifier. * @return A structure modifier.
*/ */
public static StructureModifier<Object> getStructure(int id) { public static StructureModifier<Object> getStructure(int id) {
// Compile structures by default
return getStructure(id, true);
}
/**
* Retrieve a cached structure modifier for the given packet id.
* @param id - packet ID.
* @param compile - whether or not to asynchronously compile the structure modifier.
* @return A structure modifier.
*/
public static StructureModifier<Object> getStructure(int id, boolean compile) {
StructureModifier<Object> result = structureModifiers.get(id); StructureModifier<Object> result = structureModifiers.get(id);
// Use the vanilla class definition // We don't want to create this for every lookup
if (result == null) { if (result == null) {
result = new StructureModifier<Object>( // Use the vanilla class definition
final StructureModifier<Object> value = new StructureModifier<Object>(
MinecraftRegistry.getPacketClassFromID(id, true), Packet.class, true); MinecraftRegistry.getPacketClassFromID(id, true), Packet.class, true);
structureModifiers.put(id, result); result = structureModifiers.putIfAbsent(id, value);
// We may end up creating multiple modifiers, but we'll agree on which to use
if (result == null) {
result = value;
}
}
// Automatically compile the structure modifier
if (compile && !(result instanceof CompiledStructureModifier)) {
// Compilation is many orders of magnitude slower than synchronization
synchronized (compiling) {
final int idCopy = id;
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
if (!compiling.contains(id) && compiler != null) {
compiler.scheduleCompilation(result, new CompileListener<Object>() {
@Override
public void onCompiled(StructureModifier<Object> compiledModifier) {
structureModifiers.put(idCopy, compiledModifier);
}
});
compiling.add(id);
}
}
} }
return result; return result;

Datei anzeigen

@ -80,6 +80,27 @@ public class BackgroundCompiler {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public void scheduleCompilation(final Map<Class, StructureModifier> cache, final Class key) { public void scheduleCompilation(final Map<Class, StructureModifier> cache, final Class key) {
@SuppressWarnings("unchecked")
final StructureModifier<Object> uncompiled = cache.get(key);
if (uncompiled != null) {
scheduleCompilation(uncompiled, new CompileListener<Object>() {
@Override
public void onCompiled(StructureModifier<Object> compiledModifier) {
// Update cache
cache.put(key, compiledModifier);
}
});
}
}
/**
* Ensure that the given structure modifier is eventually compiled.
* @param uncompiled - structure modifier to compile.
* @param listener - listener responsible for responding to the compilation.
*/
public <TKey> void scheduleCompilation(final StructureModifier<TKey> uncompiled, final CompileListener<TKey> listener) {
// Only schedule if we're enabled // Only schedule if we're enabled
if (enabled && !shuttingDown) { if (enabled && !shuttingDown) {
@ -92,11 +113,11 @@ public class BackgroundCompiler {
@Override @Override
public Object call() throws Exception { public Object call() throws Exception {
StructureModifier<?> modifier = cache.get(key); StructureModifier<TKey> modifier = uncompiled;
// Update the cache! // Do our compilation
modifier = compiler.compile(modifier); modifier = compiler.compile(modifier);
cache.put(key, modifier); listener.onCompiled(modifier);
// We'll also return the new structure modifier // We'll also return the new structure modifier
return modifier; return modifier;

Datei anzeigen

@ -0,0 +1,17 @@
package com.comphenix.protocol.reflect.compiler;
import com.comphenix.protocol.reflect.StructureModifier;
/**
* Used to save the result of an compilation.
*
* @author Kristian
* @param <TKey> - type of the structure modifier field.
*/
public interface CompileListener<TKey> {
/**
* Invoked when a structure modifier has been successfully compiled.
* @param compiledModifier - the compiled structure modifier.
*/
public void onCompiled(StructureModifier<TKey> compiledModifier);
}