From 5cdd61df59fc87e9f8dbbfb0f1ea1c535a3694f9 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 17 Oct 2009 17:48:06 +0200 Subject: [PATCH] Added timelimits to threads. --- src/dolda/jsvc/RequestThread.java | 6 +++ src/dolda/jsvc/ThreadContext.java | 85 +++++++++++++++++++++++++++++++++++++++ src/dolda/jsvc/util/Misc.java | 9 +++++ 3 files changed, 100 insertions(+) diff --git a/src/dolda/jsvc/RequestThread.java b/src/dolda/jsvc/RequestThread.java index 7940748..6ada741 100644 --- a/src/dolda/jsvc/RequestThread.java +++ b/src/dolda/jsvc/RequestThread.java @@ -3,6 +3,7 @@ package dolda.jsvc; public class RequestThread extends Thread { private Request req; private Responder resp; + private long stime = 0; public RequestThread(Responder resp, Request req, ThreadGroup th, String name) { super(th, name); @@ -11,6 +12,7 @@ public class RequestThread extends Thread { } public void run() { + stime = System.currentTimeMillis(); resp.respond(req); try { req.output().close(); @@ -22,4 +24,8 @@ public class RequestThread extends Thread { public static Request request() { return(((RequestThread)Thread.currentThread()).req); } + + public long stime() { + return(stime); + } } diff --git a/src/dolda/jsvc/ThreadContext.java b/src/dolda/jsvc/ThreadContext.java index 486bc91..27d90bf 100644 --- a/src/dolda/jsvc/ThreadContext.java +++ b/src/dolda/jsvc/ThreadContext.java @@ -1,7 +1,9 @@ package dolda.jsvc; +import dolda.jsvc.util.Misc; import java.util.logging.*; import java.lang.reflect.*; +import java.util.*; public class ThreadContext extends ThreadGroup { private Logger logger = Logger.getLogger("dolda.jsvc.context"); @@ -9,6 +11,8 @@ public class ThreadContext extends ThreadGroup { private long reqs = 0; private final ServerContext ctx; public final Responder root; + private int timelimit = 0; + private boolean forcelimit = false; public ThreadContext(ThreadGroup parent, String name, ServerContext ctx, Class bootclass) { super((parent == null)?(Thread.currentThread().getThreadGroup()):parent, name); @@ -18,7 +22,88 @@ public class ThreadContext extends ThreadGroup { logger.log(Level.SEVERE, "Worker thread terminated with an uncaught exception", e); } }; + + int tl; + tl = Integer.parseInt(ctx.sysconfig("jsvc.timelimit", "0")); + if((tl > 0) && ((timelimit == 0) || (tl < timelimit))) + timelimit = tl; + tl = Integer.parseInt(ctx.libconfig("jsvc.timelimit", "0")); + if((tl > 0) && ((timelimit == 0) || (tl < timelimit))) + timelimit = tl; + forcelimit |= Misc.boolval(ctx.sysconfig("jsvc.forcelimit", "0")); + forcelimit |= Misc.boolval(ctx.libconfig("jsvc.forcelimit", "0")); + root = bootstrap(bootclass); + + if(timelimit > 0) + (new WatchDog()).start(); + } + + private class WatchDog extends Thread { + private Map state = new WeakHashMap(); + + private class State { + String st = "running"; + long lastkill; + } + + private WatchDog() { + super(ThreadContext.this, "Worker watchdog"); + setDaemon(true); + } + + @SuppressWarnings("deprecation") + private long ckthread(long now, RequestThread rt) { + State st = state.get(rt); + if(st == null) { + st = new State(); + state.put(rt, st); + } + if(st.st == "running") { + if(now - rt.stime() > timelimit) { + rt.interrupt(); + st.st = "interrupted"; + st.lastkill = now; + return(5000); + } else { + return(timelimit - (now - rt.stime())); + } + } else if((st.st == "interrupted") || (st.st == "killed")) { + if(st.st == "killed") + logger.log(Level.WARNING, "Thread " + rt + " refused to die; killing again"); + if(now - st.lastkill > 5000) { + rt.stop(); + st.st = "killed"; + st.lastkill = now; + } else { + return(5000 - (now - st.lastkill)); + } + } + return(timelimit); + } + + public void run() { + try { + while(true) { + long next = timelimit; + long now = System.currentTimeMillis(); + Thread[] w = new Thread[workers.activeCount() + 5]; + int num = workers.enumerate(w); + for(int i = 0; i < num; i++) { + if(w[i] instanceof RequestThread){ + RequestThread rt = (RequestThread)w[i]; + if(rt.stime() > 0) { + long n = ckthread(now, rt); + if(n < next) + next = n; + } + } + } + Thread.sleep(next); + } + } catch(InterruptedException e) { + } + } } public void uncaughtException(Thread t, Throwable e) { diff --git a/src/dolda/jsvc/util/Misc.java b/src/dolda/jsvc/util/Misc.java index 122a654..63a10a7 100644 --- a/src/dolda/jsvc/util/Misc.java +++ b/src/dolda/jsvc/util/Misc.java @@ -122,4 +122,13 @@ public class Misc { } return(buf.toString()); } + + public static boolean boolval(String val) { + val = val.trim().toLowerCase(); + if(val.equals("1") || val.equals("on") || val.equals("true") || val.equals("yes") || val.equals("\u22a4")) + return(true); + if(val.equals("0") || val.equals("off") || val.equals("false") || val.equals("no") || val.equals("\u22a5")) + return(false); + throw(new IllegalArgumentException("value not recognized as boolean: " + val)); + } } -- 2.11.0