# # # patch "www/viewmtn/mtn.py" # from [172745ec4da85714e3257a1af2d2316803ab78dc] # to [f0b9b7a61b48af7aadf635ced4df571892711c9f] # # patch "www/viewmtn/viewmtn.py" # from [4e0b57e1e24da5a5c6f33a93d250d4670b62150f] # to [243495ac0b352cb6c0d52b3b4e84461ad7772df8] # ============================================================ --- www/viewmtn/mtn.py 172745ec4da85714e3257a1af2d2316803ab78dc +++ www/viewmtn/mtn.py f0b9b7a61b48af7aadf635ced4df571892711c9f @@ -14,6 +14,7 @@ import threading import pipes import select import threading +import time import popen2 from common import set_nonblocking, terminate_popen3 from traceback import format_exc @@ -24,6 +25,8 @@ from web import debug import web from web import debug +import sys + # regular expressions that are of general use when # validating monotone output def group_compile(r): @@ -75,18 +78,32 @@ class Automate(object): pipes.quote(host)] self.lock = threading.Lock() self.process = None + self.last_used = None def stop(self): if not self.process: return + #print >>sys.stderr, "Killing process: " + " ".join(self.command) + "\n" terminate_popen3(self.process) self.process = None + self.last_used = None + + def prune_if_stale(self): + lock = self.lock + if lock.acquire(False): + if self.process != None: + if time.time() - self.last_used > 60: + #print >>sys.stderr, "Pruning...\n" + self.stop() + lock.release() def __process_required(self): '''returns whether it started a process''' if self.process != None: return False + #print >>sys.stderr, "Starting process: " + " ".join(self.command) + "\n" self.process = popen2.Popen3(self.command, capturestderr=True) + self.last_used = time.time() map (set_nonblocking, [ self.process.fromchild, self.process.tochild, self.process.childerr ]) @@ -237,6 +254,7 @@ class Automate(object): yield data_buf if code > 0: raise MonotoneException("error code %d in automate packet." % (code)) + self.last_used = time.time() class MtnObject(object): def __init__(self, obj_type): @@ -360,6 +378,9 @@ class Operations(object): class Operations(object): def __init__(self, runner_args): self.automate = apply(Automate, runner_args) + + def prune_if_stale(self): + self.automate.prune_if_stale() def per_request(self): """Call this method every distinct request, to allow Operations to do any ============================================================ --- www/viewmtn/viewmtn.py 4e0b57e1e24da5a5c6f33a93d250d4670b62150f +++ www/viewmtn/viewmtn.py 243495ac0b352cb6c0d52b3b4e84461ad7772df8 @@ -14,6 +14,7 @@ import mtn, handlers, links, branchdiv from urlparse import urljoin import web import mtn, handlers, links, branchdiv +import threading from urls import common_urls, perdb_urls, nodefault_urls from render import Renderer import config @@ -171,15 +172,25 @@ def assemble_urls(): # import pprint # pp = pprint.PrettyPrinter() # pp.pprint(urls) - return urls, fvars + return urls, fvars, factory.dbstore["ops"] +def pruner(ops): + for name, op in ops.items(): + op.prune_if_stale() + pruner_thread = threading.Timer(30, pruner, [ops]) + pruner_thread.start() + if __name__ == '__main__': if hasattr(config, "running_under_apache2") and config.running_under_apache2: web.wsgi.runwsgi = runfcgi_apache if hasattr(config, "debug") and config.debug: web.webapi.internalerror = web.debugerror - urls, fvars = assemble_urls() + urls, fvars, ops = assemble_urls() + pruner_thread = threading.Timer(30, pruner, [ops]) + pruner_thread.start() web.run(urls, fvars) + print >>sys.stderr, "Done, killing pruner." + pruner_thread.cancel() ### ### vi:expandtab:sw=4:ts=4