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