remm 01/12/11 10:56:03 Modified: catalina/src/share/org/apache/catalina/core StandardWrapper.java Log: - Since I had lots of trouble getting STM right, I decided to experiment a bit more as an exercice. This patch implements instance pooling using a stack. This gives STM very good performance (it needs two syncs + two accesses to the stack for each request, but is otherwise equivalent to a request on a normal servlet). Revision Changes Path 1.35 +152 -57 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java Index: StandardWrapper.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v retrieving revision 1.34 retrieving revision 1.35 diff -u -r1.34 -r1.35 --- StandardWrapper.java 2001/12/11 08:28:38 1.34 +++ StandardWrapper.java 2001/12/11 18:56:03 1.35 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v 1.34 2001/12/11 08:28:38 remm Exp $ - * $Revision: 1.34 $ - * $Date: 2001/12/11 08:28:38 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardWrapper.java,v 1.35 2001/12/11 18:56:03 remm Exp $ + * $Revision: 1.35 $ + * $Date: 2001/12/11 18:56:03 $ * * ==================================================================== * @@ -70,6 +70,7 @@ import java.net.URL; import java.util.Enumeration; import java.util.HashMap; +import java.util.Stack; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -98,14 +99,10 @@ * Standard implementation of the <b>Wrapper</b> interface that represents * an individual servlet definition. No child Containers are allowed, and * the parent Container must be a Context. - * <p> - * <b>IMPLEMENTATION NOTE</b>: This implementation does <i>not</i> support - * a pool of instances for servlets that implement SingleThreadModel. IMHO - * developers should not be encouraged to use this technique, so efforts to - * make them efficient are counter-productive. * * @author Craig R. McClanahan - * @version $Revision: 1.34 $ $Date: 2001/12/11 08:28:38 $ + * @author Remy Maucherat + * @version $Revision: 1.35 $ $Date: 2001/12/11 18:56:03 $ */ public final class StandardWrapper @@ -131,12 +128,6 @@ /** - * Has our SingleThreadModel servlet instance been allocated already? - */ - private boolean allocated = false; - - - /** * The date and time at which this servlet will become available (in * milliseconds since the epoch), or zero if the servlet is available. * If this value equals Long.MAX_VALUE, the unavailability of this @@ -236,6 +227,24 @@ private boolean unloading = false; + /** + * Maximum number of STM instances. + */ + private int maxInstances = 20; + + + /** + * Number of instances currently loaded for a STM servlet. + */ + private int nInstances = 0; + + + /** + * Stack containing the STM instances. + */ + private Stack instancePool = null; + + // ------------------------------------------------------------- Properties @@ -410,6 +419,33 @@ /** + * Return maximum number of instances that will be allocated when a single + * thread model servlet is used. + */ + public int getMaxInstances() { + + return (this.maxInstances); + + } + + + /** + * Set the maximum number of instances that will be allocated when a single + * thread model servlet is used. + * + * @param maxInstnces New value of maxInstances + */ + public void setMaxInstances(int maxInstances) { + + int oldMaxInstances = this.maxInstances; + this.maxInstances = maxInstances; + support.firePropertyChange("maxInstances", oldMaxInstances, + this.maxInstances); + + } + + + /** * Set the parent Container of this Wrapper, but only if it is a Context. * * @param container Proposed parent Container @@ -497,7 +533,7 @@ public boolean isSingleThreadModel() { try { - load(); + loadServlet(); } catch (Throwable t) { ; } @@ -592,8 +628,6 @@ * <code>SingleThreadModel</code>, the Wrapper implementation must ensure * that this instance is not allocated again until it is deallocated by a * call to <code>deallocate()</code>. - * <p> - * <b>FIXME: Provide a way to avoid waiting forever.</b> * * @exception ServletException if the servlet init() method threw * an exception @@ -609,42 +643,61 @@ throw new ServletException (sm.getString("standardWrapper.unloading", getName())); - // Load and initialize our instance if necessary - if (instance == null) { - try { - load(); - } catch (ServletException e) { - throw e; - } catch (Throwable e) { - throw new ServletException - (sm.getString("standardWrapper.allocate"), e); - } - } - // If not SingleThreadedModel, return the same instance every time if (!singleThreadModel) { - if (debug >= 2) - log(" Returning non-STM instance"); - countAllocated++; - return (instance); - } - // Lock and return this instance - synchronized (instance) { - while (allocated) { + // Load and initialize our instance if necessary + if (instance == null) { + synchronized (this) { + if (instance == null) { + try { + instance = loadServlet(); + } catch (ServletException e) { + throw e; + } catch (Throwable e) { + throw new ServletException + (sm.getString("standardWrapper.allocate"), e); + } + } + } + } + + if (!singleThreadModel) { if (debug >= 2) - log(" Waiting for allocated STM instance"); - try { - instance.wait(); - } catch (InterruptedException e) { - ; + log(" Returning non-STM instance"); + countAllocated++; + return (instance); + } + + } + + synchronized (instancePool) { + + while (countAllocated >= nInstances) { + // Allocate a new instance if possible, or else wait + if (nInstances < maxInstances) { + try { + instancePool.push(loadServlet()); + nInstances++; + } catch (ServletException e) { + throw e; + } catch (Throwable e) { + throw new ServletException + (sm.getString("standardWrapper.allocate"), e); + } + } else { + try { + instancePool.wait(); + } catch (InterruptedException e) { + ; + } } } if (debug >= 2) log(" Returning allocated STM instance"); - allocated = true; countAllocated++; - return (instance); + return (Servlet) instancePool.pop(); + } } @@ -661,16 +714,17 @@ */ public void deallocate(Servlet servlet) throws ServletException { - countAllocated--; - // If not SingleThreadModel, no action is required - if (!singleThreadModel) + if (!singleThreadModel) { + countAllocated--; return; + } // Unlock and free this instance - synchronized (instance) { - allocated = false; - instance.notify(); + synchronized (instancePool) { + countAllocated--; + instancePool.push(servlet); + instancePool.notify(); } } @@ -752,11 +806,22 @@ * @exception ServletException if some other loading problem occurs */ public synchronized void load() throws ServletException { + instance = loadServlet(); + } - // Nothing to do if we already have an instance - if (instance != null) - return; + /** + * Load and initialize an instance of this servlet, if there is not already + * at least one initialized instance. This can be used, for example, to + * load servlets that are marked in the deployment descriptor to be loaded + * at server startup time. + */ + public synchronized Servlet loadServlet() throws ServletException { + + // Nothing to do if we already have an instance or an instance pool + if (!singleThreadModel && (instance != null)) + return instance; + // If this "servlet" is really a JSP file, get the right class. // HOLD YOUR NOSE - this is a kludge that avoids having to do special // case Catalina-specific code in Jasper - it also requires that the @@ -873,9 +938,13 @@ } // Register our newly initialized instance - instance = servlet; - singleThreadModel = instance instanceof SingleThreadModel; + singleThreadModel = servlet instanceof SingleThreadModel; + if (singleThreadModel) { + if (instancePool == null) + instancePool = new Stack(); + } fireContainerEvent("load", this); + return servlet; } @@ -977,7 +1046,7 @@ public synchronized void unload() throws ServletException { // Nothing to do if we have never loaded the instance - if (instance == null) + if (!singleThreadModel && (instance == null)) return; unloading = true; @@ -1015,6 +1084,8 @@ instanceSupport.fireInstanceEvent (InstanceEvent.AFTER_DESTROY_EVENT, instance, t); instance = null; + instancePool = null; + nInstances = 0; fireContainerEvent("unload", this); unloading = false; throw new ServletException @@ -1027,6 +1098,30 @@ // Deregister the destroyed instance instance = null; + + if (singleThreadModel && (instancePool != null)) { + try { + Thread.currentThread().setContextClassLoader(classLoader); + while (!instancePool.isEmpty()) { + ((Servlet) instancePool.pop()).destroy(); + } + } catch (Throwable t) { + instancePool = null; + nInstances = 0; + unloading = false; + fireContainerEvent("unload", this); + throw new ServletException + (sm.getString("standardWrapper.destroyException", + getName()), t); + } finally { + // restore the context ClassLoader + Thread.currentThread().setContextClassLoader + (oldCtxClassLoader); + } + instancePool = null; + nInstances = 0; + } + unloading = false; fireContainerEvent("unload", this);
-- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>