Remove thread tracker system
This commit is contained in:
@@ -4,8 +4,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ServerConfig {
|
||||
//Thread Tracker Configuration
|
||||
public boolean USE_THREAD_TRACKER;
|
||||
|
||||
//Database Configuration
|
||||
public String DB_URL_FORMAT;
|
||||
|
||||
@@ -37,7 +37,6 @@ import constants.net.OpcodeConstants;
|
||||
import constants.net.ServerConstants;
|
||||
import net.netty.LoginServer;
|
||||
import net.packet.Packet;
|
||||
import net.server.audit.ThreadTracker;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.coordinator.session.IpAddresses;
|
||||
import net.server.coordinator.session.SessionCoordinator;
|
||||
@@ -869,10 +868,6 @@ public class Server {
|
||||
ThreadManager.getInstance().start();
|
||||
initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan
|
||||
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
ThreadTracker.getInstance().registerThreadTrackerTask();
|
||||
}
|
||||
|
||||
try {
|
||||
int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
||||
|
||||
@@ -945,7 +940,6 @@ public class Server {
|
||||
|
||||
long timeLeft = getTimeLeftForNextHour();
|
||||
tMan.register(new CharacterDiseaseTask(), YamlConfig.config.server.UPDATE_INTERVAL, YamlConfig.config.server.UPDATE_INTERVAL);
|
||||
tMan.register(new ReleaseLockTask(), MINUTES.toMillis(2), MINUTES.toMillis(2));
|
||||
tMan.register(new CouponTask(), YamlConfig.config.server.COUPON_INTERVAL, timeLeft);
|
||||
tMan.register(new RankingCommandTask(), MINUTES.toMillis(5), MINUTES.toMillis(5));
|
||||
tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft);
|
||||
@@ -1908,10 +1902,6 @@ public class Server {
|
||||
|
||||
List<Channel> allChannels = getAllChannels();
|
||||
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
ThreadTracker.getInstance().cancelThreadTrackerTask();
|
||||
}
|
||||
|
||||
for (Channel ch : allChannels) {
|
||||
while (!ch.finishedShutdown()) {
|
||||
try {
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* @author Ronan
|
||||
*/
|
||||
public class LockCollector {
|
||||
|
||||
private static final LockCollector instance = new LockCollector();
|
||||
|
||||
public static LockCollector getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final Map<Runnable, Integer> disposableLocks = new HashMap<>(200);
|
||||
private final Lock lock = new ReentrantLock(true);
|
||||
|
||||
public void registerDisposeAction(Runnable r) {
|
||||
lock.lock();
|
||||
try {
|
||||
disposableLocks.put(r, 0);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void runLockCollector() {
|
||||
List<Runnable> toDispose = new ArrayList<>();
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
for (Entry<Runnable, Integer> e : disposableLocks.entrySet()) {
|
||||
Integer eVal = e.getValue();
|
||||
if (eVal > 5) { // updates each 2min
|
||||
toDispose.add(e.getKey());
|
||||
} else {
|
||||
disposableLocks.put(e.getKey(), ++eVal);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
for (Runnable r : toDispose) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit;
|
||||
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import server.TimerManager;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
* <p>
|
||||
* 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 final Logger log = LoggerFactory.getLogger(ThreadTracker.class);
|
||||
private static ThreadTracker instance = null;
|
||||
|
||||
public static ThreadTracker getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new ThreadTracker();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final Lock ttLock = new ReentrantLock(true);
|
||||
|
||||
private final Map<Long, List<MonitoredLockType>> 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, MonitoredLockType> lockIds = new HashMap<>();
|
||||
private final Map<Long, Long> lockThreads = new HashMap<>();
|
||||
private final Map<Long, Integer> lockUpdate = new HashMap<>();
|
||||
|
||||
private final Map<MonitoredLockType, Map<Long, Integer>> locks = new HashMap<>();
|
||||
ScheduledFuture<?> threadTrackerSchedule;
|
||||
|
||||
private String printThreadTrackerState(String dateFormat) {
|
||||
|
||||
Map<MonitoredLockType, List<Integer>> lockValues = new HashMap<>();
|
||||
Set<Long> executingThreads = new HashSet<>();
|
||||
|
||||
for (Map.Entry<Long, AtomicInteger> lc : lockCount.entrySet()) {
|
||||
if (lc.getValue().get() != 0) {
|
||||
executingThreads.add(lockThreads.get(lc.getKey()));
|
||||
|
||||
MonitoredLockType lockId = lockIds.get(lc.getKey());
|
||||
List<Integer> list = lockValues.get(lockId);
|
||||
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
lockValues.put(lockId, list);
|
||||
}
|
||||
|
||||
list.add(lc.getValue().get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String s = "----------------------------\r\n" + dateFormat + "\r\n ";
|
||||
s += "Lock-thread usage count:";
|
||||
for (Map.Entry<MonitoredLockType, 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 (MonitoredLockType lockid : threadTracker.get(tid)) {
|
||||
s += (lockid.name() + " ");
|
||||
}
|
||||
s += "|";
|
||||
}
|
||||
|
||||
s += "\r\n\r\n";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private static String printThreadLog(List<MonitoredLockType> stillLockedPath, String dateFormat) {
|
||||
String s = "----------------------------\r\n" + dateFormat + "\r\n ";
|
||||
for (MonitoredLockType 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 (StackTraceElement stackTraceElement : list) {
|
||||
s += (" " + stackTraceElement.toString() + "\r\n");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void accessThreadTracker(boolean update, boolean lock, MonitoredLockType 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<MonitoredLockType> tt = threadTracker.get(l);
|
||||
|
||||
if (tt.isEmpty()) {
|
||||
toRemove.add(l);
|
||||
} else {
|
||||
StackTraceElement[] ste = threads.get(l).getStackTrace();
|
||||
if (ste.length > 0) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
|
||||
dateFormat.setTimeZone(TimeZone.getDefault());
|
||||
String df = dateFormat.format(new Date());
|
||||
|
||||
log.debug("Thread log - {}", printThreadLog(tt, df));
|
||||
log.debug("thread stack - {}", printThreadStack(ste, 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, Integer> it : lockUpdate.entrySet()) {
|
||||
int val = it.getValue() + 1;
|
||||
|
||||
if (val < 60) {
|
||||
lockUpdate.put(it.getKey(), val);
|
||||
} else {
|
||||
toRemove.add(it.getKey()); // free the structure after 60 silent updates
|
||||
}
|
||||
}
|
||||
|
||||
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.getDefault());
|
||||
|
||||
log.error("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-------------------------------");
|
||||
}
|
||||
} 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, 0);
|
||||
}
|
||||
c.incrementAndGet();
|
||||
|
||||
List<MonitoredLockType> list = threadTracker.get(tid);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>(5);
|
||||
threadTracker.put(tid, list);
|
||||
threadUpdate.put(tid, 0);
|
||||
threads.put(tid, Thread.currentThread());
|
||||
} else if (list.isEmpty()) {
|
||||
threadUpdate.put(tid, 0);
|
||||
}
|
||||
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);
|
||||
if (c != null) { // thanks BHB for detecting an NPE here
|
||||
c.decrementAndGet();
|
||||
}
|
||||
|
||||
lockUpdate.put(lockOid, 0);
|
||||
|
||||
List<MonitoredLockType> list = threadTracker.get(tid);
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
if (lockId.equals(list.get(i))) {
|
||||
list.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Map<Long, Integer> threadLock = locks.get(lockId);
|
||||
threadLock.put(tid, threadLock.get(tid) - 1);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
ttLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private String printLockStatus(MonitoredLockType lockId) {
|
||||
String s = "";
|
||||
|
||||
for (Long threadid : locks.get(lockId).keySet()) {
|
||||
for (MonitoredLockType lockid : threadTracker.get(threadid)) {
|
||||
s += (" " + lockid.name());
|
||||
}
|
||||
|
||||
s += " |\r\n";
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void registerThreadTrackerTask() {
|
||||
threadTrackerSchedule = TimerManager.getInstance().register(() -> accessThreadTracker(true, false, MonitoredLockType.UNDEFINED, -1), 10000, 10000);
|
||||
}
|
||||
|
||||
public void cancelThreadTrackerTask() {
|
||||
threadTrackerSchedule.cancel(false);
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
|
||||
public enum MonitoredLockType {
|
||||
UNDEFINED,
|
||||
INTERVAL,
|
||||
CHARACTER_CHR,
|
||||
CHARACTER_CPN,
|
||||
CHARACTER_EFF,
|
||||
CHARACTER_PET,
|
||||
CHARACTER_PRT,
|
||||
CHARACTER_EVT,
|
||||
CHARACTER_STA,
|
||||
CLIENT,
|
||||
CLIENT_ANNOUNCER,
|
||||
CLIENT_ENCODER,
|
||||
CLIENT_SESSION,
|
||||
CLIENT_LOGIN,
|
||||
BOOK,
|
||||
ITEM,
|
||||
INVENTORY,
|
||||
SRVHANDLER_IDLE,
|
||||
SRVHANDLER_TEMP,
|
||||
BUFF_STORAGE,
|
||||
PLAYER_STORAGE,
|
||||
PLAYER_DOOR,
|
||||
SERVER,
|
||||
SERVER_DISEASES,
|
||||
SERVER_LOGIN,
|
||||
SERVER_LOGIN_COORD,
|
||||
SERVER_WORLDS,
|
||||
MERCHANT,
|
||||
CHANNEL,
|
||||
CHANNEL_EVENTS,
|
||||
CHANNEL_FACEEXPRS,
|
||||
CHANNEL_FACESCHDL,
|
||||
CHANNEL_MOBACTION,
|
||||
CHANNEL_MOBANIMAT,
|
||||
CHANNEL_MOBMIST,
|
||||
CHANNEL_MOBSKILL,
|
||||
CHANNEL_MOBSTATUS,
|
||||
CHANNEL_OVTSTATUS,
|
||||
CHANNEL_OVERALL,
|
||||
GUILD,
|
||||
PARTY,
|
||||
WORLD_PARTY,
|
||||
WORLD_PARTY_SEARCH_ECHELON,
|
||||
WORLD_PARTY_SEARCH_QUEUE,
|
||||
WORLD_PARTY_SEARCH_STORAGE,
|
||||
WORLD_SRVMESSAGES,
|
||||
WORLD_PETS,
|
||||
WORLD_CHARS,
|
||||
WORLD_CHANNELS,
|
||||
WORLD_MOUNTS,
|
||||
WORLD_PSHOPS,
|
||||
WORLD_MERCHS,
|
||||
WORLD_MAPOBJS,
|
||||
WORLD_SAVECHARS,
|
||||
WORLD_SUGGEST,
|
||||
EIM,
|
||||
EIM_PARTY,
|
||||
EIM_SCRIPT,
|
||||
EM_LOBBY,
|
||||
EM_QUEUE,
|
||||
EM_SCHDL,
|
||||
EM_START,
|
||||
CASHSHOP,
|
||||
VISITOR_PSHOP,
|
||||
STORAGE,
|
||||
MOB,
|
||||
MOB_AGGRO,
|
||||
MOB_ANI,
|
||||
MOB_EXT,
|
||||
MOB_STATI,
|
||||
MOBSKILL_FACTORY,
|
||||
PORTAL,
|
||||
VISITOR_MERCH,
|
||||
MAP_CHRS,
|
||||
MAP_OBJS,
|
||||
MAP_MANAGER,
|
||||
MAP_ITEM,
|
||||
MAP_LOOT,
|
||||
MAP_BOUNDS,
|
||||
MAP_AGGRO,
|
||||
MAP_AGGRO_IDLE,
|
||||
MINIDUNGEON,
|
||||
REACTOR,
|
||||
REACTOR_HIT
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public interface MonitoredReadLock {
|
||||
|
||||
void lock();
|
||||
|
||||
void unlock();
|
||||
|
||||
boolean tryLock();
|
||||
|
||||
MonitoredReadLock dispose();
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public interface MonitoredReentrantLock {
|
||||
|
||||
void lock();
|
||||
|
||||
void unlock();
|
||||
|
||||
boolean tryLock();
|
||||
|
||||
MonitoredReentrantLock dispose();
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks;
|
||||
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MonitoredReentrantReadWriteLock extends ReentrantReadWriteLock {
|
||||
public final MonitoredLockType id;
|
||||
|
||||
public MonitoredReentrantReadWriteLock(MonitoredLockType id) {
|
||||
super();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MonitoredReentrantReadWriteLock(MonitoredLockType id, boolean fair) {
|
||||
super(fair);
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReadLock readLock() {
|
||||
return super.readLock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteLock writeLock() {
|
||||
return super.writeLock();
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public interface MonitoredWriteLock {
|
||||
|
||||
void lock();
|
||||
|
||||
void unlock();
|
||||
|
||||
boolean tryLock();
|
||||
|
||||
MonitoredWriteLock dispose();
|
||||
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks.active;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.ThreadTracker;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReadLock;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.empty.EmptyReadLock;
|
||||
import server.TimerManager;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
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 java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements MonitoredReadLock {
|
||||
private ScheduledFuture<?> timeoutSchedule = null;
|
||||
private StackTraceElement[] deadlockedState = null;
|
||||
private final MonitoredLockType id;
|
||||
private final int hashcode;
|
||||
private final Lock state = new ReentrantLock(true);
|
||||
private final AtomicInteger reentrantCount = new AtomicInteger(0);
|
||||
|
||||
public TrackerReadLock(MonitoredReentrantReadWriteLock lock) {
|
||||
super(lock);
|
||||
this.id = lock.id;
|
||||
hashcode = this.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
if (deadlockedState != null) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
|
||||
dateFormat.setTimeZone(TimeZone.getDefault());
|
||||
|
||||
//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));
|
||||
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
|
||||
deadlockedState = null;
|
||||
}
|
||||
|
||||
registerLocking();
|
||||
}
|
||||
|
||||
super.lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
unregisterLocking();
|
||||
}
|
||||
|
||||
super.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
if (super.tryLock()) {
|
||||
if (YamlConfig.config.server.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));
|
||||
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(() -> issueDeadlock(t), YamlConfig.config.server.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 (StackTraceElement stackTraceElement : list) {
|
||||
s += (" " + stackTraceElement.toString() + "\r\n");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoredReadLock dispose() {
|
||||
state.lock();
|
||||
try {
|
||||
if (timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
|
||||
reentrantCount.set(Integer.MAX_VALUE);
|
||||
} finally {
|
||||
state.unlock();
|
||||
}
|
||||
|
||||
//unlock();
|
||||
return new EmptyReadLock(id);
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks.active;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.ThreadTracker;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import net.server.audit.locks.empty.EmptyReentrantLock;
|
||||
import server.TimerManager;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class TrackerReentrantLock extends ReentrantLock implements MonitoredReentrantLock {
|
||||
private ScheduledFuture<?> timeoutSchedule = null;
|
||||
private StackTraceElement[] deadlockedState = null;
|
||||
private final MonitoredLockType id;
|
||||
private final int hashcode;
|
||||
private final Lock state = new ReentrantLock(true);
|
||||
private final AtomicInteger reentrantCount = new AtomicInteger(0);
|
||||
|
||||
public TrackerReentrantLock(MonitoredLockType id) {
|
||||
super();
|
||||
this.id = id;
|
||||
hashcode = this.hashCode();
|
||||
}
|
||||
|
||||
public TrackerReentrantLock(MonitoredLockType id, boolean fair) {
|
||||
super(fair);
|
||||
this.id = id;
|
||||
hashcode = this.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
if (deadlockedState != null) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
|
||||
dateFormat.setTimeZone(TimeZone.getDefault());
|
||||
|
||||
//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));
|
||||
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
|
||||
deadlockedState = null;
|
||||
}
|
||||
|
||||
registerLocking();
|
||||
}
|
||||
|
||||
super.lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
unregisterLocking();
|
||||
}
|
||||
|
||||
super.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
if (super.tryLock()) {
|
||||
if (YamlConfig.config.server.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));
|
||||
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(() -> issueDeadlock(t), YamlConfig.config.server.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 (StackTraceElement stackTraceElement : list) {
|
||||
s += (" " + stackTraceElement.toString() + "\r\n");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoredReentrantLock dispose() {
|
||||
state.lock();
|
||||
try {
|
||||
if (timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
|
||||
reentrantCount.set(Integer.MAX_VALUE);
|
||||
} finally {
|
||||
state.unlock();
|
||||
}
|
||||
|
||||
//unlock();
|
||||
return new EmptyReentrantLock(id);
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks.active;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.ThreadTracker;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
|
||||
import net.server.audit.locks.MonitoredWriteLock;
|
||||
import net.server.audit.locks.empty.EmptyWriteLock;
|
||||
import server.TimerManager;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
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 java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implements MonitoredWriteLock {
|
||||
private ScheduledFuture<?> timeoutSchedule = null;
|
||||
private StackTraceElement[] deadlockedState = null;
|
||||
private final MonitoredLockType id;
|
||||
private final int hashcode;
|
||||
private final Lock state = new ReentrantLock(true);
|
||||
private final AtomicInteger reentrantCount = new AtomicInteger(0);
|
||||
|
||||
public TrackerWriteLock(MonitoredReentrantReadWriteLock lock) {
|
||||
super(lock);
|
||||
this.id = lock.id;
|
||||
hashcode = this.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
if (deadlockedState != null) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
|
||||
dateFormat.setTimeZone(TimeZone.getDefault());
|
||||
|
||||
//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));
|
||||
ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode);
|
||||
deadlockedState = null;
|
||||
}
|
||||
|
||||
registerLocking();
|
||||
}
|
||||
|
||||
super.lock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {
|
||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
||||
unregisterLocking();
|
||||
}
|
||||
|
||||
super.unlock();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
if (super.tryLock()) {
|
||||
if (YamlConfig.config.server.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));
|
||||
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(() -> issueDeadlock(t), YamlConfig.config.server.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 (StackTraceElement stackTraceElement : list) {
|
||||
s += (" " + stackTraceElement.toString() + "\r\n");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoredWriteLock dispose() {
|
||||
state.lock();
|
||||
try {
|
||||
if (timeoutSchedule != null) {
|
||||
timeoutSchedule.cancel(false);
|
||||
timeoutSchedule = null;
|
||||
}
|
||||
|
||||
reentrantCount.set(Integer.MAX_VALUE);
|
||||
} finally {
|
||||
state.unlock();
|
||||
}
|
||||
|
||||
//unlock();
|
||||
return new EmptyWriteLock(id);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package net.server.audit.locks.empty;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public abstract class AbstractEmptyLock {
|
||||
|
||||
protected static String printThreadStack(StackTraceElement[] list) {
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); // DRY-code opportunity performed by jtumidanski
|
||||
dateFormat.setTimeZone(TimeZone.getDefault());
|
||||
String df = dateFormat.format(new Date());
|
||||
|
||||
String s = "\r\n" + df + "\r\n";
|
||||
for (StackTraceElement stackTraceElement : list) {
|
||||
s += (" " + stackTraceElement.toString() + "\r\n");
|
||||
}
|
||||
s += "----------------------------\r\n\r\n";
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks.empty;
|
||||
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReadLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class EmptyReadLock extends AbstractEmptyLock implements MonitoredReadLock {
|
||||
private static final Logger log = LoggerFactory.getLogger(EmptyReadLock.class);
|
||||
private final MonitoredLockType id;
|
||||
|
||||
public EmptyReadLock(MonitoredLockType type) {
|
||||
this.id = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
log.warn("Captured locking tentative on disposed lock {}: {}", id, printThreadStack(Thread.currentThread().getStackTrace()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
log.warn("Captured try-locking tentative on disposed lock {}: {}", id, printThreadStack(Thread.currentThread().getStackTrace()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoredReadLock dispose() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks.empty;
|
||||
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class EmptyReentrantLock extends AbstractEmptyLock implements MonitoredReentrantLock {
|
||||
private static final Logger log = LoggerFactory.getLogger(EmptyReentrantLock.class);
|
||||
private final MonitoredLockType id;
|
||||
|
||||
public EmptyReentrantLock(MonitoredLockType type) {
|
||||
this.id = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
log.warn("Captured locking tentative on disposed lock {}: {}", id, printThreadStack(Thread.currentThread().getStackTrace()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
log.warn("Captured try-locking tentative on disposed lock {}: {}", id, printThreadStack(Thread.currentThread().getStackTrace()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoredReentrantLock dispose() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.audit.locks.empty;
|
||||
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredWriteLock;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class EmptyWriteLock extends AbstractEmptyLock implements MonitoredWriteLock {
|
||||
private static final Logger log = LoggerFactory.getLogger(EmptyWriteLock.class);
|
||||
private final MonitoredLockType id;
|
||||
|
||||
public EmptyWriteLock(MonitoredLockType type) {
|
||||
this.id = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lock() {
|
||||
log.warn("Captured locking tentative on disposed lock {}: {}", id, printThreadStack(Thread.currentThread().getStackTrace()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlock() {}
|
||||
|
||||
@Override
|
||||
public boolean tryLock() {
|
||||
log.warn("Captured try-locking tentative on dispsoed lock {}: {}", id, printThreadStack(Thread.currentThread().getStackTrace()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MonitoredWriteLock dispose() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,6 @@ package net.server.services;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.Server;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.MonitoredReentrantLock;
|
||||
import server.TimerManager;
|
||||
import tools.Pair;
|
||||
|
||||
@@ -38,18 +36,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
public abstract class BaseScheduler {
|
||||
private int idleProcs = 0;
|
||||
private final List<SchedulerListener> listeners = new LinkedList<>();
|
||||
private final List<MonitoredReentrantLock> externalLocks = new LinkedList<>();
|
||||
private final List<Lock> externalLocks = new LinkedList<>();
|
||||
private final Map<Object, Pair<Runnable, Long>> registeredEntries = new HashMap<>();
|
||||
|
||||
private ScheduledFuture<?> schedulerTask = null;
|
||||
private final Lock schedulerLock = new ReentrantLock(true);
|
||||
private final Runnable monitorTask = () -> runBaseSchedule();
|
||||
|
||||
protected BaseScheduler(MonitoredLockType lockType) {
|
||||
protected BaseScheduler() {
|
||||
}
|
||||
|
||||
// NOTE: practice EXTREME caution when adding external locks to the scheduler system, if you don't know what you're doing DON'T USE THIS.
|
||||
protected BaseScheduler(MonitoredLockType lockType, List<MonitoredReentrantLock> extLocks) {
|
||||
protected BaseScheduler(List<Lock> extLocks) {
|
||||
externalLocks.addAll(extLocks);
|
||||
}
|
||||
|
||||
@@ -58,22 +56,12 @@ public abstract class BaseScheduler {
|
||||
}
|
||||
|
||||
private void lockScheduler() {
|
||||
if (!externalLocks.isEmpty()) {
|
||||
for (MonitoredReentrantLock l : externalLocks) {
|
||||
l.lock();
|
||||
}
|
||||
}
|
||||
|
||||
externalLocks.forEach(Lock::lock);
|
||||
schedulerLock.lock();
|
||||
}
|
||||
|
||||
private void unlockScheduler() {
|
||||
if (!externalLocks.isEmpty()) {
|
||||
for (MonitoredReentrantLock l : externalLocks) {
|
||||
l.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
externalLocks.forEach(Lock::unlock);
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package net.server.services.task.channel;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -53,10 +52,6 @@ public class EventService extends BaseService {
|
||||
|
||||
private class EventScheduler extends BaseScheduler {
|
||||
|
||||
public EventScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_EVENTS);
|
||||
}
|
||||
|
||||
public void registerDelayedAction(Runnable runAction, long delay) {
|
||||
registerEntry(runAction, runAction, delay);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package net.server.services.task.channel;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -65,8 +64,6 @@ public class MobAnimationService extends BaseService {
|
||||
private final Lock animationLock = new ReentrantLock(true);
|
||||
|
||||
public MobAnimationScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_MOBACTION);
|
||||
|
||||
super.addListener((toRemove, update) -> {
|
||||
animationLock.lock();
|
||||
try {
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package net.server.services.task.channel;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -53,10 +52,6 @@ public class MobClearSkillService extends BaseService {
|
||||
|
||||
private class MobClearSkillScheduler extends BaseScheduler {
|
||||
|
||||
public MobClearSkillScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_MOBSKILL);
|
||||
}
|
||||
|
||||
public void registerClearSkillAction(Runnable runAction, long delay) {
|
||||
registerEntry(runAction, runAction, delay);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package net.server.services.task.channel;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -53,10 +52,6 @@ public class MobMistService extends BaseService {
|
||||
|
||||
private class MobMistScheduler extends BaseScheduler {
|
||||
|
||||
public MobMistScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_MOBMIST);
|
||||
}
|
||||
|
||||
public void registerMistCancelAction(Runnable runAction, long delay) {
|
||||
registerEntry(runAction, runAction, delay);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ package net.server.services.task.channel;
|
||||
|
||||
import client.status.MonsterStatusEffect;
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -93,8 +92,6 @@ public class MobStatusService extends BaseService {
|
||||
}
|
||||
|
||||
public MobStatusScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_MOBSTATUS);
|
||||
|
||||
super.addListener((toRemove, update) -> {
|
||||
List<Runnable> toRun = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package net.server.services.task.channel;
|
||||
|
||||
import config.YamlConfig;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -58,10 +57,6 @@ public class OverallService extends BaseService { // thanks Alex for suggestin
|
||||
|
||||
public class OverallScheduler extends BaseScheduler {
|
||||
|
||||
public OverallScheduler() {
|
||||
super(MonitoredLockType.CHANNEL_OVERALL);
|
||||
}
|
||||
|
||||
public void registerDelayedAction(Runnable runAction, long delay) {
|
||||
registerEntry(runAction, runAction, delay);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
*/
|
||||
package net.server.services.task.world;
|
||||
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.services.BaseScheduler;
|
||||
import net.server.services.BaseService;
|
||||
|
||||
@@ -44,10 +43,6 @@ public class CharacterSaveService extends BaseService {
|
||||
|
||||
private class CharacterSaveScheduler extends BaseScheduler {
|
||||
|
||||
public CharacterSaveScheduler() {
|
||||
super(MonitoredLockType.WORLD_SAVECHARS);
|
||||
}
|
||||
|
||||
public void registerSaveCharacter(Integer characterId, Runnable runAction) {
|
||||
registerEntry(characterId, runAction, 0);
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
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 Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.task;
|
||||
|
||||
import net.server.audit.LockCollector;
|
||||
|
||||
/**
|
||||
* @author Ronan
|
||||
* @info Thread responsible for expiring locks signalized for dispose.
|
||||
*/
|
||||
public class ReleaseLockTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
LockCollector.getInstance().runLockCollector();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user