Better handle locks for EntityTracker and misc

This commit is contained in:
Sotr
2018-08-02 00:43:14 +08:00
parent 9b5b40c002
commit 2ba4bc2755
6 changed files with 50 additions and 36 deletions

View File

@@ -16,9 +16,9 @@ public abstract class MixinWorldManager {
@Overwrite @Overwrite
public void a(Entity entity) { public void a(Entity entity) {
this.world.getTracker().entriesLock.lock(); // Akarin this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
this.world.getTracker().track(entity); this.world.getTracker().track(entity);
this.world.getTracker().entriesLock.unlock(); // Akarin this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
if (entity instanceof EntityPlayer) { if (entity instanceof EntityPlayer) {
this.world.worldProvider.a((EntityPlayer) entity); this.world.worldProvider.a((EntityPlayer) entity);

View File

@@ -745,9 +745,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (entity instanceof EntityPlayer) { if (entity instanceof EntityPlayer) {
WorldServer worldServer = (WorldServer) entity.getWorld(); WorldServer worldServer = (WorldServer) entity.getWorld();
worldServer.tracker.untrackEntity(this); worldServer.tracker.untrackEntity(this);
worldServer.tracker.entriesLock.lock(); // Akarin worldServer.tracker.entriesLock.writeLock().lock(); // Akarin - ProtocolSupport will overwrite track method
worldServer.tracker.track(this); worldServer.tracker.track(this);
worldServer.tracker.entriesLock.unlock(); // Akarin worldServer.tracker.entriesLock.writeLock().unlock(); // Akarin - ProtocolSupport will overwrite track method
} }
// Paper end // Paper end

View File

@@ -2,25 +2,24 @@ package net.minecraft.server;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
/** /**
* Akarin Changes Note * Akarin Changes Note
* 1) Add lock for entries set operations (safety issue) * 1) Made collections and entry access thread-safe (safety issue)
*/ */
public class EntityTracker { public class EntityTracker {
private static final Logger a = LogManager.getLogger(); private static final Logger a = LogManager.getLogger();
private final WorldServer world; private final WorldServer world;
private final Set<EntityTrackerEntry> c = Sets.newHashSet(); private final Set<EntityTrackerEntry> c = Sets.newHashSet();
public final ReentrantLock entriesLock = new ReentrantLock(); // Akarin - add lock public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap(); public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
private int e; private int e;
@@ -38,7 +37,7 @@ public class EntityTracker {
this.addEntity(entity, 512, 2); this.addEntity(entity, 512, 2);
EntityPlayer entityplayer = (EntityPlayer) entity; EntityPlayer entityplayer = (EntityPlayer) entity;
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin // entriesLock.writeLock().lock(); // Akarin - locked in EntityPlayer
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -47,7 +46,7 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer); entitytrackerentry.updatePlayer(entityplayer);
} }
} }
entriesLock.unlock(); // Akarin // entriesLock.writeLock().unlock(); // Akarin - locked in EntityPlayer
} else if (entity instanceof EntityFishingHook) { } else if (entity instanceof EntityFishingHook) {
this.addEntity(entity, 64, 5, true); this.addEntity(entity, 64, 5, true);
} else if (entity instanceof EntityArrow) { } else if (entity instanceof EntityArrow) {
@@ -118,18 +117,18 @@ public class EntityTracker {
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
try { try {
// entriesLock.writeLock().lock(); // Akarin - locked from track method
if (this.trackedEntities.b(entity.getId())) { if (this.trackedEntities.b(entity.getId())) {
throw new IllegalStateException("Entity is already tracked!"); throw new IllegalStateException("Entity is already tracked!");
} }
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag); EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
entriesLock.lock(); // Akarin
this.c.add(entitytrackerentry); this.c.add(entitytrackerentry);
this.trackedEntities.a(entity.getId(), entitytrackerentry); this.trackedEntities.a(entity.getId(), entitytrackerentry);
entitytrackerentry.scanPlayers(this.world.players); entitytrackerentry.scanPlayers(this.world.players);
entriesLock.unlock(); // Akarin // entriesLock.writeLock().unlock(); // Akarin - locked from track method
} catch (Throwable throwable) { } catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track"); CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
@@ -166,34 +165,32 @@ public class EntityTracker {
public void untrackEntity(Entity entity) { public void untrackEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
entriesLock.writeLock().lock(); // Akarin
if (entity instanceof EntityPlayer) { if (entity instanceof EntityPlayer) {
EntityPlayer entityplayer = (EntityPlayer) entity; EntityPlayer entityplayer = (EntityPlayer) entity;
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(entityplayer); entitytrackerentry.a(entityplayer);
} }
entriesLock.unlock(); // Akarin
} }
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId()); EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
if (entitytrackerentry1 != null) { if (entitytrackerentry1 != null) {
entriesLock.lock(); // Akarin
this.c.remove(entitytrackerentry1); this.c.remove(entitytrackerentry1);
entitytrackerentry1.a(); entitytrackerentry1.a();
entriesLock.unlock(); // Akarin
} }
entriesLock.writeLock().unlock(); // Akarin
} }
public void updatePlayers() { public void updatePlayers() {
ArrayList arraylist = Lists.newArrayList(); ArrayList arraylist = Lists.newArrayList();
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
world.timings.tracker1.startTiming(); // Spigot world.timings.tracker1.startTiming(); // Spigot
entriesLock.lock(); // Akarin entriesLock.writeLock().lock(); // Akarin
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -221,14 +218,14 @@ public class EntityTracker {
} }
} }
} }
entriesLock.unlock(); // Akarin entriesLock.writeLock().unlock(); // Akarin
world.timings.tracker2.stopTiming(); // Spigot world.timings.tracker2.stopTiming(); // Spigot
} }
public void a(EntityPlayer entityplayer) { public void a(EntityPlayer entityplayer) {
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin entriesLock.writeLock().lock(); // Akarin
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -239,11 +236,13 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer); entitytrackerentry.updatePlayer(entityplayer);
} }
} }
entriesLock.unlock(); // Akarin entriesLock.writeLock().unlock(); // Akarin
} }
public void a(Entity entity, Packet<?> packet) { public void a(Entity entity, Packet<?> packet) {
entriesLock.readLock().lock(); // Akarin
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId()); EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
entriesLock.readLock().unlock(); // Akarin
if (entitytrackerentry != null) { if (entitytrackerentry != null) {
entitytrackerentry.broadcast(packet); entitytrackerentry.broadcast(packet);
@@ -252,7 +251,9 @@ public class EntityTracker {
} }
public void sendPacketToEntity(Entity entity, Packet<?> packet) { public void sendPacketToEntity(Entity entity, Packet<?> packet) {
entriesLock.readLock().lock(); // Akarin
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId()); EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
entriesLock.readLock().unlock(); // Akarin
if (entitytrackerentry != null) { if (entitytrackerentry != null) {
entitytrackerentry.broadcastIncludingSelf(packet); entitytrackerentry.broadcastIncludingSelf(packet);
@@ -262,21 +263,21 @@ public class EntityTracker {
public void untrackPlayer(EntityPlayer entityplayer) { public void untrackPlayer(EntityPlayer entityplayer) {
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin entriesLock.writeLock().lock();
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.clear(entityplayer); entitytrackerentry.clear(entityplayer);
} }
entriesLock.unlock(); // Akarin entriesLock.writeLock().unlock();
} }
public void a(EntityPlayer entityplayer, Chunk chunk) { public void a(EntityPlayer entityplayer, Chunk chunk) {
ArrayList arraylist = Lists.newArrayList(); ArrayList arraylist = Lists.newArrayList();
ArrayList arraylist1 = Lists.newArrayList(); ArrayList arraylist1 = Lists.newArrayList();
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin entriesLock.writeLock().lock(); // Akarin
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -293,7 +294,7 @@ public class EntityTracker {
} }
} }
} }
entriesLock.unlock(); // Akarin entriesLock.writeLock().unlock(); // Akarin
Entity entity1; Entity entity1;
@@ -320,13 +321,13 @@ public class EntityTracker {
public void a(int i) { public void a(int i) {
this.e = (i - 1) * 16; this.e = (i - 1) * 16;
Iterator iterator = this.c.iterator(); Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin entriesLock.readLock().lock(); // Akarin
while (iterator.hasNext()) { while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(this.e); entitytrackerentry.a(this.e);
} }
entriesLock.unlock(); // Akarin entriesLock.readLock().unlock(); // Akarin
} }
} }

