You are viewing a plain text version of this content. The canonical link for it is here.
Posted to site-cvs@tcl.apache.org by mx...@apache.org on 2014/08/09 17:29:08 UTC

svn commit: r1616967 [2/2] - in /tcl/rivet/trunk: ./ src/apache-2/ src/common/ src/experimental/

Modified: tcl/rivet/trunk/src/experimental/mod_rivet.c
URL: http://svn.apache.org/viewvc/tcl/rivet/trunk/src/experimental/mod_rivet.c?rev=1616967&r1=1616966&r2=1616967&view=diff
==============================================================================
--- tcl/rivet/trunk/src/experimental/mod_rivet.c (original)
+++ tcl/rivet/trunk/src/experimental/mod_rivet.c Sat Aug  9 15:29:07 2014
@@ -1,25 +1,38 @@
 /* mod_rivet.c -- The apache module itself, for Apache 2.4. */
 
-/* Copyright 2000-2005 The Apache Software Foundation
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-   	http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
 */
 
 /* $Id: mod_rivet.h 1609472 2014-07-10 15:08:52Z mxmanghi $ */
 
-#include "httpd.h"
-#include "http_config.h"
-#include "http_protocol.h"
+/* Rivet config */
+#ifdef HAVE_CONFIG_H
+#include <rivet_config.h>
+#endif
+
+/* Apache includes */
+#include <httpd.h>
+#include <http_config.h>
+#include <http_core.h>
+#include <http_protocol.h>
+#include <http_log.h>
+#include <http_main.h>
 #include <ap_config.h>
 #include <apr_queue.h>
 #include <apr_strings.h>
@@ -29,14 +42,23 @@
 #include <apr_thread_cond.h>
 #include <apr_file_io.h>
 #include <apr_file_info.h>
+#include <apr_env.h>
+
+/* Tcl includes */
+#include <tcl.h>
+
 /* as long as we need to emulate ap_chdir_file we need to include unistd.h */
 #include <unistd.h>
 
 #include "TclWeb.h"
+#include "rivet.h"
 #include "mod_rivet.h"
+#include "mod_rivet_common.h"
+#include "rivetParser.h"
 #include "rivetChannel.h"
+#include "apache_config.h"
 
-mod_rivet_globals       module_globals;
+mod_rivet_globals*      module_globals;
 rivet_server_conf       rsc;
 rivet_interp_globals    interp_globals;
 
@@ -46,54 +68,679 @@ apr_threadkey_t*        handler_thread_k
 
 #define ERRORBUF_SZ     256
 
+/* -- Rivet_ExecuteAndCheck
+ * 
+ * Tcl script execution central procedure. The script stored
+ * outbuf is evaluated and in case an error occurs in the execution
+ * an error handler is executed. In case the error code returned
+ * is RIVET then the error was caused by the invocation of a 
+ * abort_page command and the script stored in conf->abort_script
+ * is run istead. The default error script prints the error buffer
+ *
+ *   Arguments:
+ * 
+ *      - Tcl_Interp* interp:      the Tcl interpreter 
+ *      - Tcl_Obj* tcl_script_obj: a pointer to the Tcl_Obj holding the script
+ *      - request_rec* req:        the current request_rec object pointer
+ *
+ *   Returned value:
+ *
+ *      - invariably TCL_OK
+ *
+ *   Side effects:
+ *
+ *      The Tcl interpreter internal status is changed by the execution
+ *      of the script
+ *
+ */
+
 static int
