ThreadTracker + Attempt on NPC Disappearing fix

Engineered the ThreadTracker: server-embedded deadlock auditing tool, which will print error messages in case of found deadlocks (also showing all in-use locks on the time of the issue).
Changed the player's id on DB now starting from 20mil, thus preventing players from overwriting NPC/mobs with same oid in-game. Requires proper testing to see if the issue has been cleared.
This commit is contained in:
ronancpl
2017-11-16 12:22:32 -02:00
parent aecc3e300a
commit 2b38b62683
50 changed files with 1099 additions and 123 deletions
+6 -5
View File
@@ -45,7 +45,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Comparator;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
@@ -154,6 +154,7 @@ import constants.skills.ThunderBreaker;
import net.server.channel.handlers.PartyOperationHandler;
import scripting.item.ItemScriptManager;
import server.maps.MapleMapItem;
import tools.locks.MonitoredEnums;
public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private static NumberFormat nf = new DecimalFormat("#,###,###,###");
@@ -270,10 +271,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private ScheduledFuture<?> extraRecoveryTask = null;
private ScheduledFuture<?> chairRecoveryTask = null;
private ScheduledFuture<?> pendantOfSpirit = null; //1122017
private Lock chrLock = new ReentrantLock(true);
private Lock effLock = new ReentrantLock(true);
private Lock petLock = new ReentrantLock(true); // for quest tasks as well
private Lock prtLock = new ReentrantLock();
private Lock chrLock = new MonitoredReentrantLock(MonitoredEnums.CHR, true);
private Lock effLock = new MonitoredReentrantLock(MonitoredEnums.EFF, true);
private Lock petLock = new MonitoredReentrantLock(MonitoredEnums.PET, true); // for quest tasks as well
private Lock prtLock = new MonitoredReentrantLock(MonitoredEnums.PRT);
private Map<Integer, Set<Integer>> excluded = new LinkedHashMap<>();
private Set<Integer> excludedItems = new LinkedHashSet<>();
private List<MapleRing> crushRings = new ArrayList<>();
+4 -3
View File
@@ -42,7 +42,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import javax.script.ScriptEngine;
@@ -77,6 +77,7 @@ import tools.FilePrinter;
import tools.HexTool;
import tools.MapleAESOFB;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public class MapleClient {
@@ -108,7 +109,7 @@ public class MapleClient {
private int picattempt = 0;
private byte gender = -1;
private boolean disconnecting = false;
private final Lock lock = new ReentrantLock(true);
private final Lock lock = new MonitoredReentrantLock(MonitoredEnums.CLIENT, true);
private int votePoints;
private int voteTime = -1;
private long lastNpcClick;
@@ -832,7 +833,7 @@ public class MapleClient {
removePlayer();
player.saveCooldowns();
player.saveToDB();
player.saveToDB(true);
player = null;
return;
+3 -2
View File
@@ -31,16 +31,17 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public final class MonsterBook {
private int specialCard = 0;
private int normalCard = 0;
private int bookLevel = 1;
private Map<Integer, Integer> cards = new LinkedHashMap<>();
private Lock lock = new ReentrantLock();
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.BOOK);
private Set<Entry<Integer, Integer>> getCardSet() {
lock.lock();
+3 -3
View File
@@ -346,7 +346,7 @@ public class Commands {
case "time":
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("-GMT3"));
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
player.yellowMessage("Solaxia Server Time: " + dateFormat.format(new Date()));
break;
@@ -741,7 +741,7 @@ public class Commands {
player.yellowMessage("Players on this map:");
for (MapleMapObject mmo : player.getMap().getPlayers()) {
MapleCharacter chr = (MapleCharacter) mmo;
player.dropMessage(5, ">> " + chr.getName());
player.dropMessage(5, ">> " + chr.getName() + " - " + chr.getId() + " - Oid: " + chr.getObjectId());
}
player.yellowMessage("NPCs on this map:");
for (MapleMapObject npcs : player.getMap().getMapObjects()) {
@@ -755,7 +755,7 @@ public class Commands {
if (mobs instanceof MapleMonster) {
MapleMonster mob = (MapleMonster) mobs;
if(mob.isAlive()){
player.dropMessage(5, ">> " + mob.getName() + " - " + mob.getId());
player.dropMessage(5, ">> " + mob.getName() + " - " + mob.getId() + " - Oid: " + mob.getObjectId());
}
}
}
+4 -2
View File
@@ -27,9 +27,11 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -45,7 +47,7 @@ public enum ItemFactory {
MERCHANT(6, false);
private final int value;
private final boolean account;
private static final ReentrantLock lock = new ReentrantLock(true);
private static final Lock lock = new MonitoredReentrantLock(MonitoredEnums.ITEM, true);
private ItemFactory(int value, boolean account) {
this.value = value;
+3 -2
View File
@@ -30,7 +30,7 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.Pair;
import client.MapleCharacter;
@@ -39,6 +39,7 @@ import constants.ItemConstants;
import server.MapleItemInformationProvider;
import server.MapleInventoryManipulator;
import tools.FilePrinter;
import tools.locks.MonitoredEnums;
/**
*
@@ -50,7 +51,7 @@ public class MapleInventory implements Iterable<Item> {
private byte slotLimit;
private MapleInventoryType type;
private boolean checked = false;
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.INVENTORY, true);
public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) {
this.owner = mc;
+4 -1
View File
@@ -8,10 +8,12 @@ public class ServerConstants {
public static String DB_URL = "";
public static String DB_USER = "";
public static String DB_PASS = "";
public static final boolean DB_EXPERIMENTAL_POOL = true; //[EXPERIMENTAL] Installs a connection pool to hub DB connections. Set false to default.
public static final boolean DB_EXPERIMENTAL_POOL = true; //[EXPERIMENTAL] Installs a connection pool to hub DB connections. Set false to default.
public static final boolean USE_THREAD_TRACKER = true; //[WARNING] This deadlock auditing thing will bloat the memory as fast as the time frame one takes to lose track of a raindrop on a tempesting day. Only for debug purposes.
//World And Version
public static short VERSION = 83;
public static String TIMEZONE = "-GMT3";
public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"};
//Login Configuration
@@ -85,6 +87,7 @@ public class ServerConstants {
//Dangling Items Configuration
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history.
public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reach timed out, a critical server deadlock has made present.
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
+4 -3
View File
@@ -43,13 +43,14 @@ import client.MapleClient;
import constants.ServerConstants;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.ScheduledFuture;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import server.TimerManager;
import tools.locks.MonitoredEnums;
public class MapleServerHandler extends IoHandlerAdapter {
@@ -58,8 +59,8 @@ public class MapleServerHandler extends IoHandlerAdapter {
private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm");
private static AtomicLong sessionId = new AtomicLong(7777);
private Lock idleLock = new ReentrantLock(true);
private Lock tempLock = new ReentrantLock(true);
private Lock idleLock = new MonitoredReentrantLock(MonitoredEnums.SHANDLER_IDLE, true);
private Lock tempLock = new MonitoredReentrantLock(MonitoredEnums.SHANDLER_TEMP, true);
private Map<MapleClient, Long> idleSessions = new HashMap<>(100);
private Map<MapleClient, Long> tempIdleSessions = new HashMap<>();
private ScheduledFuture<?> idleManager = null;
+11 -10
View File
@@ -35,27 +35,28 @@ public class MaplePacketEncoder implements ProtocolEncoder {
public void encode(final IoSession session, final Object message, final ProtocolEncoderOutput out) throws Exception {
final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
if (client != null) {
final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message;
final byte[] unencrypted = new byte[input.length];
System.arraycopy(input, 0, unencrypted, 0, input.length);
final byte[] ret = new byte[unencrypted.length + 4];
final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
MapleCustomEncryption.encryptData(unencrypted);
try {
client.lockClient();
try {
final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message;
final byte[] unencrypted = new byte[input.length];
System.arraycopy(input, 0, unencrypted, 0, input.length);
final byte[] ret = new byte[unencrypted.length + 4];
final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
MapleCustomEncryption.encryptData(unencrypted);
send_crypto.crypt(unencrypted);
System.arraycopy(header, 0, ret, 0, 4);
System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
out.write(IoBuffer.wrap(ret));
} finally {
client.unlockClient();
}
// System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
// out.write(ByteBuffer.wrap(ret));
} else {
} catch (NullPointerException npe) {
out.write(IoBuffer.wrap(((byte[]) message)));
}
}
+3 -2
View File
@@ -25,7 +25,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredEnums;
import tools.locks.MonitoredReentrantLock;
/**
*
@@ -33,7 +34,7 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class PlayerBuffStorage {
private int id = (int) (Math.random() * 100);
private final Lock lock = new ReentrantLock(true);
private final Lock lock = new MonitoredReentrantLock(MonitoredEnums.BUFF_STORAGE, true);
private Map<Integer, List<PlayerBuffValueHolder>> buffs = new HashMap<>();
public void addBuffsToStorage(int chrid, List<PlayerBuffValueHolder> toStore) {
+3 -1
View File
@@ -26,12 +26,14 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import tools.locks.MonitoredEnums;
public class PlayerStorage {
private final ReentrantReadWriteLock locks = new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredEnums.PLAYER_STORAGE, true);
private final ReadLock rlock = locks.readLock();
private final WriteLock wlock = locks.writeLock();
private final Map<Integer, MapleCharacter> storage = new LinkedHashMap<>();
+17 -9
View File
@@ -33,15 +33,16 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.ScheduledFuture;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
@@ -70,11 +71,13 @@ import client.SkillFactory;
import constants.ItemConstants;
import constants.ServerConstants;
import java.util.Calendar;
import net.server.audit.ThreadTracker;
import server.quest.MapleQuest;
import tools.locks.MonitoredEnums;
public class Server implements Runnable {
private static final Set<Integer> activeFly = new HashSet<>();
private static final Map<Integer, Integer> couponRates = new LinkedHashMap<>();
private static final Map<Integer, Integer> couponRates = new HashMap<>(30);
private static final List<Integer> activeCoupons = new LinkedList<>();
private IoAcceptor acceptor;
@@ -83,11 +86,12 @@ public class Server implements Runnable {
private final Properties subnetInfo = new Properties();
private static Server instance = null;
private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
private final Map<Integer, MapleGuild> guilds = new LinkedHashMap<>();
private final Map<MapleClient, Long> inLoginState = new LinkedHashMap<>();
private final Lock srvLock = new ReentrantLock();
private final Map<Integer, MapleGuild> guilds = new HashMap<>(100);
private final Map<MapleClient, Long> inLoginState = new HashMap<>(100);
private final Lock srvLock = new MonitoredReentrantLock(MonitoredEnums.SERVER);
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
private final Map<Integer, MapleAlliance> alliances = new LinkedHashMap<>();
private final Map<Integer, MapleAlliance> alliances = new HashMap<>(100);
private boolean online = false;
public static long uptime = System.currentTimeMillis();
@@ -301,7 +305,9 @@ public class Server implements Runnable {
timeToTake = System.currentTimeMillis();
MapleQuest.loadAllQuest();
System.out.println("Quest loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().registerThreadTrackerTask();
try {
Integer worldCount = Math.min(ServerConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds")));
@@ -317,7 +323,7 @@ public class Server implements Runnable {
worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
worlds.add(world);
channels.add(new LinkedHashMap<Integer, String>());
channels.add(new HashMap<Integer, String>());
for (int j = 0; j < Integer.parseInt(p.getProperty("channels" + i)); j++) {
int channelid = j + 1;
Channel channel = new Channel(i, channelid);
@@ -807,6 +813,8 @@ public class Server implements Runnable {
}
}
}*/
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().cancelThreadTrackerTask();
TimerManager.getInstance().purge();
TimerManager.getInstance().stop();
+281
View File
@@ -0,0 +1,281 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.audit;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import server.TimerManager;
import tools.FilePrinter;
import tools.locks.MonitoredEnums;
import constants.ServerConstants;
/**
*
* @author RonanLana
*
* This tool has the main purpose of auditing deadlocks throughout the server and must be used only for debugging. The flag is USE_THREAD_TRACKER.
*/
public class ThreadTracker {
private static ThreadTracker instance = null;
private final Lock ttLock = new ReentrantLock(true);
private final Map<Long, List<MonitoredEnums>> threadTracker = new HashMap<>();
private final Map<Long, Integer> threadUpdate = new HashMap<>();
private final Map<Long, Thread> threads = new HashMap<>();
private final Map<Long, AtomicInteger> lockCount = new HashMap<>();
private final Map<Long, MonitoredEnums> lockIds = new HashMap<>();
private final Map<Long, Long> lockThreads = new HashMap<>();
private final Map<Long, Byte> lockUpdate = new HashMap<>();
private final Map<MonitoredEnums, Map<Long, Integer>> locks = new HashMap<>();
ScheduledFuture<?> threadTrackerSchedule;
private String printThreadTrackerState(String dateFormat) {
Map<MonitoredEnums, List<Integer>> lockValues = new HashMap<>();
Set<Long> executingThreads = new HashSet<>();
for(Map.Entry<Long, AtomicInteger> lock : lockCount.entrySet()) {
if(lock.getValue().get() != 0) {
executingThreads.add(lockThreads.get(lock.getKey()));
MonitoredEnums lockId = lockIds.get(lock.getKey());
List<Integer> list = lockValues.get(lockId);
if(list == null) {
list = new ArrayList<>();
lockValues.put(lockId, list);
}
list.add(lock.getValue().get());
}
}
String s = "----------------------------\r\n" + dateFormat + "\r\n ";
s += "Lock-thread usage count:";
for(Map.Entry<MonitoredEnums, List<Integer>> lock : lockValues.entrySet()) {
s += ("\r\n " + lock.getKey().name() + ": ");
for(Integer i : lock.getValue()) {
s += (i + " ");
}
}
s += "\r\n\r\nThread opened lock path:";
for(Long tid : executingThreads) {
s += "\r\n";
for(MonitoredEnums lockid : threadTracker.get(tid)) {
s += (lockid.name() + " ");
}
s += "|";
}
s += "\r\n\r\n";
return s;
}
private static String printThreadLog(List<MonitoredEnums> stillLockedPath, String dateFormat) {
String s = "----------------------------\r\n" + dateFormat + "\r\n ";
for(MonitoredEnums lock : stillLockedPath) {
s += (lock.name() + " ");
}
s += "\r\n\r\n";
return s;
}
private static String printThreadStack(StackTraceElement[] list, String dateFormat) {
String s = "----------------------------\r\n" + dateFormat + "\r\n";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
public void accessThreadTracker(boolean update, boolean lock, MonitoredEnums lockId, long lockOid) {
ttLock.lock();
try {
if(update) {
if(!lock) { // update tracker
List<Long> toRemove = new ArrayList<>();
for(Long l : threadUpdate.keySet()) {
int next = threadUpdate.get(l) + 1;
if(next == 4) {
List<MonitoredEnums> tt = threadTracker.get(l);
if(tt.isEmpty()) {
toRemove.add(l);
} else {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
String df = dateFormat.format(new Date());
FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df));
FilePrinter.print(FilePrinter.DEADLOCK_STACK, printThreadStack(threads.get(l).getStackTrace(), df));
}
}
threadUpdate.put(l, next);
}
for(Long l : toRemove) {
threadTracker.remove(l);
threadUpdate.remove(l);
threads.remove(l);
for(Map<Long, Integer> threadLock : locks.values()) {
threadLock.remove(l);
}
}
toRemove.clear();
for(Entry<Long, Byte> it : lockUpdate.entrySet()) {
byte val = (byte)(it.getValue() + 1);
if(val < 60) { // free the structure after 60 silent updates
lockUpdate.put(it.getKey(), val);
} else {
toRemove.add(it.getKey());
}
}
for(Long l : toRemove) {
lockCount.remove(l);
lockIds.remove(l);
lockThreads.remove(l);
lockUpdate.remove(l);
}
} else { // print status
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
FilePrinter.printError(FilePrinter.DEADLOCK_STATE, printThreadTrackerState(dateFormat.format(new Date())));
//FilePrinter.printError(FilePrinter.DEADLOCK_STATE, "[" + dateFormat.format(new Date()) + "] Presenting current lock path for lockid " + lockId.name() + ".\r\n" + printLockStatus(lockId) + "\r\n-------------------------------\r\n");
}
} else {
long tid = Thread.currentThread().getId();
if(lock) {
AtomicInteger c = lockCount.get(lockOid);
if(c == null) {
c = new AtomicInteger(0);
lockCount.put(lockOid, c);
lockIds.put(lockOid, lockId);
lockThreads.put(lockOid, tid);
lockUpdate.put(lockOid, (byte) 0);
}
c.incrementAndGet();
List<MonitoredEnums> list = threadTracker.get(tid);
if(list == null) {
list = new ArrayList<>(20);
threadTracker.put(tid, list);
threadUpdate.put(tid, 0);
threads.put(tid, Thread.currentThread());
}
list.add(lockId);
Map<Long, Integer> threadLock = locks.get(lockId);
if(threadLock == null) {
threadLock = new HashMap<>(5);
locks.put(lockId, threadLock);
}
Integer lc = threadLock.get(tid);
if(lc != null) {
threadLock.put(tid, lc + 1);
} else {
threadLock.put(tid, 1);
}
}
else {
AtomicInteger c = lockCount.get(lockOid);
c.decrementAndGet();
lockUpdate.put(lockOid, (byte) 0);
List<MonitoredEnums> list = threadTracker.get(tid);
for(int i = list.size() - 1; i >= 0; i--) {
if(lockId.getValue() == list.get(i).getValue()) {
list.remove(i);
break;
}
}
Map<Long, Integer> threadLock = locks.get(lockId);
threadLock.put(tid, threadLock.get(tid) - 1);
}
}
} finally {
ttLock.unlock();
}
}
private String printLockStatus(MonitoredEnums lockId) {
String s = "";
for(Long threadid : locks.get(lockId).keySet()) {
for(MonitoredEnums lockid : threadTracker.get(threadid)) {
s += (" " + lockid.name());
}
s += " |\r\n";
}
return s;
}
public void registerThreadTrackerTask() {
threadTrackerSchedule = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
accessThreadTracker(true, false, MonitoredEnums.UNDEFINED, -1);
}
}, 10000, 10000);
}
public void cancelThreadTrackerTask() {
threadTrackerSchedule.cancel(false);
}
public static ThreadTracker getInstance() {
if (instance == null) {
instance = new ThreadTracker();
}
return instance;
}
}
+5 -3
View File
@@ -33,7 +33,8 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -67,6 +68,7 @@ import tools.MaplePacketCreator;
import client.MapleCharacter;
import constants.ServerConstants;
import server.maps.MapleMiniDungeonInfo;
import tools.locks.MonitoredEnums;
public final class Channel {
@@ -90,11 +92,11 @@ public final class Channel {
private Map<Integer, Integer> dojoParty = new HashMap<>();
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
private ReentrantReadWriteLock merchantLock = new ReentrantReadWriteLock(true);
private ReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MERCHANT, true);
private ReadLock merchRlock = merchantLock.readLock();
private WriteLock merchWlock = merchantLock.writeLock();
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.CHANNEL, true);
public Channel(final int world, final int channel) {
this.world = world;
@@ -84,7 +84,7 @@ public final class ReportHandler extends AbstractMaplePacketHandler {
public void addReport(int reporterid, int victimid, int reason, String description, String chatlog) {
Calendar calendar = Calendar.getInstance();
Timestamp currentTimestamp = new java.sql.Timestamp(calendar.getTime().getTime());
Connection con = null;
Connection con;
try {
con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("INSERT INTO reports (`reporttime`, `reporterid`, `victimid`, `reason`, `chatlog`, `description`) VALUES (?, ?, ?, ?, ?, ?)");
@@ -51,6 +51,7 @@ import server.life.MobAttackInfoFactory;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Randomizer;
@@ -78,9 +79,17 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
oid = slea.readInt();
try {
attacker = (MapleMonster) map.getMapObject(oid);
List<loseItem> loseItems;
MapleMapObject mmo = map.getMapObject(oid);
if(mmo instanceof MapleMonster) {
attacker = (MapleMonster) mmo;
if(attacker.getId() != monsteridfrom) {
attacker = null;
}
}
if (attacker != null) {
List<loseItem> loseItems;
if (attacker.isBuffed(MonsterStatus.NEUTRALISE)) {
return;
}
@@ -119,10 +128,8 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
} catch(ClassCastException e) {
//this happens due to mob on last map damaging player just before changing maps
if(ServerConstants.USE_DEBUG) {
e.printStackTrace();
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity.");
}
e.printStackTrace();
FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Attacker is not a mob-type, rather is a " + map.getMapObject(oid).getClass().getName() + " entity.");
return;
}
+3 -2
View File
@@ -36,12 +36,13 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import net.server.Server;
import net.server.channel.Channel;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public class MapleGuild {
public final static int CREATE_GUILD_COST = 1500000;
@@ -52,7 +53,7 @@ public class MapleGuild {
}
private final List<MapleGuildCharacter> members;
private final Lock membersLock = new ReentrantLock(true);
private final Lock membersLock = new MonitoredReentrantLock(MonitoredEnums.GUILD, true);
private String rankTitles[] = new String[5]; // 1 = master, 2 = jr, 5 = lowest member
private String name, notice;
+3 -2
View File
@@ -30,8 +30,9 @@ import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Comparator;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredEnums;
public class MapleParty {
private int id;
@@ -43,7 +44,7 @@ public class MapleParty {
private Map<Integer, Integer> histMembers = new HashMap<>();
private int nextEntry = 0;
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.PARTY, true);
public MapleParty(int id, MaplePartyCharacter chrfor) {
this.leaderId = chrfor.getId();
+8 -7
View File
@@ -43,7 +43,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.ScheduledFuture;
@@ -68,6 +68,7 @@ import server.maps.AbstractMapleMapObject;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -89,25 +90,25 @@ public class World {
private Map<Integer, MapleParty> parties = new HashMap<>();
private AtomicInteger runningPartyId = new AtomicInteger();
private Lock partyLock = new ReentrantLock(true);
private Lock partyLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_PARTY, true);
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
private Lock owlLock = new ReentrantLock();
private Lock owlLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_OWL);
private Lock activePetsLock = new ReentrantLock(true);
private Lock activePetsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_PETS, true);
private Map<Integer, Byte> activePets = new LinkedHashMap<>();
private ScheduledFuture<?> petsSchedule;
private long petUpdate;
private Lock activeMountsLock = new ReentrantLock(true);
private Lock activeMountsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_MOUNTS, true);
private Map<Integer, Byte> activeMounts = new LinkedHashMap<>();
private ScheduledFuture<?> mountsSchedule;
private long mountUpdate;
private Lock activePlayerShopsLock = new ReentrantLock(true);
private Lock activePlayerShopsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_PSHOPS, true);
private Map<Integer, MaplePlayerShop> activePlayerShops = new LinkedHashMap<>();
private Lock activeMerchantsLock = new ReentrantLock(true);
private Lock activeMerchantsLock = new MonitoredReentrantLock(MonitoredEnums.WORLD_MERCHS, true);
private Map<Integer, Pair<MapleHiredMerchant, Byte>> activeMerchants = new LinkedHashMap<>();
private long merchantUpdate;
@@ -35,7 +35,8 @@ import java.util.Set;
import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -69,6 +70,7 @@ import server.MapleItemInformationProvider;
import server.life.MapleLifeFactory;
import server.life.MapleNPC;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
/**
*
@@ -90,12 +92,12 @@ public class EventInstanceManager {
private List<Integer> mapIds = new LinkedList<>();
private List<Boolean> isInstanced = new LinkedList<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredEnums.EIM, true);
private final ReadLock rL = lock.readLock();
private final WriteLock wL = lock.writeLock();
private final Lock pL = new ReentrantLock(true);
private final Lock sL = new ReentrantLock();
private final Lock pL = new MonitoredReentrantLock(MonitoredEnums.EIM_PARTY, true);
private final Lock sL = new MonitoredReentrantLock(MonitoredEnums.EIM_SCRIPT, true);
private ScheduledFuture<?> event_schedule = null;
private boolean disposed = false;
+4 -3
View File
@@ -53,7 +53,8 @@ import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredEnums;
import tools.locks.MonitoredReentrantLock;
/**
*
@@ -74,8 +75,8 @@ public class EventManager {
private Integer readyId = 0;
private Properties props = new Properties();
private String name;
private Lock lobbyLock = new ReentrantLock();
private Lock queueLock = new ReentrantLock();
private Lock lobbyLock = new MonitoredReentrantLock(MonitoredEnums.EM_LOBBY);
private Lock queueLock = new MonitoredReentrantLock(MonitoredEnums.EM_QUEUE);
private static final int maxLobbys = 8; // an event manager holds up to this amount of concurrent lobbys
+3 -2
View File
@@ -31,7 +31,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import provider.MapleData;
import provider.MapleDataProvider;
@@ -46,6 +46,7 @@ import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.ItemConstants;
import java.util.Collections;
import tools.locks.MonitoredEnums;
/*
* @author Flav
@@ -241,7 +242,7 @@ public class CashShop {
private List<Item> inventory = new ArrayList<>();
private List<Integer> wishList = new ArrayList<>();
private int notes = 0;
private Lock lock = new ReentrantLock();
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.CASHSHOP);
public CashShop(int accountId, int characterId, int jobType) throws SQLException {
this.accountId = accountId;
+3 -2
View File
@@ -32,13 +32,14 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import net.SendOpcode;
import server.maps.AbstractMapleMapObject;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.output.MaplePacketLittleEndianWriter;
import tools.locks.MonitoredEnums;
/**
*
@@ -55,7 +56,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
private List<String> bannedList = new ArrayList<>();
private List<Pair<MapleCharacter, String>> chatLog = new LinkedList<>();
private Map<Integer, Byte> chatSlot = new LinkedHashMap<>();
private Lock visitorLock = new ReentrantLock(true);
private Lock visitorLock = new MonitoredReentrantLock(MonitoredEnums.VISITOR_PSHOP, true);
public MaplePlayerShop(MapleCharacter owner, String description) {
this.setPosition(owner.getPosition());
+3 -2
View File
@@ -34,10 +34,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -50,7 +51,7 @@ public class MapleStorage {
private byte slots;
private Map<MapleInventoryType, List<Item>> typeItems = new HashMap<>();
private List<Item> items;
private Lock lock = new ReentrantLock(true);
private Lock lock = new MonitoredReentrantLock(MonitoredEnums.STORAGE, true);
private MapleStorage(int id, byte slots, int meso) {
this.id = id;
+6 -4
View File
@@ -51,7 +51,8 @@ import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import server.TimerManager;
@@ -62,6 +63,7 @@ import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.locks.MonitoredEnums;
public class MapleMonster extends AbstractLoadedMapleLife {
private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats.
@@ -84,9 +86,9 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private int team;
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
private ReentrantLock externalLock = new ReentrantLock();
private ReentrantLock monsterLock = new ReentrantLock(true);
private ReentrantLock statiLock = new ReentrantLock();
private Lock externalLock = new MonitoredReentrantLock(MonitoredEnums.MOB_EXT);
private Lock monsterLock = new MonitoredReentrantLock(MonitoredEnums.MOB, true);
private Lock statiLock = new MonitoredReentrantLock(MonitoredEnums.MOB_STATI);
public MapleMonster(int id, MapleMonsterStats stats) {
super(id);
+3 -1
View File
@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -34,6 +35,7 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import tools.locks.MonitoredEnums;
/**
*
@@ -44,7 +46,7 @@ public class MobSkillFactory {
private static Map<String, MobSkill> mobSkills = new HashMap<String, MobSkill>();
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
private final static ReentrantReadWriteLock dataLock = new ReentrantReadWriteLock();
private final static ReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MOBSKILL_FACTORY);
private final static ReadLock rL = dataLock.readLock();
private final static WriteLock wL = dataLock.writeLock();
+3 -2
View File
@@ -27,7 +27,8 @@ import scripting.portal.PortalScriptManager;
import server.MaplePortal;
import tools.MaplePacketCreator;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredEnums;
import tools.locks.MonitoredReentrantLock;
public class MapleGenericPortal implements MaplePortal {
@@ -117,7 +118,7 @@ public class MapleGenericPortal implements MaplePortal {
if(scriptName != null) {
if(scriptLock == null) {
scriptLock = new ReentrantLock(false);
scriptLock = new MonitoredReentrantLock(MonitoredEnums.PORTAL, false);
}
} else {
scriptLock = null;
+3 -2
View File
@@ -37,7 +37,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
@@ -45,6 +45,7 @@ import server.MaplePlayerShopItem;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -63,7 +64,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
private List<SoldItem> sold = new LinkedList<>();
private AtomicBoolean open = new AtomicBoolean();
private MapleMap map;
private Lock visitorLock = new ReentrantLock(true);
private Lock visitorLock = new MonitoredReentrantLock(MonitoredEnums.VISITOR_MERCH, true);
public MapleHiredMerchant(final MapleCharacter owner, int itemId, String desc) {
this.setPosition(owner.getPosition());
+5 -3
View File
@@ -52,6 +52,7 @@ import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicInteger;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -78,13 +79,14 @@ import server.life.MonsterGlobalDropEntry;
import server.life.SpawnPoint;
import server.partyquest.MonsterCarnival;
import server.partyquest.MonsterCarnivalParty;
import server.partyquest.Pyramid;
//import server.partyquest.Pyramid;
import scripting.event.EventInstanceManager;
import server.life.MonsterListener;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
import tools.locks.MonitoredEnums;
public class MapleMap {
private static final List<MapleMapObjectType> rangedMapobjectTypes = Arrays.asList(MapleMapObjectType.SHOP, MapleMapObjectType.ITEM, MapleMapObjectType.NPC, MapleMapObjectType.MONSTER, MapleMapObjectType.DOOR, MapleMapObjectType.SUMMON, MapleMapObjectType.REACTOR);
@@ -161,11 +163,11 @@ public class MapleMap {
if (this.monsterRate == 0) {
this.monsterRate = 1;
}
final ReentrantReadWriteLock chrLock = new ReentrantReadWriteLock(true);
final ReentrantReadWriteLock chrLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MAP_CHRS, true);
chrRLock = chrLock.readLock();
chrWLock = chrLock.writeLock();
final ReentrantReadWriteLock objectLock = new ReentrantReadWriteLock(true);
final ReentrantReadWriteLock objectLock = new MonitoredReentrantReadWriteLock(MonitoredEnums.MAP_OBJS, true);
objectRLock = objectLock.readLock();
objectWLock = objectLock.writeLock();
}
+3 -1
View File
@@ -33,6 +33,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import tools.locks.MonitoredReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
@@ -46,6 +47,7 @@ import server.life.MapleMonster;
import scripting.event.EventInstanceManager;
import tools.DatabaseConnection;
import tools.StringUtil;
import tools.locks.MonitoredEnums;
public class MapleMapFactory {
@@ -64,7 +66,7 @@ public class MapleMapFactory {
this.channel = channel;
this.event = eim;
ReentrantReadWriteLock rrwl = new ReentrantReadWriteLock();
ReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredEnums.MAP_FACTORY);
this.mapsRLock = rrwl.readLock();
this.mapsWLock = rrwl.writeLock();
}
+4 -2
View File
@@ -24,8 +24,10 @@ import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import java.awt.Point;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
public class MapleMapItem extends AbstractMapleMapObject {
@@ -35,7 +37,7 @@ public class MapleMapItem extends AbstractMapleMapObject {
protected byte type;
protected boolean pickedUp = false, playerDrop;
protected long dropTime;
private ReentrantLock itemLock = new ReentrantLock();
private Lock itemLock = new MonitoredReentrantLock(MonitoredEnums.MAP_ITEM);
public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, byte type, boolean playerDrop) {
setPosition(position);
+3 -2
View File
@@ -25,8 +25,9 @@ import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import tools.MaplePacketCreator;
import tools.locks.MonitoredEnums;
/**
*
@@ -35,7 +36,7 @@ import tools.MaplePacketCreator;
public class MapleMiniDungeon {
List<MapleCharacter> players = new ArrayList<>();
ScheduledFuture<?> timeoutTask = null;
Lock lock = new ReentrantLock(true);
Lock lock = new MonitoredReentrantLock(MonitoredEnums.MINIDUNGEON, true);
int baseMap;
long expireTime;
+3 -2
View File
@@ -29,12 +29,13 @@ import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import tools.locks.MonitoredReentrantLock;
import scripting.reactor.ReactorScriptManager;
import server.TimerManager;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.locks.MonitoredEnums;
/**
*
@@ -53,7 +54,7 @@ public class MapleReactor extends AbstractMapleMapObject {
private boolean shouldCollect;
private boolean attackHit;
private ScheduledFuture<?> timeoutTask = null;
private Lock reactorLock = new ReentrantLock(true);
private Lock reactorLock = new MonitoredReentrantLock(MonitoredEnums.REACTOR, true);
public MapleReactor(MapleReactorStats stats, int rid) {
this.evstate = (byte)0;
+5 -1
View File
@@ -44,7 +44,11 @@ public class FilePrinter {
QUEST_UNCODED = "uncodedQuests.txt",
AUTOSAVING_CHARACTER = "saveCharAuto.txt",
SAVING_CHARACTER = "saveChar.txt",
USED_COMMANDS = "usedCommands.txt";//more to come (maps)
USED_COMMANDS = "usedCommands.txt",
DEADLOCK_ERROR = "deadlocks.txt",
DEADLOCK_STACK = "deadlocks/path.txt",
DEADLOCK_LOCKS = "deadlocks/locks.txt",
DEADLOCK_STATE = "deadlocks/state.txt";
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); //for file system purposes, it's nice to use yyyy-MM-dd
private static final String FILE_PATH = "logs/" + sdf.format(Calendar.getInstance().getTime()) + "/"; // + sdf.format(Calendar.getInstance().getTime()) + "/"
+81
View File
@@ -0,0 +1,81 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
/**
*
* @author RonanLana
*/
public enum MonitoredEnums {
UNDEFINED(-1),
CHR(0),
EFF(1),
PET(2),
PRT(3),
CLIENT(4),
BOOK(5),
ITEM(6),
INVENTORY(7),
SHANDLER_IDLE(8),
SHANDLER_TEMP(9),
BUFF_STORAGE(10),
PLAYER_STORAGE(11),
SERVER(12),
MERCHANT(13),
CHANNEL(14),
GUILD(15),
PARTY(16),
WORLD_PARTY(17),
WORLD_OWL(18),
WORLD_PETS(19),
WORLD_MOUNTS(20),
WORLD_PSHOPS(21),
WORLD_MERCHS(21),
EIM(22),
EIM_PARTY(23),
EIM_SCRIPT(24),
EM_LOBBY(25),
EM_QUEUE(26),
CASHSHOP(27),
VISITOR_PSHOP(28),
STORAGE(29),
MOB_EXT(30),
MOB(31),
MOB_STATI(32),
MOBSKILL_FACTORY(33),
PORTAL(34),
VISITOR_MERCH(35),
MAP_CHRS(36),
MAP_OBJS(37),
MAP_FACTORY(38),
MAP_ITEM(39),
MINIDUNGEON(40),
REACTOR(41);
private final int i;
private MonitoredEnums(int val) {
this.i = val;
}
public int getValue() {
return i;
}
}
+147
View File
@@ -0,0 +1,147 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredReadLock extends ReentrantReadWriteLock.ReadLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredEnums id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredReadLock(MonitoredReentrantReadWriteLock lock) {
super(lock);
this.id = lock.id;
hashcode = this.hashCode();
}
@Override
public void lock() {
super.lock();
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}
+150
View File
@@ -0,0 +1,150 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredReentrantLock extends ReentrantLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredEnums id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredReentrantLock(MonitoredEnums id) {
super();
this.id = id;
hashcode = this.hashCode();
}
public MonitoredReentrantLock(MonitoredEnums id, boolean fair) {
super(fair);
this.id = id;
hashcode = this.hashCode();
}
@Override
public void lock() {
super.lock();
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}
@@ -0,0 +1,49 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
*
* @author RonanLana
*/
public class MonitoredReentrantReadWriteLock extends ReentrantReadWriteLock {
public final MonitoredEnums id;
public MonitoredReentrantReadWriteLock(MonitoredEnums id) {
super();
this.id = id;
}
public MonitoredReentrantReadWriteLock(MonitoredEnums id, boolean fair) {
super(fair);
this.id = id;
}
@Override
public ReadLock readLock() {
return super.readLock();
}
@Override
public WriteLock writeLock() {
return super.writeLock();
}
}
+145
View File
@@ -0,0 +1,145 @@
/*
* This file is part of the MapleSolaxiaV2 Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.locks;
import constants.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import net.server.Server;
import net.server.audit.ThreadTracker;
import tools.FilePrinter;
/**
*
* @author RonanLana
*/
public class MonitoredWriteLock extends ReentrantReadWriteLock.WriteLock {
private ScheduledFuture<?> timeoutSchedule = null;
private StackTraceElement[] deadlockedState = null;
private final MonitoredEnums id;
private final int hashcode;
private final Lock state = new ReentrantLock(true);
private final AtomicInteger reentrantCount = new AtomicInteger(0);
public MonitoredWriteLock(MonitoredReentrantReadWriteLock lock) {
super(lock);
this.id = lock.id;
hashcode = this.hashCode();
}
@Override
public void lock() {
super.lock();
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
}
@Override
public void unlock() {
if(ServerConstants.USE_THREAD_TRACKER) {
unregisterLocking();
}
super.unlock();
}
@Override
public boolean tryLock() {
if(super.tryLock()) {
if(ServerConstants.USE_THREAD_TRACKER) {
if(deadlockedState != null) {
//FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n");
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
deadlockedState = null;
}
registerLocking();
}
return true;
} else {
return false;
}
}
private void registerLocking() {
state.lock();
try {
ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode);
if(reentrantCount.incrementAndGet() == 1) {
final Thread t = Thread.currentThread();
timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
issueDeadlock(t);
}
}, ServerConstants.LOCK_MONITOR_TIME);
}
} finally {
state.unlock();
}
}
private void unregisterLocking() {
state.lock();
try {
if(reentrantCount.decrementAndGet() == 0) {
if(timeoutSchedule != null) {
timeoutSchedule.cancel(false);
timeoutSchedule = null;
}
}
ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode);
} finally {
state.unlock();
}
}
private void issueDeadlock(Thread t) {
deadlockedState = t.getStackTrace();
//super.unlock();
}
private static String printStackTrace(StackTraceElement[] list) {
String s = "";
for(int i = 0; i < list.length; i++) {
s += (" " + list[i].toString() + "\r\n");
}
return s;
}
}