Reputation: 530
I am trying to start and manage some processes from my Flask web app. I want to be able to start a process and be able to terminate/kill it.
In my example, testscript.sh is just sleeping for 10 seconds.
My class processManager implements the Borg design pattern in an attempt to solve my problem, but it didn't change anything. (Beforehand, my processes, lock and thread variables were just global variables and processManager wasn't a class at all.)
The processes attribute is a list of all the launched processes.
addProcess starts a new process, adds it to processes. It looks if the polling thread is running and starts it if it is not.
The polling thread polls all the processes in the processes list and checks if their exit status have changed. If all the processes are finished, the thread stops.
stopProcess loops through processes to find the right one and terminate it.
class Borg:
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
# Pokes the processes to see their exit status. Stops when there is no running thread left.
class processManager(Borg):
class pollProcesses (threading.Thread):
def __init__(self, pManager):
threading.Thread.__init__(self)
self.pManager = pManager
def run(self):
while True:
#poll exit codes:
#None Running
#0 Finished nicely
#-n terminated by signal n
time.sleep(1)
pprint.pprint("-----------")
self.pManager.lock.acquire()
stop = True
for p in self.pManager.processes:
status = p.poll()
pprint.pprint(str(p.jobid)+" "+str(p.pid)+" "+str(status))
if status is None:
stop = False
#else log process status somewhere
self.pManager.lock.release()
if stop:
pprint.pprint("-----------")
pprint.pprint("No process running, stopping scan.")
break;
def __init__(self):
Borg.__init__(self)
pprint.pprint("New instance!")
if not hasattr(self, "processes"):
pprint.pprint("FIRST instance!")
self.processes = []
if not hasattr(self, "lock"):
self.lock = threading.Lock()
if not hasattr(self, "thread"):
self.thread = None
def addProcess(self, job):
pprint.pprint("-----------")
path = os.path.realpath(__file__)
pprint.pprint("Adding new process")
p = Popen(["/path/to/testscript.sh"], shell=True)
p.jobid = job['id']
# Lock the processes list before adding data
self.lock.acquire()
self.processes.append(p)
#If thread is finished, start it up
if self.thread is None or not self.thread.isAlive():
pprint.pprint("Starting thread")
self.thread = None
self.thread = self.pollProcesses(self)
self.thread.start()
pprint.pprint(self.thread)
self.lock.release()
return
def stopProcess(self, jobid):
pprint.pprint("STOP job"+str(jobid))
self.lock.acquire()
pprint.pprint(self.thread)
pprint.pprint("Jobs in processes:")
for p in self.processes:
pprint.pprint(p.jobid)
if p.jobid == jobid:
p.terminate()
self.lock.release()
return
In the flask app, I basically do something like this:
@plugin.route('/startjob', methods=['GET'])
def startJob():
if not hasattr(g, "pManager"):
g.pManager = processManager()
#SNIP - create/obtain job
g.pManager.addProcess(job)
return "OK"
And something pretty similar for stopping a job.
Now, if I launch a job and try to stop it from my Flask app, sometimes the processes list will create a brand new Borg/processManager (printing "FIRST instance!")
And from then on, everything becomes unpredictable:
My process statuses are still being updated even if from two threads and two different processes lists. It was not my goal but it works.
But if I wish to stop a process, the stopProcess function might be the "wrong one", since there are now several processManagers with different processes list, I have no way of knowing whether the stopProcess function called even has acces to the particular process I wish to stop.
I think that might be caused by mod_wsgi or apache, whose multithreading/multiprocessing mechanisms cause my processManager to be run in entirely different contexts.
I would like the processManager's process list, lock and thread to be the same, no matter what mod_wsgi process is accessing it.
I am still fairly new to python so my problem might be something else entirely. Any help is appreciated.
Please keep in mind this is code is simplified, it still reproduces the problem but it really doesn't do much.
Upvotes: 0
Views: 645
Reputation: 58523
In general it it not a good idea to create sub processes from a web application as it can cause various issues depending on the web server being used. Issues can arise because of use of multiprocess web server configuration, inheritance of odd signal masks from parent etc.
if you are wanting to create jobs to perform certain tasks, you would likely be better off with a purpose built task queuing system such as Celery, Redis Queue (RQ), gearman or similar.
Upvotes: 2