-RivetServerInit (apr_pool_t *pPool, apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *s)
+Rivet_ExecuteAndCheck(Tcl_Interp *interp, Tcl_Obj *tcl_script_obj, request_rec *req)
 {
-    apr_status_t aprrv;
-    char errorbuf[ERRORBUF_SZ];
+    rivet_thread_private*   private;
+
+    ap_assert (apr_threadkey_private_get ((void **)&private,rivet_thread_key) == APR_SUCCESS);
+
+    rivet_server_conf *conf = Rivet_GetConf(req);
+    rivet_interp_globals *globals = Tcl_GetAssocData(interp, "rivet", NULL);
+
+    Tcl_Preserve (interp);
+    if ( Tcl_EvalObjEx(interp, tcl_script_obj, 0) == TCL_ERROR ) {
+        Tcl_Obj *errscript;
+        Tcl_Obj *errorCodeListObj;
+        Tcl_Obj *errorCodeElementObj;
+        char *errorCodeSubString;
+
+        /* There was an error, see if it's from Rivet and it was caused
+         * by abort_page.
+         */
+
+        errorCodeListObj = Tcl_GetVar2Ex (interp, "errorCode", (char *)NULL, TCL_GLOBAL_ONLY);
+
+        /* errorCode is guaranteed to be set to NONE, but let's make sure
+         * anyway rather than causing a SIGSEGV
+         */
+        ap_assert (errorCodeListObj != (Tcl_Obj *)NULL);
+
+        /* dig the first element out of the errorCode list and see if it
+         * says Rivet -- this shouldn't fail either, but let's assert
+         * success so we don't get a SIGSEGV afterwards */
+        ap_assert (Tcl_ListObjIndex (interp, errorCodeListObj, 0, &errorCodeElementObj) == TCL_OK);
+
+        /* if the error was thrown by Rivet, see if it's abort_page and,
+         * if so, don't treat it as an error, i.e. don't execute the
+         * installed error handler or the default one, just check if
+         * a rivet_abort_script is defined, otherwise the page emits 
+         * as normal
+         */
+        if (strcmp (Tcl_GetString (errorCodeElementObj), "RIVET") == 0) {
+
+            /* dig the second element out of the errorCode list, make sure
+             * it succeeds -- it should always
+             */
+            ap_assert (Tcl_ListObjIndex (interp, errorCodeListObj, 1, &errorCodeElementObj) == TCL_OK);
+
+            errorCodeSubString = Tcl_GetString (errorCodeElementObj);
+            if (strcmp (errorCodeSubString, ABORTPAGE_CODE) == 0) 
+            {
+                if (conf->rivet_abort_script) 
+                {
+                    if (Tcl_EvalObjEx(interp,conf->rivet_abort_script,0) == TCL_ERROR)
+                    {
+                        CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+                        TclWeb_PrintError("<b>Rivet AbortScript failed!</b>",1,globals->req);
+                        TclWeb_PrintError( errorinfo, 0, globals->req );
+                    }
+                }
+                goto good;
+            }
+        }
+
+        Tcl_SetVar( interp, "errorOutbuf",Tcl_GetStringFromObj( tcl_script_obj, NULL ),TCL_GLOBAL_ONLY );
+
+        /* If we don't have an error script, use the default error handler. */
+        if (conf->rivet_error_script ) {
+            errscript = conf->rivet_error_script;
+        } else {
+            errscript = conf->rivet_default_error_script;
+        }
+
+        Tcl_IncrRefCount(errscript);
+        if (Tcl_EvalObjEx(interp, errscript, 0) == TCL_ERROR) {
+            CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+            TclWeb_PrintError("<b>Rivet ErrorScript failed!</b>",1,globals->req);
+            TclWeb_PrintError( errorinfo, 0, globals->req );
+        }
+
+        /* This shouldn't make the default_error_script go away,
+         * because it gets a Tcl_IncrRefCount when it is created. */
+        Tcl_DecrRefCount(errscript);
+    }
+
+    /* Make sure to flush the output if buffer_add was the only output */
+good:
+    
+    if (conf->after_every_script) {
+        if (Tcl_EvalObjEx(interp,conf->after_every_script,0) == TCL_ERROR)
+        {
+            CONST84 char *errorinfo = Tcl_GetVar( interp, "errorInfo", 0 );
+            TclWeb_PrintError("<b>Rivet AfterEveryScript failed!</b>",1,globals->req);
+            TclWeb_PrintError( errorinfo, 0, globals->req );
+        }
+    }
+
+    if (!globals->req->headers_set && (globals->req->charset != NULL)) {
+        TclWeb_SetHeaderType (apr_pstrcat(globals->req->req->pool,"text/html;",globals->req->charset,NULL),globals->req);
+    }
+    TclWeb_PrintHeaders(globals->req);
+    Tcl_Flush(*(private->channel));
+    Tcl_Release(interp);
+
+    return TCL_OK;
+}
+
+/*
+ * -- Rivet_ParseExecFile
+ *
+ * given a filename if the file exists it's either parsed (when a rivet
+ * template) and then executed as a Tcl_Obj instance or directly executed
+ * if a Tcl script.
+ *
+ * This is a separate function so that it may be called from command 'parse'
+ *
+ * Arguments:
+ *
+ *   - TclWebRequest: pointer to the structure collecting Tcl and Apache data
+ *   - filename:      pointer to a string storing the path to the template or
+ *                    Tcl script
+ *   - toplevel:      integer to be interpreted as a boolean meaning the
+ *                    file is pointed by the request. When 0 that's a subtemplate
+ *                    to be parsed and executed from another template
+ */
+
+int
+Rivet_ParseExecFile(TclWebRequest *req, char *filename, int toplevel)
+{
+    char *hashKey = NULL;
+    int isNew = 0;
+    int result = 0;
+
+    Tcl_Obj *outbuf = NULL;
+    Tcl_HashEntry *entry = NULL;
+
+    time_t ctime;
+    time_t mtime;
+
+    rivet_server_conf *rsc;
+    Tcl_Interp *interp = req->interp;
+
+    rsc = Rivet_GetConf( req->req );
+
+    /* If the user configuration has indeed been updated, I guess that
+       pretty much invalidates anything that might have been
+       cached. */
+
+    /* This is all horrendously slow, and means we should *also* be
+       doing caching on the modification time of the .htaccess files
+       that concern us. FIXME */
+
+    if (rsc->user_scripts_updated && *(rsc->cache_size) != 0) {
+        int ct;
+        Tcl_HashEntry *delEntry;
+        /* Clean out the list. */
+        ct = *(rsc->cache_free);
+        while (ct < *(rsc->cache_size)) {
+            /* Free the corresponding hash entry. */
+            delEntry = Tcl_FindHashEntry(
+                    rsc->objCache,
+                    rsc->objCacheList[ct]);
+            if (delEntry != NULL) {
+                Tcl_DecrRefCount((Tcl_Obj *)Tcl_GetHashValue(delEntry));
+            }
+            Tcl_DeleteHashEntry(delEntry);
+
+            free(rsc->objCacheList[ct]);
+            rsc->objCacheList[ct] = NULL;
+            ct ++;
+        }
+        *(rsc->cache_free) = *(rsc->cache_size);
+    }
+
+    /* If toplevel is 0, we are being called from Parse, which means
+       we need to get the information about the file ourselves. */
+    if (toplevel == 0)
+    {
+        Tcl_Obj *fnobj;
+        Tcl_StatBuf buf;
+
+        fnobj = Tcl_NewStringObj(filename, -1);
+        Tcl_IncrRefCount(fnobj);
+        if( Tcl_FSStat(fnobj, &buf) < 0 )
+            return TCL_ERROR;
+        Tcl_DecrRefCount(fnobj);
+        ctime = buf.st_ctime;
+        mtime = buf.st_mtime;
+    } else {
+        ctime = req->req->finfo.ctime;
+        mtime = req->req->finfo.mtime;
+    }
+
+    /* Look for the script's compiled version.  If it's not found,
+     * create it.
+     */
+    if (*(rsc->cache_size))
+    {
+        hashKey = (char*) apr_psprintf(req->req->pool, "%s%lx%lx%d", filename,
+                mtime, ctime, toplevel);
+        entry = Tcl_CreateHashEntry(rsc->objCache, hashKey, &isNew);
+    }
+
+    /* We don't have a compiled version.  Let's create one. */
+    if (isNew || *(rsc->cache_size) == 0)
+    {
+        outbuf = Tcl_NewObj();
+        Tcl_IncrRefCount(outbuf);
+
+        if (toplevel) {
+            if (rsc->rivet_before_script) {
+                Tcl_AppendObjToObj(outbuf,rsc->rivet_before_script);
+            }
+        }
+
+/*
+ * We check whether we are dealing with a pure Tcl script or a Rivet template.
+ * Actually this check is done only if we are processing a toplevel file, every nested 
+ * file (files included through the 'parse' command) is treated as a template.
+ */
+
+        if (!toplevel || (Rivet_CheckType(req->req) == RIVET_TEMPLATE))
+        {
+            /* toplevel == 0 means we are being called from the parse
+             * command, which only works on Rivet .rvt files. */
+
+            result = Rivet_GetRivetFile(filename, toplevel, outbuf, interp);
+
+        } else {
+            /* It's a plain Tcl file */
+            result = Rivet_GetTclFile(filename, outbuf, interp);
+        }
+
+        if (result != TCL_OK)
+        {
+            Tcl_DecrRefCount(outbuf);
+            return result;
+        }
+
+        if (toplevel && rsc->rivet_after_script) {
+            Tcl_AppendObjToObj(outbuf,rsc->rivet_after_script);
+        }
+
+        if (*(rsc->cache_size)) {
+            /* We need to incr the reference count of outbuf because we want
+             * it to outlive this function.  This allows it to stay alive
+             * as long as it's in the object cache.
+             */
+            Tcl_IncrRefCount( outbuf );
+            Tcl_SetHashValue(entry, (ClientData)outbuf);
+        }
+
+        if (*(rsc->cache_free)) {
+
+            //hkCopy = (char*) malloc ((strlen(hashKey)+1) * sizeof(char));
+            //strcpy(rsc->objCacheList[-- *(rsc->cache_free)], hashKey);
+            rsc->objCacheList[--*(rsc->cache_free)] = 
+                (char*) malloc ((strlen(hashKey)+1) * sizeof(char));
+            strcpy(rsc->objCacheList[*(rsc->cache_free)], hashKey);
+            //rsc->objCacheList[-- *(rsc->cache_free) ] = strdup(hashKey);
+        } else if (*(rsc->cache_size)) { /* If it's zero, we just skip this. */
+            Tcl_HashEntry *delEntry;
+            delEntry = Tcl_FindHashEntry(
+                    rsc->objCache,
+                    rsc->objCacheList[*(rsc->cache_size) - 1]);
+            Tcl_DecrRefCount((Tcl_Obj *)Tcl_GetHashValue(delEntry));
+            Tcl_DeleteHashEntry(delEntry);
+            free(rsc->objCacheList[*(rsc->cache_size) - 1]);
+            memmove((rsc->objCacheList) + 1, rsc->objCacheList,
+                    sizeof(char *) * (*(rsc->cache_size) - 1));
+
+            //hkCopy = (char*) malloc ((strlen(hashKey)+1) * sizeof(char));
+            //strcpy (rsc->objCacheList[0], hashKey);
+            rsc->objCacheList[0] = (char*) malloc ((strlen(hashKey)+1) * sizeof(char));
+            strcpy (rsc->objCacheList[0], hashKey);
+            
+            //rsc->objCacheList[0] = (char*) strdup(hashKey);
+        }
+    } else {
+        /* We found a compiled version of this page. */
+        outbuf = (Tcl_Obj *)Tcl_GetHashValue(entry);
+        Tcl_IncrRefCount(outbuf);
+    }
+
+    rsc->user_scripts_updated = 0;
+    {
+        int res = 0;
+        res = Rivet_ExecuteAndCheck(interp, outbuf, req->req);
+        Tcl_DecrRefCount(outbuf);
+        return res;
+    }
+}
+
+/*
+ * -- Rivet_ParseExecString
+ *
+ * This function accepts a Tcl_Obj carrying a string to be interpreted as
+ * a Rivet template. This function is the core for command 'parsestr'
+ * 
+ * Arguments:
+ *
+ *   - TclWebRequest* req: pointer to the structure collecting Tcl and
+ *   Apache data
+ *   - Tcl_Obj* inbuf: Tcl object storing the template to be parsed.
+ */
+
+int
+Rivet_ParseExecString (TclWebRequest* req, Tcl_Obj* inbuf)
+{
+    int res = 0;
+    Tcl_Obj* outbuf = Tcl_NewObj();
+    Tcl_Interp *interp = req->interp;
+
+    Tcl_IncrRefCount(outbuf);
+    Tcl_AppendToObj(outbuf, "puts -nonewline \"", -1);
+
+    /* If we are not inside a <? ?> section, add the closing ". */
+    if (Rivet_Parser(outbuf, inbuf) == 0)
+    {
+        Tcl_AppendToObj(outbuf, "\"\n", 2);
+    } 
+
+    Tcl_AppendToObj(outbuf, "\n", -1);
+
+    res = Rivet_ExecuteAndCheck(interp, outbuf, req->req);
+    Tcl_DecrRefCount(outbuf);
+
+    return res;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ * Rivet_CreateTclInterp --
+ *
+ * Arguments:
+ *  server_rec* s: pointer to a server_rec structure
+ *
+ * Results:
+ *  pointer to a Tcl_Interp structure
+ * 
+ * Side Effects:
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static Tcl_Interp* 
+Rivet_CreateTclInterp (server_rec* s)
+{
+    Tcl_Interp* interp;
+
+    /* Initialize TCL stuff  */
+    Tcl_FindExecutable(RIVET_NAMEOFEXECUTABLE);
+    interp = Tcl_CreateInterp();
+
+    if (interp == NULL)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                     MODNAME ": Error in Tcl_CreateInterp, aborting\n");
+        exit(1);
+    }
+
+    if (Tcl_Init(interp) == TCL_ERROR)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                     MODNAME ": Error in Tcl_Init: %s, aborting\n",
+                     Tcl_GetStringResult(interp));
+        exit(1);
+    }
+
+    return interp;
+}
 
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Rivet_PerInterpInit --
+ *
+ *  Do the initialization that needs to happen for every
+ *  interpreter.
+ *
+ * Results:
+ *  None.
+ *
+ * Side Effects:
+ *  None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+void  Rivet_PerInterpInit(Tcl_Interp* interp, server_rec *s, apr_pool_t *p)
+{
+    rivet_interp_globals *globals = NULL;
+    Tcl_Obj* auto_path = NULL;
+    Tcl_Obj* rivet_tcl = NULL;
+
+    ap_assert (interp != (Tcl_Interp *)NULL);
+    Tcl_Preserve (interp);
+
+    /* We register the Tcl channel to the interpreter */
+
+    Tcl_RegisterChannel(interp, *(module_globals->outchannel));
+
+    /* Set up interpreter associated data */
+
+    globals = apr_pcalloc (p, sizeof(rivet_interp_globals));
+    Tcl_SetAssocData (interp,"rivet",NULL,globals);
+    
+    /* 
+     * abort_page status variables in globals are set here and then 
+     * reset in Rivet_SendContent just before the request processing is 
+     * completed 
+     */
+
+    /* Rivet commands namespace is created */
+    globals->rivet_ns = Tcl_CreateNamespace (interp,RIVET_NS,NULL,
+                                            (Tcl_NamespaceDeleteProc *)NULL);
+    globals->page_aborting  = 0;
+    globals->abort_code     = NULL;
+    globals->req            = TclWeb_NewRequestObject (p); 
+    globals->srec           = s;
+    globals->r              = NULL;
+
+    /* Eval Rivet's init.tcl file to load in the Tcl-level commands. */
+
+    /* We put in front the auto_path list the path to the directory where
+     * init.tcl is located (provides package Rivet, previously RivetTcl)
+     */
+
+    auto_path = Tcl_GetVar2Ex(interp,"auto_path",NULL,TCL_GLOBAL_ONLY);
+
+    rivet_tcl = Tcl_NewStringObj(RIVET_DIR,-1);
+    Tcl_IncrRefCount(rivet_tcl);
+
+    if (Tcl_ListObjReplace(interp,auto_path,0,0,1,&rivet_tcl) == TCL_ERROR)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
+                     MODNAME ": error setting auto_path: %s",
+                     Tcl_GetStringFromObj(auto_path,NULL));
+    } 
+    Tcl_DecrRefCount(rivet_tcl);
+
+    /* Initialize the interpreter with Rivet's Tcl commands. */
+    Rivet_InitCore(interp);
+
+    /* Create a global array with information about the server. */
+    Rivet_InitServerVariables(interp, p );
+//  Rivet_PropagateServerConfArray( interp, rsc );
+
+    /* Loading into the interpreter commands in librivet.so */
+    /* Tcl Bug #3216070 has been solved with 8.5.10 and commands shipped with
+     * Rivetlib can be mapped at this stage
+     */
+
+    if (Tcl_PkgRequire(interp, RIVETLIB_TCL_PACKAGE, RIVET_VERSION, 1) == NULL)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                     MODNAME ": Error loading rivetlib package: %s",
+                     Tcl_GetStringResult(interp) );
+        exit(1);
+    }
+
+    /*  If rivet is configured to export the ::rivet namespace commands we set the
+     *  array variable ::rivet::module_conf(export_namespace_commands) before calling init.tcl
+     *  This array will be unset after commands are exported.
+     */
+
+    Tcl_SetVar2Ex(interp,"module_conf","export_namespace_commands",Tcl_NewIntObj(RIVET_NAMESPACE_EXPORT),0);
+    Tcl_SetVar2Ex(interp,"module_conf","import_rivet_commands",Tcl_NewIntObj(RIVET_NAMESPACE_IMPORT),0);
+
+    /* Eval Rivet's init.tcl file to load in the Tcl-level commands. */
+
+    /* Watch out! Calling Tcl_PkgRequire with a version number binds this module to
+     * the Rivet package revision number in rivet/init.tcl
+     *
+     * RIVET_TCL_PACKAGE_VERSION is defined by configure.ac as the combination
+     * "MAJOR_VERSION.MINOR_VERSION". We don't expect to change rivet/init.tcl
+     * across patchlevel releases
+     */
+
+    if (Tcl_PkgRequire(interp, "Rivet", RIVET_TCL_PACKAGE_VERSION, 1) == NULL)
+    {
+        ap_log_error (APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                      MODNAME ": init.tcl must be installed correctly for Apache Rivet to function: %s (%s)",
+                      Tcl_GetStringResult(interp), RIVET_DIR );
+        exit(1);
+    }
+
+    Tcl_Release(interp);
+}
+
+/*
+ *
+ */
+
+static int Rivet_InitHandler(apr_pool_t *pPool, apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *s)
+{
+    rivet_server_conf *rsc = RIVET_SERVER_CONF( s->module_config );
+
+    apr_threadkey_private_create (&rivet_thread_key,NULL, pPool);
+
+    module_globals->rivet_panic_pool       = pPool;
+    module_globals->rivet_panic_server_rec = s;
+    module_globals->server                 = s;
+
+#if RIVET_DISPLAY_VERSION
     ap_add_version_component(pPool, "Rivet/Experimental/"__DATE__"/"__TIME__"/");
+    //ap_add_version_component(pPool, RIVET_PACKAGE_NAME"/"RIVET_VERSION);
+#else
+    ap_add_version_component(pPool, RIVET_PACKAGE_NAME);
+#endif
+
+    FILEDEBUGINFO;
+
+    /* we create and initialize a master (server) interpreter */
+
+    rsc->server_interp = Rivet_CreateTclInterp(s) ; /* root interpreter */
+
+    /* Create TCL channel and store a pointer in the rivet_server_conf object */
+
+    module_globals->outchannel    = apr_pcalloc (pPool, sizeof(Tcl_Channel));
+    *(module_globals->outchannel) = Tcl_CreateChannel(&RivetChan, "apacheout", rivet_thread_key, TCL_WRITABLE);
+
+    /* The channel we have just created replaces Tcl's stdout */
+
+    Tcl_SetStdChannel (*(module_globals->outchannel), TCL_STDOUT);
+
+    /* Set the output buffer size to the largest allowed value, so that we 
+     * won't send any result packets to the browser unless the Rivet
+     * programmer does a "flush stdout" or the page is completed.
+     */
+
+    Tcl_SetChannelBufferSize (*(module_globals->outchannel), TCL_MAX_CHANNEL_BUFFER_SIZE);
+
+    /* We register the Tcl channel to the interpreter */
+
+    Tcl_RegisterChannel(rsc->server_interp, *(module_globals->outchannel));
+
+    Rivet_PerInterpInit(rsc->server_interp,s,pPool);
+
+    /* we don't create the cache here: it would make sense for prefork MPM
+     * but threaded MPM bridges have their pool of threads. Each of them
+     * will by now have their own cache
+     */
+
+    // Rivet_CreateCache(s,pPool);
+
+    if (rsc->rivet_server_init_script != NULL) {
+        Tcl_Interp* interp = rsc->server_interp;
+
+        if (Tcl_EvalObjEx(interp, rsc->rivet_server_init_script, 0) != TCL_OK)
+        {
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
+                         MODNAME ": Error running ServerInitScript '%s': %s",
+                         Tcl_GetString(rsc->rivet_server_init_script),
+                         Tcl_GetVar(interp, "errorInfo", 0));
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 
+                         MODNAME ": ServerInitScript '%s' successful", 
+                         Tcl_GetString(rsc->rivet_server_init_script));
+        }
+    }
+    Tcl_SetPanicProc(Rivet_Panic);
     