View File

@@ -15,14 +15,14 @@ import org.bukkit.event.player.PlayerVelocityEvent;
/** /**
* Akarin Changes Note * Akarin Changes Note
* 1) Make trackedPlayerMap thread-safe (safety issue) * 1) Made entry access thread-safe (safety issue)
*/ */
public class EntityTrackerEntry { public class EntityTrackerEntry {
private static final Logger c = LogManager.getLogger(); private static final Logger c = LogManager.getLogger();
private final Entity tracker; private final Entity tracker;
private final int e; private final int e;
private int f; private volatile int f; // Akarin
private final int g; private final int g;
private long xLoc; private long xLoc;
private long yLoc; private long yLoc;
@@ -47,7 +47,7 @@ public class EntityTrackerEntry {
// Paper start // Paper start
// Replace trackedPlayers Set with a Map. The value is true until the player receives // Replace trackedPlayers Set with a Map. The value is true until the player receives
// their first update (which is forced to have absolute coordinates), false afterward. // their first update (which is forced to have absolute coordinates), false afterward.
public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.concurrent.ConcurrentHashMap<EntityPlayer, Boolean>(); // Akarin - make concurrent public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.HashMap<EntityPlayer, Boolean>();
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet(); public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
// Paper end // Paper end
@@ -489,13 +489,23 @@ public class EntityTrackerEntry {
return false; return false;
} }
private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) { private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) {
return entity == entityplayer || entity.tracker != null && entity.tracker.trackedPlayers.contains(entityplayer); // Akarin start
boolean mayTrackedBy = entity == entityplayer || entity.tracker != null;
if (mayTrackedBy) {
((WorldServer) entity.world).tracker.entriesLock.readLock().lock();
mayTrackedBy = entity.tracker.trackedPlayers.contains(entityplayer);
((WorldServer) entity.world).tracker.entriesLock.readLock().unlock();
}
return mayTrackedBy;
// Akarin end
} }
private void updatePassengers(EntityPlayer player) { private void updatePassengers(EntityPlayer player) {
if (tracker.isVehicle()) { if (tracker.isVehicle()) {
tracker.passengers.forEach((e) -> { tracker.passengers.forEach((e) -> {
if (e.tracker != null) { if (e.tracker != null) {
((WorldServer) e.world).tracker.entriesLock.writeLock().lock(); // Akarin
e.tracker.updatePlayer(player); e.tracker.updatePlayer(player);
((WorldServer) e.world).tracker.entriesLock.writeLock().unlock(); // Akarin
} }
}); });
player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker)); player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));

