Initial Repository Push

This commit is contained in:
2022-04-30 09:41:17 -04:00
parent d4eef9097a
commit bbfdde57a3
835 changed files with 168392 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
import org.pmw.tinylog.Logger;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
/**
* Provides mandatory base implementation for all 'Job's'.
*
* @author
*/
public abstract class AbstractJob implements Runnable {
public enum JobRunStatus {
CREATED, RUNNING, FINISHED;
};
public enum JobCompletionStatus {
NOTCOMPLETEDYET, SUCCESS, SUCCESSWITHERRORS, FAIL;
};
/**
* Keep fields private. All access through getters n setters for Thread
* Safety.
*/
private JobRunStatus runStatus;
private JobCompletionStatus completeStatus;
private UUID uuid = null;
private final AtomicReference<String> workerID;
private static final String DEFAULT_WORKERID = "UNPROCESSED_JOB";
private long submitTime = 0L;
private long startTime = 0L;
private long stopTime = 0L;
public AbstractJob() {
//Tests against DEFAULT_WORKERID to ensure single execution
this.workerID = new AtomicReference<>(DEFAULT_WORKERID);
this.runStatus = JobRunStatus.RUNNING;
this.completeStatus = JobCompletionStatus.NOTCOMPLETEDYET;
this.uuid = UUID.randomUUID();
}
@Override
public void run() {
if(workerID.get().equals(DEFAULT_WORKERID)) {
Logger.warn("FIX ME! Job ran through 'run()' directly. Use executeJob(String) instead.");
}
this.markStartRunTime();
this.setRunStatus(JobRunStatus.RUNNING);
try {
this.doJob();
} catch (Exception e) {
Logger.error(e);
}
this.setRunStatus(JobRunStatus.FINISHED);
this.markStopRunTime();
}
protected abstract void doJob();
public void executeJob(String threadName) {
this.workerID.set(threadName);
this.run();
}
protected void setRunStatus(JobRunStatus status) {
synchronized (this.runStatus) {
this.runStatus = status;
}
}
public JobRunStatus getRunStatus() {
synchronized (this.runStatus) {
return runStatus;
}
}
protected void setCompletionStatus(JobCompletionStatus status) {
synchronized (this.completeStatus) {
this.completeStatus = status;
}
}
public JobCompletionStatus getCompletionStatus() {
synchronized (this.completeStatus) {
return completeStatus;
}
}
protected void setJobId(UUID id) {
synchronized (this.uuid) {
this.uuid = id;
}
}
public UUID getJobId() {
synchronized (this.uuid) {
return uuid;
}
}
public String getWorkerID() {
return workerID.get();
}
/*
* Time markers
*/
protected void markSubmitTime() {
this.submitTime = System.currentTimeMillis()-2;
}
protected void markStartRunTime() {
this.startTime = System.currentTimeMillis()-1;
}
protected void markStopRunTime() {
this.stopTime = System.currentTimeMillis();
}
public final long getSubmitTime() {
return submitTime;
}
public final long getStartTime() {
return startTime;
}
public final long getStopTime() {
return stopTime;
}
}
+118
View File
@@ -0,0 +1,118 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
public abstract class AbstractJobStatistics {
private String objectName;
private long totalServiceTime = 0L;
private long totalQueueTime = 0L;
private long executions = 0L;
private long maxServiceTime = 0L;
private long minServiceTime = 0L;
private long minQueueTime = 0L;
private long maxQueueTime = 0L;
public AbstractJobStatistics() {
this.objectName = "Unknown";
}
public AbstractJobStatistics(String objectName) {
this.objectName = objectName;
}
public void setObjectName (String objectName) {
this.objectName = objectName;
}
public String getObjectName () {
return this.objectName;
}
public long getExecutions() {
return this.executions;
}
public long getTotalServiceTime() {
return this.totalServiceTime;
}
public long getTotalQueueTime() {
return this.totalQueueTime;
}
public long getAvgQueueTime() {
if (this.executions > 0L && this.totalQueueTime > 0L)
return this.totalQueueTime / this.executions;
else
return 0L;
}
public long getAvgServiceTime() {
if (this.executions > 0L && this.totalServiceTime > 0L)
return this.totalServiceTime / this.executions;
else
return 0L;
}
public long getMinServiceTime() {
return this.minServiceTime;
}
public long getMinQueueTime() {
return this.minQueueTime;
}
public long getMaxServiceTime() {
return this.maxServiceTime;
}
public long getMaxQueueTime() {
return this.maxQueueTime;
}
public void incrExecutions() {
this.executions++;
}
public void addServiceTime(long svcTime) {
this.totalServiceTime += svcTime;
this.incrExecutions();
if (svcTime > this.maxServiceTime)
this.maxServiceTime = svcTime;
if (svcTime < this.minServiceTime || this.minServiceTime == 0L)
this.minServiceTime = svcTime;
}
public void addQueueTime(long queueTime) {
this.totalQueueTime += queueTime;
this.incrExecutions();
if (queueTime > this.maxQueueTime)
this.maxQueueTime = queueTime;
if (queueTime < this.minQueueTime || this.minQueueTime == 0L)
this.minQueueTime = queueTime;
}
public String asString() {
return this.objectName + " execs=" + this.executions + " avg_svc_ms=" + this.getAvgServiceTime() +
" min_svc_ms=" + this.minServiceTime + " max_svc_ms=" + this.maxServiceTime +
" avg_q_ms=" + this.getAvgQueueTime() + " min_q_ms=" + this.minQueueTime +
" max_q_ms=" + this.maxQueueTime;
}
public String asChatMsg() {
return this.objectName + "=>" + this.executions + ',' + this.getAvgServiceTime() + '/' + this.minServiceTime +
'/' + this.maxServiceTime + " "+ this.getAvgQueueTime() + '/' +
this.minQueueTime + '/' + this.maxQueueTime + '\n';
}
}
+28
View File
@@ -0,0 +1,28 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
public abstract class AbstractScheduleJob extends AbstractJob {
public AbstractScheduleJob() {
super();
}
@Override
protected abstract void doJob();
public void cancelJob() {
JobScheduler.getInstance().cancelScheduledJob(this);
_cancelJob();
}
protected abstract void _cancelJob();
}
+19
View File
@@ -0,0 +1,19 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
public class ClassJobStatistics extends AbstractJobStatistics {
public ClassJobStatistics (String className) {
super(className);
}
}
+84
View File
@@ -0,0 +1,84 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
public class JobContainer implements Comparable<JobContainer> {
final AbstractJob job;
final long timeOfExecution;
final boolean noTimer;
JobContainer(AbstractJob job, long timeOfExecution) {
if (job == null) {
throw new IllegalArgumentException("No 'null' jobs allowed.");
}
this.job = job;
this.timeOfExecution = timeOfExecution;
this.noTimer = false;
}
public JobContainer(AbstractJob job) {
if (job == null) {
throw new IllegalArgumentException("No 'null' jobs allowed.");
}
this.job = job;
this.timeOfExecution = Long.MAX_VALUE;
this.noTimer = true;
}
public AbstractJob getJob() {
return job;
}
public boolean noTimer() {
return noTimer;
}
public long timeOfExection() {
return this.timeOfExecution;
}
public int timeToExecutionLeft() {
if (JobScheduler.getInstance().isAlive()) {
int timeLeft = (int) (timeOfExecution - System.currentTimeMillis());
if (timeLeft < 0)
timeLeft = 0;
return timeLeft;
} else
return (int) (timeOfExecution - JobScheduler.getInstance().getTimeOfKill());
}
@Override
public int compareTo(JobContainer compared) {
if (timeOfExecution < compared.timeOfExecution) {
return -1;
}
if (timeOfExecution > compared.timeOfExecution) {
return 1;
}
return 0;
}
@Override
public boolean equals(Object obj) {
return job.equals(((JobContainer) obj).job);
}
@Override
public int hashCode() {
return job.hashCode();
}
public void cancelJob() {
if (job != null && job instanceof AbstractScheduleJob)
((AbstractScheduleJob)job).cancelJob();
}
}
+232
View File
@@ -0,0 +1,232 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
import engine.core.ControlledRunnable;
import engine.server.MBServerStatics;
import engine.util.ThreadUtils;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
/**
* Note to DEVs. When attempting to log something in this class, use logDIRECT
* only.
*/
public class JobManager extends ControlledRunnable {
/*
* Singleton implementation.
*/
private static volatile JobManager INSTANCE;
public static JobManager getInstance() {
if (JobManager.INSTANCE == null) {
synchronized (JobManager.class) {
if (JobManager.INSTANCE == null) {
JobManager.INSTANCE = new JobManager();
JobManager.INSTANCE.startup();
}
}
}
return JobManager.INSTANCE;
}
/*
* Class implementation
*/
private final ArrayList<JobPool> jobPoolList = new ArrayList<>();
private final ConcurrentHashMap<String, JobPool> jobQueueMapping= new ConcurrentHashMap<>(MBServerStatics.CHM_INIT_CAP, MBServerStatics.CHM_LOAD, MBServerStatics.CHM_THREAD_HIGH);
private boolean shutdownNowFlag = false;
private JobManager() {
super("JobManager");
// create the initial job pools with the correct sizes
// based on the number of array elements in the initial_jo_workers
// definition in Statisc
if (MBServerStatics.INITIAL_JOBPOOL_WORKERS != null && MBServerStatics.INITIAL_JOBPOOL_WORKERS.length >0 ) {
for (int i=0; i<MBServerStatics.INITIAL_JOBPOOL_WORKERS.length; i++) {
JobPool jp = new JobPool(i,MBServerStatics.INITIAL_JOBPOOL_WORKERS[i]);
this.jobPoolList.add(jp);
Logger.info( "Adding JobPool_" + jp.getJobPoolID() + " with " + MBServerStatics.INITIAL_JOBPOOL_WORKERS[i] + " workers");
}
} else {
// not defined or empty in statics so just create 1 JobPool with 25 workers
System.out.println("Creating 1 pool called 0");
JobPool jp = new JobPool(0,25);
this.jobPoolList.add(jp);
Logger.info( "Adding JobPool_" + jp.getJobPoolID() + " with 25 workers");
}
JobScheduler.getInstance();
// if you want any jobs to default onto a given queue put it here
// otherwise everything goes on the P1 queue by default
}
/**
* Submit a job to be processed by the JobManager. There is no guarantee
* that the job will be executed immediately.
*
* @param aj
* AbstractJob to be submitted.
* @return boolean value indicating whether the job was submitted or not.
*/
public boolean submitJob(AbstractJob aj) {
if (jobQueueMapping.containsKey(aj.getClass().getSimpleName())) {
return this.jobQueueMapping.get(aj.getClass().getSimpleName()).submitJob(aj);
} else {
return this.jobPoolList.get(0).submitJob(aj);
}
}
private void auditAllWorkers() {
for (JobPool jp : this.jobPoolList) {
jp.auditWorkers();
}
}
@Override
protected boolean _Run() {
// This thread is to be used to JM internal monitoring.
// Startup workers:
this.auditAllWorkers();
ThreadUtils.sleep(MBServerStatics.JOBMANAGER_INTERNAL_MONITORING_INTERVAL_MS * 2);
this.runStatus = true;
// Monitoring loop
while (this.runCmd) {
this.auditAllWorkers();
ThreadUtils.sleep(MBServerStatics.JOBMANAGER_INTERNAL_MONITORING_INTERVAL_MS);
}
// No new submissions
for (JobPool jp : this.jobPoolList){
jp.setBlockNewSubmissions(true);
}
if (this.shutdownNowFlag == false) {
// shutdown each pool
for (JobPool jp : this.jobPoolList) {
jp.shutdown();
}
} else {
// Emergency Stop each pool
for (JobPool jp : this.jobPoolList) {
jp.emergencyStop();
}
}
this.runStatus = false;
Logger.info("JM Shutdown");
return true;
}
@Override
protected boolean _postRun() {
return true;
}
@Override
protected boolean _preRun() {
return true;
}
@Override
protected void _shutdown() {
Logger.info("JM Shutdown Requested");
JobScheduler.getInstance().shutdown();
}
@Override
protected void _startup() {
Logger.info("JM Starting up... ");
}
public void shutdownNow() {
this.shutdownNowFlag = true;
this.shutdown();
}
public String moveJob(String simpleClassName, String jobPoolID) {
// moves a job to a different queue
try {
// parse string into an int
Integer i = Integer.parseInt(jobPoolID);
for (JobPool jp : this.jobPoolList) {
if (jp.getJobPoolID() == i) {
// move the job to the new queue
this.jobQueueMapping.put(simpleClassName, jp);
return simpleClassName + " moved to JobPool ID " + jp.getJobPoolID();
}
}
} catch (NumberFormatException e) {
return "jobPoolID is not a valid number";
}
//Verify jobpool ID
return "Invalid parameters <simpleClassName - case sensitive> <jobPoolID> required";
}
public String resetJobs() {
// moves all jobs to the P1 queue
this.jobQueueMapping.clear();
return "All Jobs reset onto P1 queue";
}
public String showJobs() {
String out = "";
Iterator<String> jmi = this.jobQueueMapping.keySet().iterator();
while (jmi.hasNext()) {
String jmiKey = jmi.next();
out += jmiKey + ' ' + this.jobQueueMapping.get(jmiKey).getJobPoolID() + '\n';
}
return out;
}
public ArrayList<JobPool> getJobPoolList() {
return this.jobPoolList;
}
public String modifyJobPoolWorkers(String jobPoolID, String maxWorkers) {
try {
// parse string into an int
Integer jid = Integer.parseInt(jobPoolID);
Integer mw = Integer.parseInt(maxWorkers);
for (JobPool jp : this.jobPoolList) {
if (jp.getJobPoolID() == jid) {
// change the number of workers
return jp.setMaxWorkers(mw);
}
}
} catch (NumberFormatException e) {
return "Invalid parameters <jobPoolID> <maxWorkers> required";
}
return "Invalid parameters <jobPoolID> <maxWorkers> required";
}
}
+295
View File
@@ -0,0 +1,295 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
import engine.jobs.AttackJob;
import engine.jobs.UsePowerJob;
import engine.net.CheckNetMsgFactoryJob;
import engine.net.ConnectionMonitorJob;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
public class JobPool {
int jobPoolID;
int maxWorkers;
int nextWorkerID;
private final LinkedBlockingQueue<AbstractJob> jobWaitQueue = new LinkedBlockingQueue<>();
private final LinkedBlockingQueue<JobWorker> jobWorkerQueue = new LinkedBlockingQueue<>();
private final ArrayList<JobWorker> jobWorkerList = new ArrayList<>();
private final LinkedBlockingQueue<AbstractJob> jobRunList = new LinkedBlockingQueue<>();
private boolean blockNewSubmissions = false;
public JobPool(int id, int workers) {
this.jobPoolID = id;
// default to 1 unless workers parameter is higher
int actualWorkers = 1;
if (workers > 1)
actualWorkers = workers;
this.maxWorkers = actualWorkers;
for (int i=0;i<actualWorkers; i++) {
this.startWorker(i);
}
this.nextWorkerID = this.maxWorkers;
}
private int getNextWorkerID () {
return this.nextWorkerID++;
}
public int getJobPoolID () {
return this.jobPoolID;
}
public void setBlockNewSubmissions(boolean blocked) {
this.blockNewSubmissions = blocked;
}
public boolean submitJob(AbstractJob aj) {
if (blockNewSubmissions) {
Logger.warn("A '" + aj.getClass().getSimpleName() + "' job was submitted, but submissions are currently blocked.");
return false;
}
aj.markSubmitTime();
jobWaitQueue.add(aj);
// keep notifying workers if the wait queue has items
// commented out as the price of polling the wait queue
// size while it is being updated out-weighs the gain
// for not just blindly waking all workers
// unless we have a stupidly large pool vs CPU threads
JobWorker jw = jobWorkerQueue.poll();
if(jw != null) {
synchronized (jw) {
jw.notify();
}
}
return true;
}
private void startWorker(int workerID) {
// check we dont already have a jobWorker with that ID
synchronized(this.jobWorkerList) {
for (JobWorker jwi : this.jobWorkerList) {
if (jwi.getWorkerId() == workerID) {
Logger.error("Attempt to create worker with ID " + workerID + " failed in JobPool " + jobPoolID + " as worker ID already exists");
return;
}
}
}
// ID is unique, create worker
JobWorker jw;
jw = new JobWorker(workerID, this.jobPoolID, this.jobWaitQueue, this.jobWorkerQueue);
synchronized(this.jobWorkerList) {
//Adds to the overall list..
jobWorkerList.add(jw);
}
//Returns to the free worker queue..
jw.startup();
}
private String getQueueByClassAsString(Queue<AbstractJob> q) {
HashMap<String, Integer> ch = new HashMap<>();
int cnt = 0;
// iterate through the linked queue and get every item
// putting classname and incrementing the value each time in the hashmap
Iterator<AbstractJob> wi = q.iterator();
while (cnt < q.size() && wi.hasNext()) {
AbstractJob aj = wi.next();
if (ch.containsKey(aj.getClass().getSimpleName())) {
int newValue = ch.get(aj.getClass().getSimpleName()) + 1;
ch.put(aj.getClass().getSimpleName(), newValue);
} else {
ch.put(aj.getClass().getSimpleName(), 1);
}
cnt++;
}
// iterate through the hashmap outputting the classname and number of jobs
Iterator<String> i = ch.keySet().iterator();
String out = "";
while(i.hasNext()) {
Object key = i.next();
out += "JobPoolID_" + this.jobPoolID + ' ' + key.toString() + "=>" + ch.get(key) + '\n';
}
if (out.isEmpty())
return "No Jobs on queue\n";
else
return out;
}
public void auditWorkers() {
if(!MBServerStatics.ENABLE_AUDIT_JOB_WORKERS) {
return;
}
ArrayList<AbstractJob> problemJobs = new ArrayList<>();
// Checked for stalled Workers
Iterator<JobWorker> it = jobWorkerList.iterator();
while (it.hasNext()) {
JobWorker jw = it.next();
AbstractJob curJob = jw.getCurrentJob();
if (curJob != null) { // Has a job
if (JobPool.isExemptJobFromAudit(curJob)) {
continue;
}
// Determine whether the job is being executed or waiting to
// start;
if (curJob.getStartTime() <= 0) {
// Waiting to start
long diff = System.currentTimeMillis() - curJob.getSubmitTime();
if (diff >= MBServerStatics.JOB_STALL_THRESHOLD_MS) {
Logger.warn("Job did not start within threshold. Stopping worker#" + jw.getWorkerId() + " JobData:"
+ curJob.toString());
jw.EmergencyStop();
problemJobs.add(jw.getCurrentJob());
it.remove();
} // end if (diff >=
} else if (curJob.getStopTime() <= 0L) {
// is executing it
long diff = System.currentTimeMillis() - curJob.getStartTime();
if (diff >= MBServerStatics.JOB_STALL_THRESHOLD_MS) {
Logger.warn("Job execution time exceeded threshold(" + diff + "). Stopping worker#" + jw.getWorkerId() + " JobData:"
+ curJob.toString());
jw.EmergencyStop();
problemJobs.add(jw.getCurrentJob());
it.remove();
} // end if (diff >=
} // end if(curJob.getStopTime()
} // end if(curJob != null)
} // end While
// Check Worker Count and add workers as necessary;
int workerCount = jobWorkerList.size();
int maxThreads = this.maxWorkers;
// no pool can go below a single thread
if (maxThreads < 1)
maxThreads = 1;
while (workerCount != maxThreads) {
Logger.info("Resizing JobPool " + this.jobPoolID + " from " + workerCount + " to " + maxThreads);
if (workerCount < maxThreads) {
this.startWorker(this.getNextWorkerID());
if (jobWorkerList.size() <= workerCount) {
// Something didnt work correctly
Logger.warn("auditWorkers() failed to add a new JobWorker to JobPool " + this.jobPoolID + ". Worker count " + workerCount + " Worker pool size " + jobWorkerList.size() + " Aborting Audit.");
return;
}
} else if (workerCount > maxThreads) {
synchronized(this.jobWorkerList) {
Logger.warn("Reducing workers in JobPool " + this.jobPoolID + " Worker Count: " + workerCount + " to Max threads: " + maxThreads);
// pick a worker off the list and shut it down
JobWorker toRemove = null;
int loopTries = 5;
do {
//Infinite loop could be bad..
toRemove = jobWorkerQueue.poll();
} while (toRemove == null && --loopTries >= 0);
//remove it from the list
toRemove.shutdown();
jobWorkerList.remove(toRemove);
}
}
// update value for next loop pass
workerCount = jobWorkerList.size();
}
}
private static boolean isExemptJobFromAudit(AbstractJob aj) {
// If the job is any of the following classes, exempt it from auditWorkers
if (aj instanceof ConnectionMonitorJob) {
return true;
} else
return aj instanceof CheckNetMsgFactoryJob || aj instanceof AttackJob || aj instanceof UsePowerJob;
}
public void shutdown() {
synchronized(this.jobWorkerList) {
for (JobWorker jw : this.jobWorkerList)
jw.shutdown();
}
}
public void emergencyStop() {
synchronized(this.jobWorkerList) {
for (JobWorker jw : this.jobWorkerList)
jw.EmergencyStop();
}
}
public String getRunningQueueByClassAsString() {
return this.getQueueByClassAsString(this.jobRunList);
}
public String getWaitQueueByClassAsString () {
return this.getQueueByClassAsString(this.jobWaitQueue);
}
// used by devcmds
public String setMaxWorkers (int maxWorkers) {
if (maxWorkers > 0 && maxWorkers < 101) {
this.maxWorkers = maxWorkers;
// audit workers reduces the cap
this.auditWorkers();
return "Max workers set to " + maxWorkers + " for JobPool_" + this.jobPoolID;
} else {
return "Max workers not set, value must be from 1-100";
}
}
}
+169
View File
@@ -0,0 +1,169 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
import engine.server.MBServerStatics;
import java.util.PriorityQueue;
public class JobScheduler {
private static final JobScheduler INSTANCE = new JobScheduler();
private final PriorityQueue<JobContainer> jobs;
private volatile boolean alive;
private long timeOfKill = -1;
public static JobScheduler getInstance() {
return INSTANCE;
}
private JobScheduler() {
jobs = new PriorityQueue<>(MBServerStatics.SCHEDULER_INITIAL_CAPACITY);
Runnable worker = new Runnable() {
@Override
public void run() {
execution();
}
};
alive = true;
Thread t = new Thread(worker, "JobScheduler");
t.start();
}
/**
* This function schedules a job to execute in <i>timeToExecution</i>
* milliseconds from now.
*
* @param job
* @param timeToExecution
* @return
*/
public JobContainer scheduleJob(AbstractJob job, int timeToExecution) {
long timeOfExecution = System.currentTimeMillis() + timeToExecution;
JobContainer container = new JobContainer(job, timeOfExecution);
synchronized (jobs) {
jobs.offer(container);
jobs.notify();
}
return container;
}
/**
* This function schedules a job to execute at the absolute time of
* <i>timeOfExecution</i> (milliseconds).
*
* @param job
* @param timeOfExecution
* @return
*/
public JobContainer scheduleJob(AbstractJob job, long timeOfExecution) {
JobContainer container = new JobContainer(job, timeOfExecution);
synchronized (jobs) {
jobs.offer(container);
jobs.notify();
}
return container;
}
public boolean cancelScheduledJob(JobContainer container) {
return cancelScheduledJob(container.getJob());
}
public boolean cancelScheduledJob(AbstractJob job) {
JobContainer container = new JobContainer(job, -1);
boolean success = false;
synchronized (jobs) {
success = jobs.remove(container);
jobs.notify();
}
return success;
}
/**
* Stops the jobScheduler
*/
public void shutdown() {
if (alive) {
alive = false;
timeOfKill = System.currentTimeMillis();
synchronized (jobs) {
jobs.notify();
}
}
}
public JobContainer pollNextJobContainer() {
if (alive) {
throw new IllegalStateException("Can't poll jobs from a live scheduler.");
}
synchronized (jobs) {
return jobs.poll();
}
}
public long getTimeOfKill() {
return this.timeOfKill;
}
public boolean isAlive() {
return this.alive;
}
private void execution() {
long duration;
JobContainer container;
int compensation = MBServerStatics.SCHEDULER_EXECUTION_TIME_COMPENSATION;
while (alive) {
synchronized (jobs) {
container = jobs.peek();
if (container == null) {
// queue is empty, wait until notified (which happens after
// a new job is offered)
try {
jobs.wait(0);
} catch (InterruptedException ie) {
// do nothing
}
} else {
duration = container.timeOfExecution - System.currentTimeMillis();
if (duration < compensation) {
jobs.poll();
} else {
// enforce new loop
container = null;
// sleep until the head job execution time
try {
jobs.wait(duration);
} catch (InterruptedException ie) {
// do nothing
}
}
}
}
if (container != null) {
JobManager.getInstance().submitJob(container.job);
}
}
}
}
+109
View File
@@ -0,0 +1,109 @@
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2022
// www.magicbane.com
package engine.job;
import engine.core.ControlledRunnable;
import org.pmw.tinylog.Logger;
import java.util.Queue;
public class JobWorker extends ControlledRunnable {
private final int workerId;
private final Queue<AbstractJob> jobWaitQueue;
private final Queue<JobWorker> jobWorkerList;
private AbstractJob currentJob;
public JobWorker(final int workerID, int priorityQueue,
Queue<AbstractJob> jobWaitQueue,
Queue<JobWorker> jobWorkerList) {
super("JobWorker_" + priorityQueue + '_' + workerID);
workerId = workerID;
this.jobWaitQueue = jobWaitQueue;
this.jobWorkerList = jobWorkerList;
}
@Override
protected boolean _Run() {
while (this.runCmd) {
// Access to Queue is synchronized internal to JobManager
this.currentJob = this.jobWaitQueue.poll();
if (this.currentJob == null) {
try {
// use self as MUTEX
synchronized (this) {
this.jobWorkerList.add(this);
this.wait();
}
} catch (InterruptedException e) {
Logger.error(this.getThreadName(), e.getClass()
.getSimpleName()
+ ": " + e.getMessage());
break;
}
} else {
// execute the new job..
this.currentJob.executeJob(this.getThreadName());
this.currentJob = null;
}
}
return true;
}
@Override
protected boolean _postRun() {
return true;
}
@Override
protected boolean _preRun() {
return true;
}
@Override
protected void _shutdown() {
}
@Override
protected void _startup() {
//this.logDirectINFO(this.getThreadName(), "Starting up...");
}
public final int getWorkerId() {
return workerId;
}
public final AbstractJob getCurrentJob() {
return currentJob;
}
public final boolean hasCurrentJob() {
return (currentJob != null);
}
protected void EmergencyStop() {
this.runCmd = false;
String out = "Stack Trace";
for(StackTraceElement e : this.getThisThread().getStackTrace()) {
out += " -> " + e.toString();
}
Logger.info(out);
this.getThisThread().interrupt();
}
}