-    aprrv = apr_dso_load(&module_globals.dso_handle,"/home/manghi/Projects/rivet/src/experimental/.libs/rivet_worker_mpm.so",pPool);
+    return OK;
+}
+
+static int
+Rivet_ServerInit (apr_pool_t *pPool, apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *s)
+{
+    apr_status_t aprrv;
+    char errorbuf[ERRORBUF_SZ];
+    char* mpm_model_handler = "rivet_prefork_mpm.so";
+    char* mpm_model_path;
+
+    /* Everything revolves around this structure */
+
+    module_globals = apr_palloc(pPool,sizeof(mod_rivet_globals));
+
+    /* Let's load the Rivet-MPM bridge */
+
+    /* TODO: This kludge to load off-path MPM model share libraries has to be removed because is a security hole */
+
+    if (apr_env_get (&mpm_model_path,"RIVET_MPM_BRIDGE",pTemp) != APR_SUCCESS)
+    {
+        mpm_model_path = apr_pstrcat(pTemp,RIVET_DIR,"/mpm/",mpm_model_handler,NULL);
+    } 
+
+    aprrv = apr_dso_load(&module_globals->dso_handle,mpm_model_path,pPool);
     if (aprrv == APR_SUCCESS)
     {
         apr_status_t                rv;
         apr_dso_handle_sym_t        func = NULL;
 
-        rv = apr_dso_sym(&func,module_globals.dso_handle,"Rivet_MPM_Init");
+        rv = apr_dso_sym(&func,module_globals->dso_handle,"Rivet_MPM_Init");
         if (rv == APR_SUCCESS)
         {
-            module_globals.mpm_init = (int (*)(apr_pool_t*,server_rec*)) func;
+            module_globals->mpm_init = (int (*)(apr_pool_t*,server_rec*)) func;
         }
         else
         {
             ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
-                         MODNAME " Error loading symbol Rivet_MPM_Init: %s", 
-                         apr_dso_error(module_globals.dso_handle,errorbuf,ERRORBUF_SZ));
+                         MODNAME ": Error loading symbol Rivet_MPM_Init: %s", 
+                         apr_dso_error(module_globals->dso_handle,errorbuf,ERRORBUF_SZ));
             exit(1);   
         }
 
-        rv = apr_dso_sym(&func,module_globals.dso_handle,"Rivet_MPM_Request");
+        rv = apr_dso_sym(&func,module_globals->dso_handle,"Rivet_MPM_Request");
         if (rv == APR_SUCCESS)
         {
-            module_globals.mpm_request = (int (*)(request_rec*)) func;
+            module_globals->mpm_request = (int (*)(request_rec*)) func;
         }
         else
         {
             ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
-                         MODNAME " Error loading symbol Rivet_MPM_Request: %s", 
-                         apr_dso_error(module_globals.dso_handle,errorbuf,ERRORBUF_SZ));
+                         MODNAME ": Error loading symbol Rivet_MPM_Request: %s", 
+                         apr_dso_error(module_globals->dso_handle,errorbuf,ERRORBUF_SZ));
             exit(1);   
         }
 
+        rv = apr_dso_sym(&func,module_globals->dso_handle,"Rivet_MPM_Finalize");
+        if (rv == APR_SUCCESS)
+        {
+            module_globals->mpm_finalize = (apr_status_t (*)(void *)) func;
+        }
+        else
+        {
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
+                         MODNAME ": Error loading symbol Rivet_MPM_Finalize: %s", 
+                         apr_dso_error(module_globals->dso_handle,errorbuf,ERRORBUF_SZ));
+            exit(1);   
+        }
 
+        /* active threads count */
+        
+        apr_atomic_init(pPool);
+        module_globals->threads_count = (apr_uint32_t *) apr_palloc(pPool,sizeof(apr_uint32_t));
+        apr_atomic_set32(module_globals->threads_count,0);
+
+        module_globals->server_shutdown = 0;
+
+        Rivet_InitHandler (pPool,pLog,pTemp,s);
     }
     else
     {
 
+        /* If we don't find the mpm handler module we give up */
+
         ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
                      MODNAME " Error loading MPM manager: %s", 
-                     apr_dso_error(module_globals.dso_handle,errorbuf,1024));
+                     apr_dso_error(module_globals->dso_handle,errorbuf,1024));
         exit(1);   
     }
 
