From 6d0491e2dec908f661aa0eb12db3f47feb0f99a8 Mon Sep 17 00:00:00 2001 From: hns Date: Tue, 16 Jan 2001 22:31:08 +0000 Subject: [PATCH] Threads and evaluators are now started in lazy mode - only when needed. Halleluja! --- src/helma/framework/core/Action.java | 119 ++++++++++++- src/helma/framework/core/Application.java | 45 ++++- src/helma/framework/core/ESAppNode.java | 49 +++++- src/helma/framework/core/FunctionFile.java | 35 +++- src/helma/framework/core/Prototype.java | 19 +++ .../framework/core/RequestEvaluator.java | 112 +++++------- src/helma/framework/core/Template.java | 22 ++- src/helma/framework/core/TypeManager.java | 161 ++++-------------- src/helma/objectmodel/db/Transactor.java | 3 +- src/helma/xmlrpc/Benchmark.java | 2 +- src/helma/xmlrpc/XmlRpcClient.java | 13 +- src/helma/xmlrpc/XmlRpcClientLite.java | 51 +++++- 12 files changed, 396 insertions(+), 235 deletions(-) diff --git a/src/helma/framework/core/Action.java b/src/helma/framework/core/Action.java index 64bc324c..e3d8bb99 100644 --- a/src/helma/framework/core/Action.java +++ b/src/helma/framework/core/Action.java @@ -8,6 +8,13 @@ import java.io.*; import helma.framework.*; import helma.objectmodel.IServer; import FESI.Data.*; +import FESI.Parser.*; +import FESI.AST.ASTFormalParameterList; +import FESI.AST.ASTStatementList; +import FESI.AST.EcmaScriptTreeConstants; +import FESI.Interpreter.*; +import FESI.Exceptions.*; + /** @@ -20,34 +27,40 @@ import FESI.Data.*; public class Action { String name; - String functionName; Prototype prototype; Application app; + File file; long lastmod; + + ParsedFunction pfunc; + + public Action (File file, String name, Prototype proto) { this.prototype = proto; this.app = proto.app; this.name = name; + this.file = file; update (file); } public void update (File f) { - long fmod = f.lastModified (); + this.file = f; + long fmod = file.lastModified (); if (lastmod == fmod) return; try { - FileReader reader = new FileReader (f); - char cbuf[] = new char[(int)f.length ()]; + FileReader reader = new FileReader (file); + char cbuf[] = new char[(int) file.length ()]; reader.read (cbuf); reader.close (); String content = new String (cbuf); update (content); } catch (Exception filex) { - IServer.getLogger().log ("*** Error reading template file "+f+": "+filex); + IServer.getLogger().log ("*** Error reading action file "+file+": "+filex); } lastmod = fmod; } @@ -57,13 +70,13 @@ public class Action { public void update (String content) throws Exception { // IServer.getLogger().log ("Reading text template " + name); - functionName = name+"_hop_action"; + String fname = name+"_hop_action"; try { - app.typemgr.readFunction (functionName, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", content, prototype.getName ()); + pfunc = parseFunction (fname, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", content); } catch (Exception x) { String message = x.getMessage (); - app.typemgr.generateErrorFeedback (functionName, message, prototype.getName ()); + app.typemgr.generateErrorFeedback (fname, message, prototype.getName ()); } } @@ -74,9 +87,97 @@ public class Action { } public String getFunctionName () { - return functionName; + return pfunc.functionName; } + protected ParsedFunction parseFunction (String funcname, String params, String body) throws EcmaScriptException { + + // ESObject fp = app.eval.evaluator.getFunctionPrototype(); + // ConstructedFunctionObject function = null; + ASTFormalParameterList fpl = null; + ASTStatementList sl = null; + FunctionEvaluationSource fes = null; + + if (body == null || "".equals (body.trim())) + body = ";\r\n"; + else + body = body + "\r\n"; + if (params == null) params = ""; + else params = params.trim (); + + String fulltext = "function "+funcname+" (" + params + ") {\n" + body + "\n}"; + + EcmaScript parser; + StringReader is; + + // Special case for empty parameters + if (params.length()==0) { + fpl = new ASTFormalParameterList(EcmaScriptTreeConstants.JJTFORMALPARAMETERLIST); + } else { + is = new java.io.StringReader(params); + parser = new EcmaScript(is); + try { + fpl = (ASTFormalParameterList) parser.FormalParameterList(); + is.close(); + } catch (ParseException x) { + throw new EcmaScriptParseException (x, new StringEvaluationSource(fulltext, null)); + } + } + // this is very very very strange: without the toString, lots of obscure exceptions + // deep inside the parser... + is = new java.io.StringReader(body.toString ()); + try { + parser = new EcmaScript (is); + sl = (ASTStatementList) parser.StatementList(); + is.close(); + } catch (ParseException x) { + IServer.getLogger().log ("Error parsing file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+x); + throw new EcmaScriptParseException (x, new StringEvaluationSource(fulltext, null)); + } catch (Exception x) { + IServer.getLogger().log ("Error parsing file "+app.getName()+":"+prototype.getName()+"/"+file.getName()+": "+x); + throw new RuntimeException (x.getMessage ()); + } + + fes = new FunctionEvaluationSource (new StringEvaluationSource(fulltext, null), funcname); + return new ParsedFunction (fpl, sl, fes, fulltext, funcname); + } + + public void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException { + if (pfunc != null) + pfunc.updateRequestEvaluator (reval); + } + + class ParsedFunction { + + ASTFormalParameterList fpl = null; + ASTStatementList sl = null; + FunctionEvaluationSource fes = null; + String fullFunctionText = null; + String functionName; + + public ParsedFunction (ASTFormalParameterList fpl, ASTStatementList sl, FunctionEvaluationSource fes, + String fullFunctionText, String functionName) { + this.fpl = fpl; + this.sl = sl; + this.fes = fes; + this.fullFunctionText = fullFunctionText; + this.functionName = functionName; + } + + public void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException { + + ObjectPrototype op = reval.getPrototype (prototype.getName()); + + EcmaScriptVariableVisitor vdvisitor = reval.evaluator.getVarDeclarationVisitor(); + Vector vnames = vdvisitor.processVariableDeclarations(sl, fes); + + FunctionPrototype fp = ConstructedFunctionObject.makeNewConstructedFunction ( + reval.evaluator, functionName, fes, + fullFunctionText, fpl.getArguments(), vnames, sl); + op.putHiddenProperty (functionName, fp); + } + + } } diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 007674e3..81c24047 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -38,7 +38,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, Runn TypeManager typemgr; RequestEvaluator eval; - private Stack freeThreads; + protected Stack freeThreads; protected Vector allThreads; Hashtable sessions; @@ -154,7 +154,7 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, Runn if (allThreads != null) { for (Enumeration e=allThreads.elements (); e.hasMoreElements (); ) { RequestEvaluator ev = (RequestEvaluator) e.nextElement (); - ev.stop (); + ev.stopThread (); } } allThreads.removeAllElements (); @@ -166,20 +166,49 @@ public class Application extends UnicastRemoteObject implements IRemoteApp, Runn } } - public synchronized RequestEvaluator getEvaluator () { + protected RequestEvaluator getEvaluator () { if (freeThreads == null) throw new ApplicationStoppedException (); - if (freeThreads.empty ()) + try { + return (RequestEvaluator) freeThreads.pop (); + } catch (EmptyStackException nothreads) { throw new RuntimeException ("Maximum Thread count reached."); - RequestEvaluator ev = (RequestEvaluator) freeThreads.pop (); - return ev; + } } - public synchronized void releaseEvaluator (RequestEvaluator ev) { + protected void releaseEvaluator (RequestEvaluator ev) { if (ev != null) freeThreads.push (ev); } + protected boolean setNumberOfEvaluators (int n) { + if (n < 1 || n > 512) + return false; + int current = allThreads.size(); + synchronized (allThreads) { + if (n > current) { + int toBeCreated = n - current; + for (int i=0; i 1000) { - rtx.cleanup (); // close sql connections held by this thread - startThread (); + stopThread (); // stop thread - a new one will be created when needed + break; } - } - // IServer.getLogger().log (this+ " exiting."); + synchronized (this) { + notifyAll (); + try { + wait (); + } catch (InterruptedException ir) { + Thread.currentThread ().interrupt (); + } + } + + } while (evaluator.thread == Thread.currentThread () && evaluator.thread == rtx); } public synchronized ResponseTrans invoke (RequestTrans req, User user) throws Exception { - checkThread (); this.reqtype = HTTP; this.req = req; this.user = user; this.res = new ResponseTrans (); - notifyAll (); + checkThread (); wait (app.requestTimeout); if (reqtype > 0) { IServer.getLogger().log ("Stopping Thread for Request "+app.getName()+"/"+req.path); - evaluator.thread = null; - rtx.kill (); + stopThread (); res.reset (); res.write ("Error in application '"+app.getName()+"':

Request timed out.
"); - rtx = new Transactor (this, app.nmgr); - evaluator.thread = rtx; - rtx.start (); - Thread.yield (); } return res; @@ -488,7 +475,6 @@ public class RequestEvaluator implements Runnable { public synchronized Object invokeXmlRpc (String method, Vector args) throws Exception { - checkThread (); this.reqtype = XMLRPC; this.user = null; this.method = method; @@ -496,16 +482,10 @@ public class RequestEvaluator implements Runnable { result = null; exception = null; - notifyAll (); + checkThread (); wait (app.requestTimeout); if (reqtype > 0) { - IServer.getLogger().log ("Stopping Thread"); - evaluator.thread = null; - rtx.kill (); - rtx = new Transactor (this, app.nmgr); - evaluator.thread = rtx; - rtx.start (); - Thread.yield (); + stopThread (); } if (exception != null) @@ -525,7 +505,6 @@ public class RequestEvaluator implements Runnable { public synchronized ESValue invokeFunction (ESObject obj, String functionName, ESValue[] args) throws Exception { - checkThread (); this.reqtype = INTERNAL; this.user = null; this.current = obj; @@ -534,17 +513,11 @@ public class RequestEvaluator implements Runnable { esresult = ESNull.theNull; exception = null; - notifyAll (); + checkThread (); wait (60000l*15); // give internal call more time (15 minutes) to complete if (reqtype > 0) { - IServer.getLogger().log ("Stopping Thread"); - evaluator.thread = null; - rtx.kill (); - rtx = new Transactor (this, app.nmgr); - evaluator.thread = rtx; - rtx.start (); - Thread.yield (); + stopThread (); } if (exception != null) @@ -554,7 +527,6 @@ public class RequestEvaluator implements Runnable { public synchronized ESValue invokeFunction (User user, String functionName, ESValue[] args) throws Exception { - checkThread (); this.reqtype = INTERNAL; this.user = user; this.current = null; @@ -563,17 +535,11 @@ public class RequestEvaluator implements Runnable { esresult = ESNull.theNull; exception = null; - notifyAll (); + checkThread (); wait (app.requestTimeout); if (reqtype > 0) { - IServer.getLogger().log ("Stopping Thread"); - evaluator.thread = null; - rtx.kill (); - rtx = new Transactor (this, app.nmgr); - evaluator.thread = rtx; - rtx.start (); - Thread.yield (); + stopThread (); } if (exception != null) @@ -583,24 +549,32 @@ public class RequestEvaluator implements Runnable { /** - * Stop this request evaluator. If currently active kill the request, otherwise just + * Stop this request evaluator's current thread. If currently active kill the request, otherwise just * notify. */ - public synchronized void stop () { + public synchronized void stopThread () { + // IServer.getLogger().log ("Stopping Thread"); evaluator.thread = null; - if (reqtype != NONE) { - rtx.kill (); - } else { - notifyAll (); + Transactor t = rtx; + rtx = null; + if (t != null) { + if (reqtype != NONE) { + t.kill (); + } else { + notifyAll (); + } + t.cleanup (); } } - private void checkThread () { + private synchronized void checkThread () throws InterruptedException { if (rtx == null || !rtx.isAlive()) { + // IServer.getLogger().log ("Starting Thread"); rtx = new Transactor (this, app.nmgr); evaluator.thread = rtx; rtx.start (); - Thread.yield (); + } else { + notifyAll (); } } diff --git a/src/helma/framework/core/Template.java b/src/helma/framework/core/Template.java index 90cb4748..4e199444 100644 --- a/src/helma/framework/core/Template.java +++ b/src/helma/framework/core/Template.java @@ -7,6 +7,7 @@ import java.util.*; import java.io.*; import helma.framework.*; import FESI.Data.*; +import FESI.Exceptions.*; /** @@ -22,6 +23,9 @@ import FESI.Data.*; public class Template extends Action { + ParsedFunction psfunc; + + public Template (File file, String name, Prototype proto) { super (file, name, proto); } @@ -128,25 +132,21 @@ public class Template extends Action { } // templateBody.append ("\r\nreturn null;\r\n"); - - functionName = name; String fname = name+"_as_string"; String body = templateBody.toString (); try { - app.typemgr.readFunction (name, + pfunc = parseFunction (name, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", - body+"\r\nreturn null;\r\n", - prototype.getName ()); + body+"\r\nreturn null;\r\n"); } catch (Exception x) { String message = x.getMessage (); app.typemgr.generateErrorFeedback (name, message, prototype.getName ()); } try { - app.typemgr.readFunction (fname, + psfunc = parseFunction (fname, "arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10", - "res.pushStringBuffer(); "+body+"\r\nreturn res.popStringBuffer();\r\n", - prototype.getName ()); + "res.pushStringBuffer(); "+body+"\r\nreturn res.popStringBuffer();\r\n"); } catch (Exception x) { String message = x.getMessage (); app.typemgr.generateErrorFeedback (fname, message, prototype.getName ()); @@ -154,6 +154,12 @@ public class Template extends Action { } + public void updateRequestEvaluator (RequestEvaluator reval) throws EcmaScriptException { + if (pfunc != null) + pfunc.updateRequestEvaluator (reval); + if (psfunc != null) + psfunc.updateRequestEvaluator (reval); + } class Part { diff --git a/src/helma/framework/core/TypeManager.java b/src/helma/framework/core/TypeManager.java index fd214a8a..d5d5ee62 100644 --- a/src/helma/framework/core/TypeManager.java +++ b/src/helma/framework/core/TypeManager.java @@ -151,34 +151,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { public void registerPrototype (String name, File dir, Prototype proto) { // IServer.getLogger().log ("registering prototype "+name); - int size = app.allThreads.size (); - for (int i=0; i"+x+"

"); } } else if (list[i].endsWith (app.scriptExtension) && tmpfile.length () > 0) { @@ -211,7 +182,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { nfunc.put (tmpname, ff); } catch (Throwable x) { IServer.getLogger().log ("Error creating prototype: "+x); - // broadcaster.broadcast ("Error creating prototype "+list[i]+":
"+x+"

"); } } else if (list[i].endsWith (app.actionExtension) && tmpfile.length () > 0) { try { @@ -219,7 +189,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { nact.put (tmpname, af); } catch (Throwable x) { IServer.getLogger().log ("Error creating prototype: "+x); - // broadcaster.broadcast ("Error creating prototype "+list[i]+":
"+x+"

"); } } } @@ -257,7 +226,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { } } catch (Throwable x) { IServer.getLogger().log ("Error updating prototype: "+x); - // broadcaster.broadcast ("Error updating prototype "+list[i]+":
"+x+"

"); } ntemp.put (tmpname, t); @@ -273,7 +241,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { } } catch (Throwable x) { IServer.getLogger().log ("Error updating prototype: "+x); - // broadcaster.broadcast ("Error updating prototype "+list[i]+":
"+x+"

"); } nfunc.put (tmpname, ff); @@ -289,7 +256,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { } } catch (Throwable x) { IServer.getLogger().log ("Error updating prototype: "+x); - // broadcaster.broadcast ("Error updating prototype "+list[i]+":
"+x+"

"); } nact.put (tmpname, af); @@ -309,100 +275,6 @@ public class TypeManager implements Runnable, EcmaScriptTreeConstants { proto.actions = nact; } - protected void readFunctionFile (File f, String protoname) { - - EvaluationSource es = new FileEvaluationSource(f.getPath(), null); - FileReader fr = null; - - int size = app.allThreads.size (); - for (int i=0; i