Remove thread tracker system
This commit is contained in:
@@ -158,9 +158,6 @@ worlds:
|
|||||||
|
|
||||||
|
|
||||||
server:
|
server:
|
||||||
#Thread Tracker Configuration
|
|
||||||
USE_THREAD_TRACKER: false #[SEVERE] 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 debugging purposes.
|
|
||||||
|
|
||||||
#Database Configuration
|
#Database Configuration
|
||||||
DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic"
|
DB_URL_FORMAT: "jdbc:mysql://%s:3306/cosmic"
|
||||||
DB_HOST: "localhost"
|
DB_HOST: "localhost"
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ServerConfig {
|
public class ServerConfig {
|
||||||
//Thread Tracker Configuration
|
|
||||||
public boolean USE_THREAD_TRACKER;
|
|
||||||
|
|
||||||
//Database Configuration
|
//Database Configuration
|
||||||
public String DB_URL_FORMAT;
|
public String DB_URL_FORMAT;
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import constants.net.OpcodeConstants;
|
|||||||
import constants.net.ServerConstants;
|
import constants.net.ServerConstants;
|
||||||
import net.netty.LoginServer;
|
import net.netty.LoginServer;
|
||||||
import net.packet.Packet;
|
import net.packet.Packet;
|
||||||
import net.server.audit.ThreadTracker;
|
|
||||||
import net.server.channel.Channel;
|
import net.server.channel.Channel;
|
||||||
import net.server.coordinator.session.IpAddresses;
|
import net.server.coordinator.session.IpAddresses;
|
||||||
import net.server.coordinator.session.SessionCoordinator;
|
import net.server.coordinator.session.SessionCoordinator;
|
||||||
@@ -869,10 +868,6 @@ public class Server {
|
|||||||
ThreadManager.getInstance().start();
|
ThreadManager.getInstance().start();
|
||||||
initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan
|
initializeTimelyTasks(); // aggregated method for timely tasks thanks to lxconan
|
||||||
|
|
||||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
|
||||||
ThreadTracker.getInstance().registerThreadTrackerTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
||||||
|
|
||||||
@@ -945,7 +940,6 @@ public class Server {
|
|||||||
|
|
||||||
long timeLeft = getTimeLeftForNextHour();
|
long timeLeft = getTimeLeftForNextHour();
|
||||||
tMan.register(new CharacterDiseaseTask(), YamlConfig.config.server.UPDATE_INTERVAL, YamlConfig.config.server.UPDATE_INTERVAL);
|
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 CouponTask(), YamlConfig.config.server.COUPON_INTERVAL, timeLeft);
|
||||||
tMan.register(new RankingCommandTask(), MINUTES.toMillis(5), MINUTES.toMillis(5));
|
tMan.register(new RankingCommandTask(), MINUTES.toMillis(5), MINUTES.toMillis(5));
|
||||||
tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft);
|
tMan.register(new RankingLoginTask(), YamlConfig.config.server.RANKING_INTERVAL, timeLeft);
|
||||||
@@ -1908,10 +1902,6 @@ public class Server {
|
|||||||
|
|
||||||
List<Channel> allChannels = getAllChannels();
|
List<Channel> allChannels = getAllChannels();
|
||||||
|
|
||||||
if (YamlConfig.config.server.USE_THREAD_TRACKER) {
|
|
||||||
ThreadTracker.getInstance().cancelThreadTrackerTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Channel ch : allChannels) {
|
for (Channel ch : allChannels) {
|
||||||
while (!ch.finishedShutdown()) {
|
while (!ch.finishedShutdown()) {
|
||||||
try {
|
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 config.YamlConfig;
|
||||||
import net.server.Server;
|
import net.server.Server;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.audit.locks.MonitoredReentrantLock;
|
|
||||||
import server.TimerManager;
|
import server.TimerManager;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
@@ -38,18 +36,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
public abstract class BaseScheduler {
|
public abstract class BaseScheduler {
|
||||||
private int idleProcs = 0;
|
private int idleProcs = 0;
|
||||||
private final List<SchedulerListener> listeners = new LinkedList<>();
|
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 final Map<Object, Pair<Runnable, Long>> registeredEntries = new HashMap<>();
|
||||||
|
|
||||||
private ScheduledFuture<?> schedulerTask = null;
|
private ScheduledFuture<?> schedulerTask = null;
|
||||||
private final Lock schedulerLock = new ReentrantLock(true);
|
private final Lock schedulerLock = new ReentrantLock(true);
|
||||||
private final Runnable monitorTask = () -> runBaseSchedule();
|
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.
|
// 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);
|
externalLocks.addAll(extLocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,22 +56,12 @@ public abstract class BaseScheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void lockScheduler() {
|
private void lockScheduler() {
|
||||||
if (!externalLocks.isEmpty()) {
|
externalLocks.forEach(Lock::lock);
|
||||||
for (MonitoredReentrantLock l : externalLocks) {
|
|
||||||
l.lock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
schedulerLock.lock();
|
schedulerLock.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unlockScheduler() {
|
private void unlockScheduler() {
|
||||||
if (!externalLocks.isEmpty()) {
|
externalLocks.forEach(Lock::unlock);
|
||||||
for (MonitoredReentrantLock l : externalLocks) {
|
|
||||||
l.unlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
schedulerLock.unlock();
|
schedulerLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package net.server.services.task.channel;
|
package net.server.services.task.channel;
|
||||||
|
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -53,10 +52,6 @@ public class EventService extends BaseService {
|
|||||||
|
|
||||||
private class EventScheduler extends BaseScheduler {
|
private class EventScheduler extends BaseScheduler {
|
||||||
|
|
||||||
public EventScheduler() {
|
|
||||||
super(MonitoredLockType.CHANNEL_EVENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerDelayedAction(Runnable runAction, long delay) {
|
public void registerDelayedAction(Runnable runAction, long delay) {
|
||||||
registerEntry(runAction, runAction, delay);
|
registerEntry(runAction, runAction, delay);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package net.server.services.task.channel;
|
package net.server.services.task.channel;
|
||||||
|
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -65,8 +64,6 @@ public class MobAnimationService extends BaseService {
|
|||||||
private final Lock animationLock = new ReentrantLock(true);
|
private final Lock animationLock = new ReentrantLock(true);
|
||||||
|
|
||||||
public MobAnimationScheduler() {
|
public MobAnimationScheduler() {
|
||||||
super(MonitoredLockType.CHANNEL_MOBACTION);
|
|
||||||
|
|
||||||
super.addListener((toRemove, update) -> {
|
super.addListener((toRemove, update) -> {
|
||||||
animationLock.lock();
|
animationLock.lock();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package net.server.services.task.channel;
|
package net.server.services.task.channel;
|
||||||
|
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -53,10 +52,6 @@ public class MobClearSkillService extends BaseService {
|
|||||||
|
|
||||||
private class MobClearSkillScheduler extends BaseScheduler {
|
private class MobClearSkillScheduler extends BaseScheduler {
|
||||||
|
|
||||||
public MobClearSkillScheduler() {
|
|
||||||
super(MonitoredLockType.CHANNEL_MOBSKILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerClearSkillAction(Runnable runAction, long delay) {
|
public void registerClearSkillAction(Runnable runAction, long delay) {
|
||||||
registerEntry(runAction, runAction, delay);
|
registerEntry(runAction, runAction, delay);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package net.server.services.task.channel;
|
package net.server.services.task.channel;
|
||||||
|
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -53,10 +52,6 @@ public class MobMistService extends BaseService {
|
|||||||
|
|
||||||
private class MobMistScheduler extends BaseScheduler {
|
private class MobMistScheduler extends BaseScheduler {
|
||||||
|
|
||||||
public MobMistScheduler() {
|
|
||||||
super(MonitoredLockType.CHANNEL_MOBMIST);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerMistCancelAction(Runnable runAction, long delay) {
|
public void registerMistCancelAction(Runnable runAction, long delay) {
|
||||||
registerEntry(runAction, runAction, delay);
|
registerEntry(runAction, runAction, delay);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package net.server.services.task.channel;
|
|||||||
|
|
||||||
import client.status.MonsterStatusEffect;
|
import client.status.MonsterStatusEffect;
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -93,8 +92,6 @@ public class MobStatusService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MobStatusScheduler() {
|
public MobStatusScheduler() {
|
||||||
super(MonitoredLockType.CHANNEL_MOBSTATUS);
|
|
||||||
|
|
||||||
super.addListener((toRemove, update) -> {
|
super.addListener((toRemove, update) -> {
|
||||||
List<Runnable> toRun = new ArrayList<>();
|
List<Runnable> toRun = new ArrayList<>();
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
package net.server.services.task.channel;
|
package net.server.services.task.channel;
|
||||||
|
|
||||||
import config.YamlConfig;
|
import config.YamlConfig;
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -58,10 +57,6 @@ public class OverallService extends BaseService { // thanks Alex for suggestin
|
|||||||
|
|
||||||
public class OverallScheduler extends BaseScheduler {
|
public class OverallScheduler extends BaseScheduler {
|
||||||
|
|
||||||
public OverallScheduler() {
|
|
||||||
super(MonitoredLockType.CHANNEL_OVERALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerDelayedAction(Runnable runAction, long delay) {
|
public void registerDelayedAction(Runnable runAction, long delay) {
|
||||||
registerEntry(runAction, runAction, delay);
|
registerEntry(runAction, runAction, delay);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package net.server.services.task.world;
|
package net.server.services.task.world;
|
||||||
|
|
||||||
import net.server.audit.locks.MonitoredLockType;
|
|
||||||
import net.server.services.BaseScheduler;
|
import net.server.services.BaseScheduler;
|
||||||
import net.server.services.BaseService;
|
import net.server.services.BaseService;
|
||||||
|
|
||||||
@@ -44,10 +43,6 @@ public class CharacterSaveService extends BaseService {
|
|||||||
|
|
||||||
private class CharacterSaveScheduler extends BaseScheduler {
|
private class CharacterSaveScheduler extends BaseScheduler {
|
||||||
|
|
||||||
public CharacterSaveScheduler() {
|
|
||||||
super(MonitoredLockType.WORLD_SAVECHARS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerSaveCharacter(Integer characterId, Runnable runAction) {
|
public void registerSaveCharacter(Integer characterId, Runnable runAction) {
|
||||||
registerEntry(characterId, runAction, 0);
|
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