@@ -175,32 +822,53 @@ int RivetContent (rivet_thread_private* 
     return OK;
 }
 
-static void RivetChildInit (apr_pool_t *pChild, server_rec *s)
+static void Rivet_ChildInit (apr_pool_t *pChild, server_rec *s)
 {
-    (*module_globals.mpm_init)(pChild,s);
-//  Tcl_CreateExitHandler(tcl_exit_handler,(ClientData)rivet_thread_key);
+    (*module_globals->mpm_init)(pChild,s);
+    apr_pool_cleanup_register (pChild, s, module_globals->mpm_finalize, module_globals->mpm_finalize);
 }
 
-static int RivetHandler(request_rec *r)    
+static int Rivet_Handler (request_rec *r)    
 {
-    return (*module_globals.mpm_request)(r);
+    return (*module_globals->mpm_request)(r);
 }
 
+/*
+ * -- rivet_register_hooks: mod_rivet basic setup.
+ *
+ * 
+ */
+
 static void rivet_register_hooks(apr_pool_t *p)
 {
-    ap_hook_handler     (RivetHandler,   NULL, NULL, APR_HOOK_MIDDLE);
-    ap_hook_child_init  (RivetChildInit, NULL, NULL, APR_HOOK_LAST);
-    ap_hook_post_config (RivetServerInit, NULL, NULL, APR_HOOK_LAST);
+    ap_hook_post_config (Rivet_ServerInit, NULL, NULL, APR_HOOK_LAST);
+    ap_hook_handler     (Rivet_Handler,    NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_child_init  (Rivet_ChildInit,  NULL, NULL, APR_HOOK_LAST);
 }
 
+/* mod_rivet basic structures */
+
+/* configuration commands and directives */
+
+const command_rec rivet_cmds[] =
+{
+    AP_INIT_TAKE2 ("RivetServerConf", Rivet_ServerConf, NULL, RSRC_CONF, NULL),
+    AP_INIT_TAKE2 ("RivetDirConf", Rivet_DirConf, NULL, ACCESS_CONF, NULL),
+    AP_INIT_TAKE2 ("RivetUserConf", Rivet_UserConf, NULL, ACCESS_CONF|OR_FILEINFO,
+                   "RivetUserConf key value: sets RivetUserConf(key) = value"),
+    {NULL}
+};
+
 /* Dispatch list for API hooks */
-module AP_MODULE_DECLARE_DATA rivet_module = {
+
+module AP_MODULE_DECLARE_DATA rivet_module = 
+{
     STANDARD20_MODULE_STUFF, 
-    NULL,                /* create per-dir    config structures */
-    NULL,                /* merge  per-dir    config structures */
-    NULL,                /* create per-server config structures */
-    NULL,                /* merge  per-server config structures */
-    NULL,                /* table of config file commands       */
-    rivet_register_hooks /* register hooks                      */
+    Rivet_CreateDirConfig,  /* create per-dir config structures    */
+    Rivet_MergeDirConfig,   /* merge  per-dir config structures    */
+    Rivet_CreateConfig,     /* create per-server config structures */
+    Rivet_MergeConfig,      /* merge  per-server config structures */
+    rivet_cmds,             /* table of config file commands       */
+    rivet_register_hooks    /* register hooks                      */
 };
 

Modified: tcl/rivet/trunk/src/experimental/mod_rivet.h
URL: http://svn.apache.org/viewvc/tcl/rivet/trunk/src/experimental/mod_rivet.h?rev=1616967&r1=1616966&r2=1616967&view=diff
==============================================================================
--- tcl/rivet/trunk/src/experimental/mod_rivet.h (original)
+++ tcl/rivet/trunk/src/experimental/mod_rivet.h Sat Aug  9 15:29:07 2014
@@ -24,10 +24,16 @@
 #include <apr_tables.h>
 #include <apr_thread_proc.h>
 #include <apr_thread_cond.h>
+#include <apr_atomic.h>
 #include <tcl.h>
 #include "apache_request.h"
 #include "TclWeb.h"
 
+/* Rivet config */
+#ifdef HAVE_CONFIG_H
+#include <rivet_config.h>
+#endif
+
 /* init.tcl file relative to the server root directory */
 
 #define RIVET_DIR  RIVET_RIVETLIB_DESTDIR
@@ -41,9 +47,11 @@
 
 /* Configuration options  */
 
-/* If you do not have a threaded Tcl, you can define this to 0.  This
+/* 
+   If you do not have a threaded Tcl, you can define this to 0.  This
    has the effect of running Tcl Init code in the main parent init
-   handler, instead of in child init handlers. */
+   handler, instead of in child init handlers.
+ */
 #ifdef __MINGW32__
 #define THREADED_TCL 1
 #else
@@ -102,6 +110,8 @@ typedef struct _rivet_server_conf {
     apr_table_t*    rivet_user_vars;
     char**          objCacheList;		/* Array of cached objects (for priority handling)  */
     Tcl_HashTable*  objCache;		    /* Objects cache - the key is the script name       */
+    /* this one must go, we keep just to avoid to duplicate the config handling function just
+       to avoid to poke stuff into this field */
     Tcl_Channel*    outchannel;		    /* stuff for buffering output                       */
 } rivet_server_conf;
 
@@ -126,46 +136,56 @@ enum
     done
 };
 
-/* Job types a worker thread is supposed to respond to */
-
-typedef int rivet_job_t;
-enum {
-    request,
-    orderly_exit
-};
-
 /* we need also a place where to store module wide globals */
 
 typedef struct _mod_rivet_globals {
     apr_dso_handle_t*   dso_handle;
     apr_thread_t*       supervisor;
+    int                 server_shutdown;
     apr_thread_cond_t*  job_cond;
     apr_thread_mutex_t* job_mutex;
     apr_array_header_t* exiting;            /* */
+    apr_uint32_t*       threads_count;
+
     apr_thread_mutex_t* pool_mutex;         /* threads commmon pool mutex */
     apr_pool_t*         pool;               /* threads common memory pool */
-    apr_queue_t*        queue;              /* jobs queue */
-    apr_thread_t*       workers[TCL_INTERPS]; /* thread pool ids */
+    apr_queue_t*        queue;              /* jobs queue                 */
+    apr_thread_t*       workers[TCL_INTERPS]; /* thread pool ids          */
+
     server_rec*         server;             /* default host server_rec obj */
+    Tcl_Channel*        outchannel;         /* this is the Rivet channel for prefork MPM */
+
     int                 (*mpm_init)(apr_pool_t* pPool,server_rec* s);
     int                 (*mpm_request)(request_rec*);
-    void                (*mpm_finalize)(void);
+    request_rec*        rivet_panic_request_rec;
+    apr_pool_t*         rivet_panic_pool;
+    server_rec*         rivet_panic_server_rec;
+    apr_status_t        (*mpm_finalize)(void*);
 } mod_rivet_globals;
 
 typedef struct _thread_worker_private {
 
     Tcl_Interp*         interp;
     Tcl_Channel*        channel;
-                                        /* the request_rec and TclWebRequest 
-                                           are copied here to be passed to a 
-                                           channel                      */
+                                        /* the request_rec and TclWebRequest    *
+                                         * are copied here to be passed to a    *
+                                         * channel                              */
     request_rec*        r;			    
     TclWebRequest*      req;
     int                 req_cnt;        /* requests served by thread */
     int                 keep_going;     /* thread loop controlling variable */
+    apr_pool_t*         pool;           /* threads private memory pool */
     
 } rivet_thread_private;
 
+/* Job types a worker thread is supposed to respond to */
+
+typedef int rivet_job_t;
+enum {
+    request,
+    orderly_exit
+};
+
 /* data private to the Apache callback handling the request */
 
 typedef struct _handler_private 
@@ -212,16 +232,18 @@ Tcl_Obj* Rivet_CurrentServerRec ( Tcl_In
 #define RIVET_TEMPLATE      1
 #define RIVET_TCLFILE       2
 
-EXTERN int Rivet_chdir_file (const char *file);
-EXTERN int Rivet_CheckType (request_rec* r);
-EXTERN int Rivet_ExecuteAndCheck (Tcl_Interp *interp, Tcl_Obj *tcl_script_obj, request_rec *req);
-EXTERN int Rivet_ParseExecFile (TclWebRequest *req, char *filename, int toplevel);
+/* these two must go in their own file */
+
+EXTERN int Rivet_ParseExecFile(TclWebRequest *req, char *filename, int toplevel);
 EXTERN int Rivet_ParseExecString (TclWebRequest* req, Tcl_Obj* inbuf);
 
+/* temporary content generation handler */
+
 EXTERN int RivetContent (rivet_thread_private* private);
+
 /* error code set by command 'abort_page' */
 
-#define ABORTPAGE_CODE "ABORTPAGE"
+#define ABORTPAGE_CODE              "ABORTPAGE"
 
 #define MOD_RIVET_QUEUE_SIZE        100
 #define TCL_MAX_CHANNEL_BUFFER_SIZE (1024*1024)

Modified: tcl/rivet/trunk/src/experimental/rivetChannel.h
URL: http://svn.apache.org/viewvc/tcl/rivet/trunk/src/experimental/rivetChannel.h?rev=1616967&r1=1616966&r2=1616967&view=diff
==============================================================================
--- tcl/rivet/trunk/src/experimental/rivetChannel.h (original)
+++ tcl/rivet/trunk/src/experimental/rivetChannel.h Sat Aug  9 15:29:07 2014
@@ -5,8 +5,6 @@
 
 /* Functions for mod_dtcl Tcl output channel .*/
 
-#include "mod_rivet.h"
-
 extern int closeproc(ClientData, Tcl_Interp *);
 extern int inputproc(ClientData, char *, int, int *);
 extern int outputproc(ClientData, char *, int, int *);

Added: tcl/rivet/trunk/src/experimental/rivet_prefork_mpm.c
URL: http://svn.apache.org/viewvc/tcl/rivet/trunk/src/experimental/rivet_prefork_mpm.c?rev=1616967&view=auto
==============================================================================
--- tcl/rivet/trunk/src/experimental/rivet_prefork_mpm.c (added)
+++ tcl/rivet/trunk/src/experimental/rivet_prefork_mpm.c Sat Aug  9 15:29:07 2014
@@ -0,0 +1,541 @@
+/* rivet_prefork_mpm.c: dynamically loaded MPM aware functions for prefork module */
+
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+
+/* Id: */
+
+#include <apr_strings.h>
+
+#include "mod_rivet.h"
+#include "mod_rivet_common.h"
+#include "httpd.h"
+#include "rivetChannel.h"
+#include "apache_config.h"
+
+extern mod_rivet_globals* module_globals;
+extern apr_threadkey_t*   rivet_thread_key;
+extern apr_threadkey_t*   handler_thread_key;
+
+void  Rivet_PerInterpInit(Tcl_Interp* interp, server_rec *s, apr_pool_t *p);
+
+
+/* -----------------------------------------------------------------------------
+ * -- Rivet_SendContent
+ *
+ *   Set things up to execute a file, then execute 
+ *-----------------------------------------------------------------------------
+ */
+
+#define USE_APACHE_RSC
+
+static int
+Rivet_SendContent(rivet_thread_private *private)
+{
+    int errstatus;
+    int retval;
+    int ctype;
+    Tcl_Interp      *interp;
+    static Tcl_Obj  *request_init = NULL;
+    static Tcl_Obj  *request_cleanup = NULL;
+    rivet_interp_globals *globals = NULL;
+    request_rec* r = private->r;
+#ifdef USE_APACHE_RSC
+    rivet_server_conf    *rsc = NULL;
+#else
+    rivet_server_conf    *rdc;
+#endif
+
+    ctype = Rivet_CheckType(r);  
+    if (ctype == CTYPE_NOT_HANDLED) {
+        return DECLINED;
+    }
+
+    //Tcl_MutexLock(&sendMutex);
+
+    /* Set the global request req to know what we are dealing with in
+     * case we have to call the PanicProc. */
+    module_globals->rivet_panic_request_rec = r;
+
+    rsc = Rivet_GetConf(r);
+
+    interp  = rsc->server_interp;
+    globals = Tcl_GetAssocData(interp, "rivet", NULL);
+
+    /* The current TclWebRequest record is assigned here to the thread private data
+       for the channel to read it when actual output will flushed */
+    
+    private->req = globals->req;
+
+    /* Setting this pointer in globals is crucial as by assigning it
+     * we signal to Rivet commands we are processing an HTTP request.
+     * This pointer gets set to NULL just before we leave this function
+     * making possible to invalidate command execution that could depend
+     * on a valid request_rec
+     */
+
+    globals->r = r;
+    globals->srec = r->server;
+
+#ifndef USE_APACHE_RSC
+    if (r->per_dir_config != NULL)
+        rdc = RIVET_SERVER_CONF( r->per_dir_config );
+    else
+        rdc = rsc;
+#endif
+
+    r->allowed |= (1 << M_GET);
+    r->allowed |= (1 << M_POST);
+    r->allowed |= (1 << M_PUT);
+    r->allowed |= (1 << M_DELETE);
+    if (r->method_number != M_GET && 
+        r->method_number != M_POST && 
+        r->method_number != M_PUT && 
+        r->method_number != M_DELETE) {
+
+        retval = DECLINED;
+        goto sendcleanup;
+
+    }
+
+    if (r->finfo.filetype == 0)
+    {
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, APR_EGENERAL, 
+                     r->server,
+                     MODNAME ": File does not exist: %s",
+                     (r->path_info ? (char*)apr_pstrcat(r->pool, r->filename, r->path_info, NULL) : r->filename));
+        retval = HTTP_NOT_FOUND;
+        goto sendcleanup;
+    }
+
+    if ((errstatus = ap_meets_conditions(r)) != OK) {
+        retval = errstatus;
+        goto sendcleanup;
+    }
+
+    /* 
+     * This one is the big catch when it comes to moving towards
+     * Apache 2.0, or one of them, at least.
+     */
+
+    if (Rivet_chdir_file(r->filename) < 0)
+    {
+        /* something went wrong doing chdir into r->filename, we are not specific
+         * at this. We simply emit an internal server error and print a log message
+         */
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, APR_EGENERAL, r->server, 
+                     MODNAME ": Error accessing %s, could not chdir into directory", 
+                     r->filename);
+
+        retval = HTTP_INTERNAL_SERVER_ERROR;
+        goto sendcleanup;
+    }
+
+    /* Initialize this the first time through and keep it around. */
+    if (request_init == NULL) {
+        request_init = Tcl_NewStringObj("::Rivet::initialize_request\n", -1);
+        Tcl_IncrRefCount(request_init);
+    }
+
+    if (Tcl_EvalObjEx(interp, request_init, 0) == TCL_ERROR)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r->server,
+                            MODNAME ": Could not create request namespace (%s)\n" ,
+                            Tcl_GetStringResult(interp));
+        retval = HTTP_INTERNAL_SERVER_ERROR;
+        goto sendcleanup;
+    }
+
+    /* Apache Request stuff */
+
+    TclWeb_InitRequest(globals->req, interp, r);
+    ApacheRequest_set_post_max(globals->req->apachereq, rsc->upload_max);
+    ApacheRequest_set_temp_dir(globals->req->apachereq, rsc->upload_dir);
+
+    /* Let's copy the request data into the thread private record */
+
+    errstatus = ApacheRequest_parse(globals->req->apachereq);
+
+    if (errstatus != OK) {
+        retval = errstatus;
+        goto sendcleanup;
+    }
+
+    if (r->header_only && !rsc->honor_header_only_reqs)
+    {
+        TclWeb_SetHeaderType(DEFAULT_HEADER_TYPE, globals->req);
+        TclWeb_PrintHeaders(globals->req);
+        retval = OK;
+        goto sendcleanup;
+    }
+
+/* 
+ * if we are handling the request we also want to check if a charset 
+ * parameter was set with the content type, e.g. rivet's configuration 
+ * or .htaccess had lines like 
+ *
+ * AddType 'application/x-httpd-rivet; charset=utf-8;' rvt 
+ */
+ 
+/*
+ * if strlen(req->content_type) > strlen([RIVET|TCL]_FILE_CTYPE)
+ * a charset parameters might be there 
+ */
+
+    {
+        int content_type_len = strlen(r->content_type);
+
+        if (((ctype==RIVET_TEMPLATE) && (content_type_len > strlen(RIVET_TEMPLATE_CTYPE))) || \
+             ((ctype==RIVET_TCLFILE) && (content_type_len > strlen(RIVET_TCLFILE_CTYPE)))) {
+            
+            char* charset;
+
+            /* we parse the content type: we are after a 'charset' parameter definition */
+            
+            charset = strstr(r->content_type,"charset");
+            if (charset != NULL) {
+                charset = apr_pstrdup(r->pool,charset);
+
+                /* ther's some freedom about spaces in the AddType lines: let's strip them off */
+
+                apr_collapse_spaces(charset,charset);
+                globals->req->charset = charset;
+            }
+        }
+    }
+
+    if (Rivet_ParseExecFile(globals->req, r->filename, 1) != TCL_OK)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r->server, 
+                     MODNAME ": Error parsing exec file '%s': %s",
+                     r->filename,
+                     Tcl_GetVar(interp, "errorInfo", 0));
+    }
+
+    if (request_cleanup == NULL) {
+        request_cleanup = Tcl_NewStringObj("::Rivet::cleanup_request\n", -1);
+        Tcl_IncrRefCount(request_cleanup);
+    }
+
+    if (Tcl_EvalObjEx(interp, request_cleanup, 0) == TCL_ERROR) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r->server, 
+                     MODNAME ": Error evaluating cleanup request: %s",
+                     Tcl_GetVar(interp, "errorInfo", 0));
+    }
+
+    /* Reset globals */
+    Rivet_CleanupRequest( r );
+
+    retval = OK;
+sendcleanup:
+    globals->req->content_sent = 0;
+
+    globals->page_aborting = 0;
+    if (globals->abort_code != NULL)
+    {
+        Tcl_DecrRefCount(globals->abort_code);
+        globals->abort_code = NULL;
+    }
+
+    /* We reset this pointer to signal we have terminated the request processing */
+
+    globals->r = NULL;
+
+    //Tcl_MutexUnlock(&sendMutex);
+    return retval;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Rivet_InitTclStuff --
+ *
+ *  Initialize the Tcl system - create interpreters, load commands
+ *  and so forth.
+ *
+ * Results:
+ *  None.
+ *
+ * Side Effects:
+ *  None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void 
+Rivet_InitTclStuff(server_rec *s, apr_pool_t *p)
+{
+    rivet_server_conf *rsc  = RIVET_SERVER_CONF( s->module_config );
+    Tcl_Interp *interp      = rsc->server_interp;
+    rivet_server_conf       *myrsc;
+    server_rec              *sr;
+    int interpCount = 0;
+    //extern int ap_max_requests_per_child;
+
+/* This code is run once per child process. In a threaded Tcl builds the forking 
+ * of a child process most likely has not preserved the thread where the Tcl 
+ * notifier runs. The Notifier should have been restarted by one the 
+ * pthread_atfork callbacks (setup in Tcl >= 8.5.14 and Tcl >= 8.6.1). In
+ * case pthread_atfork is not supported we unconditionally call Tcl_InitNotifier
+ * hoping for the best (Bug #55153)      
+ */
+
+#if !defined(HAVE_PTHREAD_ATFORK)
+    Tcl_InitNotifier();
+#endif
+
+    if (rsc->rivet_global_init_script != NULL) {
+        if (Tcl_EvalObjEx(interp, rsc->rivet_global_init_script, 0) != TCL_OK)
+        {
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
+                         MODNAME ": Error running GlobalInitScript '%s': %s",
+                         Tcl_GetString(rsc->rivet_global_init_script),
+                         Tcl_GetVar(interp, "errorInfo", 0));
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 
+                         MODNAME ": GlobalInitScript '%s' successful",
+                         Tcl_GetString(rsc->rivet_global_init_script));
+        }
+    }
+
+    for (sr = s; sr; sr = sr->next)
+    {
+        myrsc = RIVET_SERVER_CONF(sr->module_config);
+
+        /* We only have a different rivet_server_conf if MergeConfig
+         * was called. We really need a separate one for each server,
+         * so we go ahead and create one here, if necessary. */
+
+        if (sr != s && myrsc == rsc) {
+            myrsc = RIVET_NEW_CONF(p);
+            ap_set_module_config(sr->module_config, &rivet_module, myrsc);
+            Rivet_CopyConfig( rsc, myrsc );
+        }
+
+        // myrsc->outchannel = rsc->outchannel;
+
+        /* This sets up slave interpreters for other virtual hosts. */
+        if (sr != s) /* not the first one  */
+        {
+            if (rsc->separate_virtual_interps != 0) 
+            {
+                char *slavename = (char*) apr_psprintf (p, "%s_%d_%d", 
+                        sr->server_hostname, 
+                        sr->port,
+                        interpCount++);
+
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 
+                            MODNAME 
+                            ": Rivet_InitTclStuff: creating slave interpreter '%s', "\
+                            "hostname '%s', port '%d', separate interpreters %d",
+                            slavename, sr->server_hostname, sr->port, 
+                            rsc->separate_virtual_interps);
+
+                /* Separate virtual interps. */
+                myrsc->server_interp = Tcl_CreateSlave(interp, slavename, 0);
+                if (myrsc->server_interp == NULL) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                                 MODNAME ": slave interp create failed: %s",
+                                 Tcl_GetStringResult(interp) );
+                    exit(1);
+                }
+                Rivet_PerInterpInit(myrsc->server_interp, s, p);
+            } else {
+                myrsc->server_interp = rsc->server_interp;
+            }
+
+            /* Since these things are global, we copy them into the
+             * rivet_server_conf struct. */
+            myrsc->cache_size = rsc->cache_size;
+            myrsc->cache_free = rsc->cache_free;
+            myrsc->objCache = rsc->objCache;
+            myrsc->objCacheList = rsc->objCacheList;
+        }
+        myrsc->server_name = (char*)apr_pstrdup(p, sr->server_hostname);
+    }
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Rivet_ChildHandlers --
+ *
+ *  Handles, depending on the situation, the scripts for the init
+ *  and exit handlers.
+ *
+ * Results:
+ *  None.
+ *
+ * Side Effects:
+ *  Runs the rivet_child_init/exit_script scripts.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void 
+Rivet_ChildHandlers(server_rec *s, int init)
+{
+    server_rec *sr;
+    rivet_server_conf *rsc;
+    rivet_server_conf *top;
+    void *function;
+    void *parentfunction;
+    char *errmsg;
+
+    top = RIVET_SERVER_CONF(s->module_config);
+    if (init == 1) {
+        parentfunction = top->rivet_child_init_script;
+        errmsg = MODNAME ": Error in Child init script: %s";
+        //errmsg = (char *) apr_pstrdup(p, "Error in child init script: %s");
+    } else {
+        parentfunction = top->rivet_child_exit_script;
+        errmsg = MODNAME ": Error in Child exit script: %s";
+        //errmsg = (char *) apr_pstrdup(p, "Error in child exit script: %s");
+    }
+
+    for (sr = s; sr; sr = sr->next)
+    {
+        rsc = RIVET_SERVER_CONF(sr->module_config);
+        function = init ? rsc->rivet_child_init_script : rsc->rivet_child_exit_script;
+
+        if (!init && sr == s) {
+            Tcl_Preserve(rsc->server_interp);
+        }
+
+        /* Execute it if it exists and it's the top level, separate
+         * virtual interps are turned on, or it's different than the
+         * main script. 
+         */
+
+        if  (function &&
+             ( sr == s || rsc->separate_virtual_interps || function != parentfunction))
+        {
+            rivet_interp_globals* globals = Tcl_GetAssocData( rsc->server_interp, "rivet", NULL );
+            Tcl_Preserve (rsc->server_interp);
+
+            globals->srec = sr;
+            if (Tcl_EvalObjEx(rsc->server_interp,function, 0) != TCL_OK) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                             errmsg, Tcl_GetString(function));
+                ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
+                             "errorCode: %s",
+                        Tcl_GetVar(rsc->server_interp, "errorCode", 0));
+                ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
+                             "errorInfo: %s",
+                        Tcl_GetVar(rsc->server_interp, "errorInfo", 0));
+            }
+            Tcl_Release (rsc->server_interp);
+        }
+
+        if (!init) 
+        {
+            Tcl_UnregisterChannel(rsc->server_interp,*(module_globals->outchannel));
+        }
+
+    }
+
+    if (!init) {
+
+    /*
+     * Upon child exit we delete the master interpreter before the 
+     * caller invokes Tcl_Finalize.  Even if we're running separate
+     * virtual interpreters, we don't delete the slaves
+     * as deleting the master implicitly deletes its slave interpreters.
+     */
+
+        rsc = RIVET_SERVER_CONF(s->module_config);
+        if (!Tcl_InterpDeleted (rsc->server_interp)) {
+            Tcl_DeleteInterp(rsc->server_interp);
+        }
+        Tcl_Release (rsc->server_interp);
+    }
+}
+
+apr_status_t Rivet_MPM_Finalize (void* data)
+{
+    server_rec* s = (server_rec*) data;
+
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, s, MODNAME ": Running ChildExit handler");
+    Rivet_ChildHandlers(s, 0);
+
+    apr_threadkey_private_delete (rivet_thread_key);
+    return OK;
+}
+
+void Rivet_MPM_Init (apr_pool_t* pool, server_rec* server)
+{
+    rivet_thread_private*   private;
+
+    apr_thread_mutex_create(&module_globals->pool_mutex, APR_THREAD_MUTEX_UNNESTED, pool);
+    //apr_threadkey_private_create (&rivet_thread_key,NULL, pool);
+
+    apr_thread_mutex_lock(module_globals->pool_mutex);
+    if (apr_pool_create(&module_globals->pool, pool) != APR_SUCCESS) 
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, server, 
+                     MODNAME ": could not initialize mod_rivet private pool");
+        exit(1);
+    }
+    apr_thread_mutex_unlock(module_globals->pool_mutex);
+
+    if (apr_threadkey_private_get ((void **)&private,rivet_thread_key) != APR_SUCCESS)
+    {
+        exit(1);
+    } 
+    else 
+    {
+
+        if (private == NULL)
+        {
+            apr_thread_mutex_lock(module_globals->pool_mutex);
+            private             = apr_palloc (module_globals->pool,sizeof(rivet_thread_private));
+            apr_thread_mutex_unlock(module_globals->pool_mutex);
+
+            private->channel    = module_globals->outchannel;
+            private->req_cnt    = 0;
+            private->keep_going = 1;
+            private->r          = NULL;
+            private->req        = NULL;
+            private->interp     = NULL;
+
+            apr_threadkey_private_set (private,rivet_thread_key);
+        }
+
+    }
+
+    /* the cache will live as long as the child process' pool */
+
+    Rivet_CreateCache(s,module_globals->pool);
+
+    Rivet_InitTclStuff(server, pool);
+    Rivet_ChildHandlers(server, 1);
+
+}
+
+int Rivet_MPM_Request (request_rec* r)
+{
+    rivet_thread_private*   private;
+
+    ap_assert (apr_threadkey_private_get ((void **)&private,rivet_thread_key) == APR_SUCCESS);
+
+    private->r = r;
+
+    return Rivet_SendContent(private);
+}
+

