initial sketch for process manager
This commit is contained in:
parent
622da2d238
commit
0433ead782
2 changed files with 287 additions and 2 deletions
|
|
@ -43,6 +43,14 @@ class ProcessController {
|
||||||
this.outputScanner.useDelimieter("\\A");
|
this.outputScanner.useDelimieter("\\A");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getPid()
|
||||||
|
* returns translated pid of this process
|
||||||
|
*/
|
||||||
|
public int getPid() {
|
||||||
|
return this.pid;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getStatus()
|
* getStatus()
|
||||||
* returns whether or not the process is running
|
* returns whether or not the process is running
|
||||||
|
|
@ -77,8 +85,7 @@ class ProcessController {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* getOutput()
|
* getOutput()
|
||||||
* gets new output from stream
|
* gets output from process
|
||||||
* (TODO: investigate whether this would better be done by )
|
|
||||||
*/
|
*/
|
||||||
public String getOutput() {
|
public String getOutput() {
|
||||||
String out = "";
|
String out = "";
|
||||||
|
|
|
||||||
278
src/main/java/JobServ/ProcessManager.java
Normal file
278
src/main/java/JobServ/ProcessManager.java
Normal file
|
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
* ProcessManager
|
||||||
|
*
|
||||||
|
* v1.0
|
||||||
|
*
|
||||||
|
* May 22, 2019
|
||||||
|
*/
|
||||||
|
|
||||||
|
package JobServ;
|
||||||
|
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Holds a list of ProcessControllers and controls access to them via mutex
|
||||||
|
* Additionally, starts and manages a background thread that clears finished processes from the arraylist
|
||||||
|
*/
|
||||||
|
class ProcessManager {
|
||||||
|
// TODO: LOCK_TIMEOUT should be defined in a configuration management system
|
||||||
|
private final int LOCK_TIMEOUT = 5; // seconds
|
||||||
|
private ArrayList<ProcessController> processQueue;
|
||||||
|
private Boolean processQueueMutex = false;
|
||||||
|
private Thread backgroundProcessCleaner;
|
||||||
|
private ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
private Callable<void> getLockCallable = new Callable<void>() {
|
||||||
|
public void Object call() {
|
||||||
|
while(processQueueMutex){
|
||||||
|
continue; // spin!
|
||||||
|
}
|
||||||
|
|
||||||
|
processQueueMutex = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constructor
|
||||||
|
* initializes process queue and start the background process checking daemon
|
||||||
|
*/
|
||||||
|
public ProcessManager() {
|
||||||
|
processQueue = new ArrayList<ProcessController>();
|
||||||
|
// TODO: In a long running server over a large period of time
|
||||||
|
// It is possible that the streams used to redirect IO in the
|
||||||
|
// Processes may become a significant use of resources.
|
||||||
|
// In this case a background thread should be called to periodically
|
||||||
|
// remove dead ProcessControllers after calling kill() on them.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* newProcess()
|
||||||
|
* Takes a command and returns the translated pid of a new process
|
||||||
|
* Returns -1 if getLock fails
|
||||||
|
*/
|
||||||
|
public int newProcess(String command) {
|
||||||
|
/*
|
||||||
|
* TRADEOFF: Could initialize new ProcessController out here
|
||||||
|
* Pro: would minimize time spent in critical section
|
||||||
|
* Con: what if initialization goes through but we dont get the lock
|
||||||
|
* we would essentially have a dangling untrackable process
|
||||||
|
* which likely changed system state before it was killed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Enter critical section
|
||||||
|
try {
|
||||||
|
this.getLock();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// (lock was not grabbed)
|
||||||
|
System.err.println("Timeout starting new job '%s': " + e.getMessage, command);
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessController newProc = ProcessController(command);
|
||||||
|
this.processQueue.add(newProc);
|
||||||
|
|
||||||
|
// Exit critical section
|
||||||
|
this.releaseLock();
|
||||||
|
|
||||||
|
return newProc.getPid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getProcessStatus()
|
||||||
|
* returns whether or not a process is running.
|
||||||
|
* 0: running
|
||||||
|
* 1: not running
|
||||||
|
* 2: doesnt exist
|
||||||
|
* 3: couldnt grab lock
|
||||||
|
*/
|
||||||
|
public int getProcessStatus(int pid) {
|
||||||
|
// Enter critical section
|
||||||
|
try {
|
||||||
|
this.getLock();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
// lock could not be grabbed before timeout
|
||||||
|
System.err.println("Timeout getting process status for %s: " + e.getMessage(),
|
||||||
|
Integer.toString(pid));
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProcessController iter : this.processQueue) {
|
||||||
|
if (iter.getPid() == pid) {
|
||||||
|
this.releaseLock();
|
||||||
|
// release lock on finding process
|
||||||
|
return iter.getStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process must not exist
|
||||||
|
this.releaseLock();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getProcessReturn()
|
||||||
|
* returns a code 0-255, or 256 if process still running
|
||||||
|
* additionally, returns 257 if lock not grabbable AND
|
||||||
|
* a 258 if process doesnt exist.
|
||||||
|
*/
|
||||||
|
public int getProcessReturn(int pid) {
|
||||||
|
// Enter Critical section
|
||||||
|
try {
|
||||||
|
this.getLock();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
System.err.println("Timeout getting process return for %s: " + e.getMessage(),
|
||||||
|
Integer.toString(pid));
|
||||||
|
return 257;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProcessController iter : this.processQueue) {
|
||||||
|
if (iter.getPid() == pid) {
|
||||||
|
this.releaseLock();
|
||||||
|
return iter.getReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.releaseLock();
|
||||||
|
return 258;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getProcessOutput()
|
||||||
|
* returns output of process 'pid'
|
||||||
|
* or returns description of error
|
||||||
|
*/
|
||||||
|
public String getProcessOutput(int pid) {
|
||||||
|
try {
|
||||||
|
this.getLock();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
System.err.println("Timeout getting process output for %s: " + e.getMessage(),
|
||||||
|
Integer.toString());
|
||||||
|
return "[-] ERROR: Timeout grabbing lock to access process information";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProcessController iter : this.processQueue) {
|
||||||
|
if (iter.getPid() == pid) {
|
||||||
|
output = iter.getOutput();
|
||||||
|
this.releaseLock();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.releaseLock();
|
||||||
|
return "[-] ERROR: Process not found"
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* killProcess()
|
||||||
|
* returns false if couldnt grab lock
|
||||||
|
* ALSO RETURNS TRUE IF PROCESS DOESNT EXIST
|
||||||
|
*/
|
||||||
|
public Boolean killProcess(int pid) {
|
||||||
|
try {
|
||||||
|
this.getLock();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
System.err.println("Timeout killing process: " + e.getMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProcessController iter : this.processQueue) {
|
||||||
|
if (iter.getPid() == pid) {
|
||||||
|
iter.kill();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.releaseLock();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cleanProcessQueue()
|
||||||
|
* represents a background thread that sits and cleans finished processes
|
||||||
|
*/
|
||||||
|
private void cleanProcessQueue() {
|
||||||
|
while(true){
|
||||||
|
try {
|
||||||
|
this.getLock();
|
||||||
|
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ProcessController iter : this.processQueue) {
|
||||||
|
if(!iter.getStatus()) {
|
||||||
|
iter.kill();
|
||||||
|
this.processQueue.remove(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.releaseLock();
|
||||||
|
Thread.sleep(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* getLock()
|
||||||
|
* Locks access to this.processQueue
|
||||||
|
* Waits for a predefined timeout period and then grabs the mutex
|
||||||
|
* Throws TimeoutException when it fails to get the lock.
|
||||||
|
*/
|
||||||
|
private synchronized void getLock() throws TimeoutException {
|
||||||
|
try {
|
||||||
|
Future<void> future = executor.submit(task);
|
||||||
|
void result = future.get(this.LOCK_TIMEOUT, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.err.println("[!] ERROR: " + e.getMessage());
|
||||||
|
throw new TimeoutException();
|
||||||
|
// rethrowing a timeout exception tells the calling process that they dont have the lock
|
||||||
|
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
System.err.println("[!] ERROR: " + e.getMessage());
|
||||||
|
throw new TimeoutException();
|
||||||
|
|
||||||
|
// cancel the attempt to grab the lock
|
||||||
|
} finally {
|
||||||
|
future.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: touch of tech debt here
|
||||||
|
* There should honestly be an
|
||||||
|
* operation retry queue for ops
|
||||||
|
* That dont get the lock in time.
|
||||||
|
*
|
||||||
|
* This would require a scheduler
|
||||||
|
* that manages a queue of callbacks
|
||||||
|
* This scheduler would also likely
|
||||||
|
* mediate access to the ProcessManager
|
||||||
|
* object for fresh calls as well.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* releaseLock()
|
||||||
|
* releases mutex so other threads can operate on processqueue
|
||||||
|
*/
|
||||||
|
private void releaseLock() {
|
||||||
|
this.processQueueMutex = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* shutdown()
|
||||||
|
* called (eventually) by the grpc shutdown hook
|
||||||
|
* (AKA when user hits control c in the shell)
|
||||||
|
* releases resources held in the processController objects
|
||||||
|
*/
|
||||||
|
private void shutdown() {
|
||||||
|
this.getLock();
|
||||||
|
for (ProcessController p : this.processQueue) {
|
||||||
|
p.kill(); // exit threads, release IO streams, etc.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue