python: Added an abort timeout for the freethread handler.
[ashd.git] / python3 / ashd / serve.py
index 8520411..74ccd92 100644 (file)
@@ -86,13 +86,40 @@ class handler(object):
         return {}
 
 class freethread(handler):
-    def __init__(self, **kw):
+    def __init__(self, *, max=None, timeout=None, **kw):
         super().__init__(**kw)
         self.current = set()
         self.lk = threading.Lock()
+        self.tcond = threading.Condition(self.lk)
+        self.max = max
+        self.timeout = timeout
+
+    @classmethod
+    def parseargs(cls, *, max=None, abort=None, **args):
+        ret = super().parseargs(**args)
+        if max:
+            ret["max"] = int(max)
+        if abort:
+            ret["timeout"] = int(abort)
+        return ret
 
     def handle(self, req):
-        reqthread(target=self.run, args=[req]).start()
+        with self.lk:
+            if self.max is not None:
+                if self.timeout is not None:
+                    now = start = time.time()
+                    while len(self.current) >= self.max:
+                        self.tcond.wait(start + self.timeout - now)
+                        now = time.time()
+                        if now - start > self.timeout:
+                            os.abort()
+                else:
+                    while len(self.current) >= self.max:
+                        self.tcond.wait()
+            th = reqthread(target=self.run, args=[req])
+            th.start()
+            while th.is_alive() and th not in self.current:
+                self.tcond.wait()
 
     def ckflush(self, req):
         while len(req.buffer) > 0:
@@ -104,6 +131,7 @@ class freethread(handler):
             th = threading.current_thread()
             with self.lk:
                 self.current.add(th)
+                self.tcond.notify_all()
             try:
                 env = req.mkenv()
                 with perf.request(env) as reqevent:
@@ -121,6 +149,7 @@ class freethread(handler):
             finally:
                 with self.lk:
                     self.current.remove(th)
+                    self.tcond.notify_all()
         finally:
             req.close()