Propchange: tcl/rivet/trunk/src/experimental/rivet_prefork_mpm.c
------------------------------------------------------------------------------
    svn:keywords = Id

Modified: tcl/rivet/trunk/src/experimental/rivet_worker_mpm.c
URL: http://svn.apache.org/viewvc/tcl/rivet/trunk/src/experimental/rivet_worker_mpm.c?rev=1616967&r1=1616966&r2=1616967&view=diff
==============================================================================
--- tcl/rivet/trunk/src/experimental/rivet_worker_mpm.c (original)
+++ tcl/rivet/trunk/src/experimental/rivet_worker_mpm.c Sat Aug  9 15:29:07 2014
@@ -1,21 +1,120 @@
+/* rivet_worker_mpm.c: dynamically loaded MPM aware functions for threaded MPM */
+
+/*
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+ */
+
+/* Id: */
 
 #include "mod_rivet.h"
 #include "httpd.h"
 #include "rivetChannel.h"
 
-extern mod_rivet_globals module_globals;
-extern apr_threadkey_t*        rivet_thread_key;
-extern apr_threadkey_t*        handler_thread_key;
+extern mod_rivet_globals* module_globals;
+extern apr_threadkey_t*  rivet_thread_key;
+extern apr_threadkey_t*  handler_thread_key;
 
 static void processor_cleanup (void *data)
 {
     rivet_thread_private*   private = (rivet_thread_private *) data;
+    rivet_server_conf*      rsc;
 
     Tcl_UnregisterChannel(private->interp,*private->channel);
-    Tcl_DeleteInterp(private->interp);
 
-    ap_log_error(APLOG_MARK, APLOG_INFO, 0, module_globals.server, 
+    rsc  = RIVET_SERVER_CONF( private->server->module_config );
+    Tcl_DeleteHashTable(rsc->objCache);
+    Tcl_DeleteInterp(private->interp);
+    
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, module_globals->server, 
             "Thread exiting after %d requests served", private->req_cnt);
+
+    apr_pool_destroy(private->pool);
+}
+
+/*
+ * I think we are correctly allocating from a thread private pool as
+ * configuration record must be freed upon thread exit
+ *
+ */
+
+static void Rivet_InitVirtualHostsInterps (server_rec *server, apr_pool_t *p)
+{
+    rivet_server_conf*  rsc  = RIVET_SERVER_CONF( server->module_config );
+    rivet_server_conf*  myrsc; 
+    server_rec*         sr;
+    int                 interpCount = 0;
+
+    for (sr = s; sr; sr = sr->next)
+    {
+        myrsc = RIVET_SERVER_CONF(sr->module_config);
+
+        /* We only have a different rivet_server_conf if MergeConfig
+         * was called. We really need a separate one for each server,
+         * so we go ahead and create one here, if necessary. */
+
+        if (sr != s && myrsc == rsc) {
+            myrsc = RIVET_NEW_CONF(p);
+            ap_set_module_config(sr->module_config, &rivet_module, myrsc);
+            Rivet_CopyConfig( rsc, myrsc );
+        }
+
+        // myrsc->outchannel = rsc->outchannel;
+
+        /* This sets up slave interpreters for other virtual hosts. */
+        if (sr != s) /* not the first one  */
+        {
+            if (rsc->separate_virtual_interps != 0) 
+            {
+                char *slavename = (char*) apr_psprintf (p, "%s_%d_%d", 
+                        sr->server_hostname, 
+                        sr->port,
+                        interpCount++);
+
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, 
+                            MODNAME 
+                            ": Rivet_InitTclStuff: creating slave interpreter '%s', "\
+                            "hostname '%s', port '%d', separate interpreters %d",
+                            slavename, sr->server_hostname, sr->port, 
+                            rsc->separate_virtual_interps);
+
+                /* Separate virtual interps. */
+                myrsc->server_interp = Tcl_CreateSlave(interp, slavename, 0);
+                if (myrsc->server_interp == NULL) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                                 MODNAME ": slave interp create failed: %s",
+                                 Tcl_GetStringResult(interp) );
+                    exit(1);
+                }
+                Rivet_PerInterpInit(myrsc->server_interp, s, p);
+            } else {
+                myrsc->server_interp = rsc->server_interp;
+            }
+
+            /* Since these things are global, we copy them into the
+             * rivet_server_conf struct. */
+            myrsc->cache_size = rsc->cache_size;
+            myrsc->cache_free = rsc->cache_free;
+            myrsc->objCache = rsc->objCache;
+            myrsc->objCacheList = rsc->objCacheList;
+        }
+        myrsc->server_name = (char*)apr_pstrdup(p, sr->server_hostname);
+    }
+
 }
 
 static void* APR_THREAD_FUNC request_processor (apr_thread_t *thd, void *data)