View File

@@ -234,11 +234,8 @@ public class PlayerChunkMap {
} // Paper timing } // Paper timing
} }
boolean unlockRequired = true; // Akarin
managedPlayersLock.readLock().lock(); // Akarin managedPlayersLock.readLock().lock(); // Akarin
if (this.managedPlayers.isEmpty()) { if (this.managedPlayers.isEmpty()) {
managedPlayersLock.readLock().unlock(); // Akarin
unlockRequired = false; // Akarin
try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper
WorldProvider worldprovider = this.world.worldProvider; WorldProvider worldprovider = this.world.worldProvider;
@@ -247,7 +244,7 @@ public class PlayerChunkMap {
} }
} // Paper timing } // Paper timing
} }
if (unlockRequired) managedPlayersLock.readLock().unlock(); // Akarin managedPlayersLock.readLock().unlock(); // Akarin
} }

View File

@@ -1107,10 +1107,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
EntityTracker tracker = ((WorldServer) entity.world).tracker; EntityTracker tracker = ((WorldServer) entity.world).tracker;
// Paper end // Paper end
tracker.entriesLock.updateLock().lock(); // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId()); EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null) { if (entry != null) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.clear(getHandle()); entry.clear(getHandle());
tracker.entriesLock.writeLock().unlock(); // Akarin
} }
tracker.entriesLock.updateLock().unlock(); // Akarin
// Remove the hidden player from this player user list, if they're on it // Remove the hidden player from this player user list, if they're on it
if (other.sentListPacket) { if (other.sentListPacket) {
@@ -1157,12 +1161,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other)); getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
tracker.entriesLock.lock(); // Akarin tracker.entriesLock.updateLock().lock(); // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId()); EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null && !entry.trackedPlayers.contains(getHandle())) { if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.updatePlayer(getHandle()); entry.updatePlayer(getHandle());
tracker.entriesLock.unlock(); // Akarin tracker.entriesLock.writeLock().unlock(); // Akarin
} }
tracker.entriesLock.updateLock().unlock(); // Akarin
} }
// Paper start // Paper start
private void reregisterPlayer(EntityPlayer player) { private void reregisterPlayer(EntityPlayer player) {