@@ -24,6 +123,10 @@ static void* APR_THREAD_FUNC request_pro
     rivet_thread_private*   private;
     Tcl_Channel             *outchannel;		    /* stuff for buffering output */
     apr_threadkey_t*        thread_key = (apr_threadkey_t *) data;  
+    rivet_server_conf*      rsc;
+    server_rec*             server;
+
+    server = module_globals->server;
 
     if (apr_threadkey_private_get ((void **)&private,thread_key) != APR_SUCCESS)
     {
@@ -34,16 +137,36 @@ static void* APR_THREAD_FUNC request_pro
 
         if (private == NULL)
         {
-            apr_thread_mutex_lock(module_globals.pool_mutex);
+            apr_thread_mutex_lock(module_globals->pool_mutex);
+            private             = apr_palloc (module_globals->pool,sizeof(*private));
+            private->channel    = apr_pcalloc (module_globals->pool,sizeof(Tcl_Channel));
+            apr_thread_mutex_unlock(module_globals->pool_mutex);
 
-            private             = apr_palloc (module_globals.pool,sizeof(*private));
-            private->channel    = apr_pcalloc (module_globals.pool, sizeof(Tcl_Channel));
             private->req_cnt    = 0;
             private->keep_going = 2;
-            apr_thread_mutex_unlock(module_globals.pool_mutex);
+            private->r          = NULL;
+            private->req        = NULL;
+            private->interp     = NULL;
+
+            if (apr_pool_create(&private->pool, NULL) != APR_SUCCESS) 
+            {
+                ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, server, 
+                             MODNAME ": could not initialize thread private pool");
+                apr_thread_exit(thd,APR_SUCCESS);
+                return NULL;
+                exit(1);
+            }
+            apr_threadkey_private_set (private,rivet_thread_key);
+
         }
 
     }
+    
+        /*
+         * From here on stuff differs substantially wrt rivet_mpm_prefork
+         * We cannot draw on Rivet_InitTclStuff that knows nothing about threads
+         * private data.
+         */
 
     interp = Tcl_CreateInterp();
     if (Tcl_Init(interp) == TCL_ERROR)
@@ -52,6 +175,13 @@ static void* APR_THREAD_FUNC request_pro
     }
     private->interp  = interp;
 
+    /* We will allocate structures (Tcl_HashTable) from this cache but they cannot
+     * survive a thread termination, thus we need to implement a method for releasing
+     * them in processor_cleanup
+     */
+
+    Rivet_CreateCache(server,private->pool);
+
     outchannel  = private->channel;
     *outchannel = Tcl_CreateChannel(&RivetChan, "apacheout", rivet_thread_key, TCL_WRITABLE);
 
@@ -65,12 +195,40 @@ static void* APR_THREAD_FUNC request_pro
          */
 
     Tcl_SetChannelBufferSize (*outchannel, TCL_MAX_CHANNEL_BUFFER_SIZE);
-    apr_threadkey_private_set (private,rivet_thread_key);
+
+        /* So far nothing differs much with what we did for the prefork bridge */
+    
+        /* At this stage we have toset up private interpreters for virtual hosts (if needed)
+         * we assume the server_rec stored in the module globals can be used to retrieve the
+         * reference to the root interpreter configuration and to the rivet global script */
+
+    rsc  = RIVET_SERVER_CONF( server->module_config );
+
+    if (rsc->rivet_global_init_script != NULL) {
+        if (Tcl_EvalObjEx(private->interp, rsc->rivet_global_init_script, 0) != TCL_OK)
+        {
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, server, 
+                         MODNAME ": Error running GlobalInitScript '%s': %s",
+                         Tcl_GetString(rsc->rivet_global_init_script),
+                         Tcl_GetVar(private->interp, "errorInfo", 0));
+        } else {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, server, 
+                         MODNAME ": GlobalInitScript '%s' successful",
+                         Tcl_GetString(rsc->rivet_global_init_script));
+        }
+    }
+
+    Rivet_InitVirtualHostsInterps (server, private->pool)
+
+
+        /* eventually we increment the number of active threads */
+
+    apr_atomic_inc32(module_globals->threads_count);
     do
     {
         apr_status_t        rv;
         void*               v;
-        apr_queue_t*        q = module_globals.queue;
+        apr_queue_t*        q = module_globals->queue;
         handler_private*    request_obj;
 
         do {
@@ -97,9 +255,8 @@ static void* APR_THREAD_FUNC request_pro
         request_obj = (handler_private *) v;
         if (request_obj->job_type == orderly_exit)
         {
-            ap_log_error(APLOG_MARK, APLOG_INFO, APR_EGENERAL, module_globals.server, "consumer thread exit");
-            apr_thread_exit(thd, rv);
-            return NULL;
+            private->keep_going = 0;
+            continue;
         }
 
         TclWeb_InitRequest(request_obj->req, private->interp, request_obj->r);
@@ -114,17 +271,20 @@ static void* APR_THREAD_FUNC request_pro
         apr_thread_cond_signal(request_obj->cond);
         apr_thread_mutex_unlock(request_obj->mutex);
     
-        apr_threadkey_private_set (private,rivet_thread_key);
         private->req_cnt++;
 
     } while (private->keep_going-- > 0);
+            
+    ap_log_error(APLOG_MARK, APLOG_INFO, 0, module_globals->server, "processor thread orlderly exit");
+
+    apr_thread_mutex_lock(module_globals->job_mutex);
+    *(apr_thread_t **) apr_array_push(module_globals->exiting) = thd;
+    apr_thread_cond_signal(module_globals->job_cond);
+    apr_thread_mutex_unlock(module_globals->job_mutex);
 
-    apr_thread_mutex_lock(module_globals.job_mutex);
-    *(apr_thread_t **) apr_array_push(module_globals.exiting) = thd;
-    apr_thread_cond_signal(module_globals.job_cond);
-    apr_thread_mutex_unlock(module_globals.job_mutex);
+    /* the counter of active threads has to be decremented */
 
-    //ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL, s, MODNAME ": thread %lp exits", thd);
+    apr_atomic_dec32(module_globals->threads_count);
 
     apr_thread_exit(thd,APR_SUCCESS);
     return NULL;
@@ -132,7 +292,7 @@ static void* APR_THREAD_FUNC request_pro
 
 static apr_status_t create_worker_thread (apr_thread_t** thd)
 {
-    return apr_thread_create(thd, NULL, request_processor, module_globals.queue, module_globals.pool);
+    return apr_thread_create(thd, NULL, request_processor, module_globals->queue, module_globals->pool);
 }
 
 static void start_thread_pool (int nthreads)
@@ -143,13 +303,13 @@ static void start_thread_pool (int nthre
     {
         apr_status_t rv;
 
-        rv = create_worker_thread( &module_globals.workers[i]);
+        rv = create_worker_thread( &module_globals->workers[i]);
 
         if (rv != APR_SUCCESS) {
             char    errorbuf[512];
 
             apr_strerror(rv, errorbuf,200);
-            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, module_globals.server, 
+            ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, module_globals->server, 
                 "Error starting request_processor thread (%d) rv=%d:%s\n",i,rv,errorbuf);
             exit(1);
 
@@ -162,37 +322,37 @@ static void* APR_THREAD_FUNC supervisor_
     server_rec* s = (server_rec *)data;
 
     start_thread_pool(TCL_INTERPS);
-    while (1)
+    do
     {
         apr_thread_t*   p;
       
-        apr_thread_mutex_lock(module_globals.job_mutex);
-        while (apr_is_empty_array(module_globals.exiting))
+        apr_thread_mutex_lock(module_globals->job_mutex);
+        while (apr_is_empty_array(module_globals->exiting) && !module_globals->server_shutdown)
         {
-            apr_thread_cond_wait(module_globals.job_cond,module_globals.job_mutex);
+            apr_thread_cond_wait(module_globals->job_cond,module_globals->job_mutex);
         }
 
-        while (!apr_is_empty_array(module_globals.exiting))
+        while (!apr_is_empty_array(module_globals->exiting) && !module_globals->server_shutdown)
         {
             int i;
-            p = *(apr_thread_t **)apr_array_pop(module_globals.exiting);
+            p = *(apr_thread_t **)apr_array_pop(module_globals->exiting);
             
-            for (i = 0; i < TCL_INTERPS; i++)
+            for (i = 0; (i < TCL_INTERPS) && !module_globals->server_shutdown; i++)
             {
-                if (p == module_globals.workers[i])
+                if (p == module_globals->workers[i])
                 {
                     apr_status_t rv;
 
-                    ap_log_error(APLOG_MARK,APLOG_INFO,0,s,"thread %d notifies orderly exit",i);
+                    ap_log_error(APLOG_MARK,APLOG_INFO,APR_EGENERAL,s,"thread %d notifies orderly exit",i);
 
                     /* terminated thread restart */
 
-                    rv = create_worker_thread (&module_globals.workers[i]);
+                    rv = create_worker_thread (&module_globals->workers[i]);
                     if (rv != APR_SUCCESS) {
-                        char    errorbuf[512];
+                        char errorbuf[512];
 
                         apr_strerror(rv,errorbuf,200);
-                        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, module_globals.server, 
+                        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s, 
                             "Error starting request_processor thread (%d) rv=%d:%s",i,rv,errorbuf);
                         exit(1);
 
@@ -202,9 +362,32 @@ static void* APR_THREAD_FUNC supervisor_
                 }
             }       
         }   
-        apr_thread_mutex_unlock(module_globals.job_mutex);
-    }
+        apr_thread_mutex_unlock(module_globals->job_mutex);
+    }  while (!module_globals->server_shutdown);
+    
+    {
+        handler_private* job;
+        int count;
+        int i;
+
+        apr_thread_mutex_lock(module_globals->pool_mutex);
+        job = (handler_private *) apr_palloc(module_globals->pool,sizeof(handler_private));
+        apr_thread_mutex_unlock(module_globals->pool_mutex);
+        job->job_type = orderly_exit;
+
+        i = 5;
+        count = (int) apr_atomic_read32(module_globals->threads_count);
+        do 
+        {
+            
+            for (i = 0; i < count; i++) { apr_queue_push(module_globals->queue,job); }
+            apr_sleep(1000000);
+            count = (int) apr_atomic_read32(module_globals->threads_count);
+
+        } while ((count > 0) && (i-- > 0));
 
+    }
+    apr_thread_exit(thd,APR_SUCCESS);
     return NULL;
 }
 
@@ -212,33 +395,33 @@ void Rivet_MPM_Init (apr_pool_t* pool, s
 {
     apr_status_t rv;
 
-    apr_thread_mutex_create(&module_globals.job_mutex, APR_THREAD_MUTEX_UNNESTED, pool);
-    apr_thread_cond_create(&module_globals.job_cond, pool);
-    module_globals.exiting = apr_array_make(pool,100,sizeof(apr_thread_t*));
-
-    apr_thread_mutex_create(&module_globals.pool_mutex, APR_THREAD_MUTEX_UNNESTED, pool);
+    apr_thread_mutex_create(&module_globals->job_mutex, APR_THREAD_MUTEX_UNNESTED, pool);
+    apr_thread_cond_create(&module_globals->job_cond, pool);
+    apr_thread_mutex_create(&module_globals->pool_mutex, APR_THREAD_MUTEX_UNNESTED, pool);
     apr_threadkey_private_create (&rivet_thread_key, processor_cleanup, pool);
     apr_threadkey_private_create (&handler_thread_key, NULL, pool);
-    module_globals.server = server;
 
-    apr_thread_mutex_lock(module_globals.pool_mutex);
-    if (apr_pool_create(&module_globals.pool, NULL) != APR_SUCCESS) 
+    module_globals->exiting = apr_array_make(pool,100,sizeof(apr_thread_t*));
+    module_globals->server = server;
+
+    apr_thread_mutex_lock(module_globals->pool_mutex);
+    if (apr_pool_create(&module_globals->pool, NULL) != APR_SUCCESS) 
     {
         ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, server, 
                      MODNAME ": could not initialize mod_rivet private pool");
         exit(1);
     }
-    apr_thread_mutex_unlock(module_globals.pool_mutex);
+    apr_thread_mutex_unlock(module_globals->pool_mutex);
 
-    if (apr_queue_create(&module_globals.queue, MOD_RIVET_QUEUE_SIZE, module_globals.pool) != APR_SUCCESS)
+    if (apr_queue_create(&module_globals->queue, MOD_RIVET_QUEUE_SIZE, module_globals->pool) != APR_SUCCESS)
     {
         ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, server, 
                      MODNAME ": could not initialize mod_rivet request queue");
         exit(1);
     }
 
-    rv = apr_thread_create( &module_globals.supervisor, NULL, 
-                            supervisor_thread, server, module_globals.pool);
+    rv = apr_thread_create( &module_globals->supervisor, NULL, 
+                            supervisor_thread, server, module_globals->pool);
 
     if (rv != APR_SUCCESS) {
         char    errorbuf[512];
@@ -248,7 +431,6 @@ void Rivet_MPM_Init (apr_pool_t* pool, s
                      MODNAME "Error starting supervisor thread rv=%d:%s\n",rv,errorbuf);
         exit(1);
     }
-
 }
 
 int Rivet_MPM_Request (request_rec* r)
@@ -266,12 +448,12 @@ int Rivet_MPM_Request (request_rec* r)
 
         if (private == NULL)
         {
-            apr_thread_mutex_lock(module_globals.pool_mutex);
-            private         = apr_palloc(module_globals.pool,sizeof(handler_private));
-            private->req    = TclWeb_NewRequestObject (module_globals.pool);
-            apr_thread_cond_create(&(private->cond), module_globals.pool);
-            apr_thread_mutex_create(&(private->mutex), APR_THREAD_MUTEX_UNNESTED, module_globals.pool);
-            apr_thread_mutex_unlock(module_globals.pool_mutex);
+            apr_thread_mutex_lock(module_globals->pool_mutex);
+            private         = apr_palloc(module_globals->pool,sizeof(handler_private));
+            private->req    = TclWeb_NewRequestObject (module_globals->pool);
+            apr_thread_cond_create(&(private->cond), module_globals->pool);
+            apr_thread_mutex_create(&(private->mutex), APR_THREAD_MUTEX_UNNESTED, module_globals->pool);
+            apr_thread_mutex_unlock(module_globals->pool_mutex);
             private->job_type = request;
         }
 
@@ -285,7 +467,7 @@ int Rivet_MPM_Request (request_rec* r)
     do
     {
 
-        rv = apr_queue_push(module_globals.queue,private);
+        rv = apr_queue_push(module_globals->queue,private);
         if (rv != APR_SUCCESS)
         {
             apr_sleep(100000);
@@ -303,7 +485,25 @@ int Rivet_MPM_Request (request_rec* r)
     return private->code;
 }
 
-void Rivet_MPM_Finalize (void)
+apr_status_t Rivet_MPM_Finalize (void* data)
 {
+    apr_status_t  rv;
+    apr_status_t  thread_status;
+    server_rec* s = (server_rec*) data;
+    
+    module_globals->server_shutdown = 1;
+    apr_thread_mutex_lock(module_globals->job_mutex);
+    apr_thread_cond_signal(module_globals->job_cond);
+    apr_thread_mutex_unlock(module_globals->job_mutex);
+
+    rv = apr_thread_join (&thread_status,module_globals->supervisor);
+    if (rv != APR_SUCCESS)
+    {
+        ap_log_error(APLOG_MARK, APLOG_ERR, APR_EGENERAL, s,
+                     MODNAME ": Error joining supervisor thread");
+    }
 
+    apr_threadkey_private_delete (rivet_thread_key);
+    apr_threadkey_private_delete (handler_thread_key);
+    return OK;
 }

Propchange: tcl/rivet/trunk/src/experimental/rivet_worker_mpm.c
------------------------------------------------------------------------------
    svn:keywords = Id



---------------------------------------------------------------------
To unsubscribe, e-mail: site-cvs-unsubscribe@tcl.apache.org
For additional commands, e-mail: site-cvs-help@tcl.apache.org