merge master into uploadcmdfix
diff --git a/ChangeLog b/ChangeLog
index 6b635a5..381c7c6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,14 @@
     * src/mod_rivet_ng/rivetCore.c: update comments to upload command
     * tests: changed upload file test
 
+2020-04-02 Massimo Manghi <mxmanghi@apache.org>
+    * doc/xml/formbroker.xml: fixed examples
+    * src/mod_rivet_ng/rivet_lazy_mpm.c: resolved access to shared counter
+    that was causing segfaults when child process was terminated
+
+2020-01-19 Massimo Manghi <mxmanghi@apache.org>
+    * doc/xml/commands.xml: remove entry for command incr0 (dropped in 3.0)
+
 2019-12-30 Massimo Manghi <mxmanghi@apache.org>
     * src/mod_rivet_ng/rivetCore.c: fixed bug in 'upload filename' that
     caused segfaults instead of Tcl errors when called without arguments
diff --git a/doc/rivet.css b/doc/rivet.css
index 71ae239..c407a8c 100644
--- a/doc/rivet.css
+++ b/doc/rivet.css
@@ -31,7 +31,7 @@
         font-family: monospace;
         white-space: pre;
         width:  95%;
-        background-color: #f7e9c6;
+        background-color: #e8ecf2;
         border: solid;
         color:  #000000;
         border-color: #a3b1bc;
@@ -83,8 +83,8 @@
 {       
         COLOR: #ffffff ;
         font-style: italic;
-        border: solid 3px #e8a34b;
-        background-color: #593c27;
+        border: solid 3px #1d252b;
+        background-color: #a3b1bc;
         PADDING: 0.5em;
 }
 
@@ -139,7 +139,7 @@
         font-weight: bold;
         margin-top: 10px;
         color: #ffffff ;
-        background-color: #f7e9c6;
+        background-color: #e8ecf2;
         border: solid 1px #a3b1bc;
         padding: 1px
 }
@@ -154,15 +154,15 @@
         font-size: 12px;
 }
 
-/*/*/
-
+/* 
 A{
     color: maroon;
 }
 
-a:visited {
+A:visited {
     color: darkgreen;
 }
+*/
 
 BODY P {
 
@@ -272,7 +272,7 @@
 
 DIV.NAVFOOTER {
         color: #000000;
-        background-color: #f7e9c6;
+        background-color: #e8ecf2;
         padding: 5px;
         margin-top: 10px;
         width: 100%;
@@ -281,7 +281,7 @@
 
 DIV.NUKEFOOTER {
         color: #000000;
-        background-color: #f7e9c6;
+        background-color: #e8ecf2;
         padding: 5px;
         margin-top: 10px;
         width: 100%;
@@ -290,7 +290,7 @@
 
 DIV.NAVHEADER {
         color: #000000;
-        background-color: #f7e9c6;
+        background-color: #e8ecf2;
         padding: 5px;
         margin-bottom: 10px;
         width: 100%;
@@ -306,7 +306,7 @@
         padding-left: 10px;
         padding-right: 10px;
         color: #000000;
-        background-color: #f7e9c6;
+        background-color: #e8ecf2;
 }
 DIV.EXAMPLE {
         border: thin dotted #22AA22;
@@ -323,8 +323,8 @@
      /*    list-style: url("images/tux-bullet.png") disc; */
     }
 
-table.namespaces {
-	border-collapse: collapse;
+.namespaces {
+	border: 1px solid black;
 }
 
 .namespaces td {
@@ -333,14 +333,11 @@
 }
 
 .namespaces thead {
-    background-color:   #5e3c27;
-    color:              white;
-    font-size:          1em;
-    text-decoration:    none;
+	background-color: #aaf;
 }
 
 .namespaces tr.init {
-	background-color: #bb8766;	
+	background-color: #e88;	
 }
 
 .namespaces tr.childinit {
@@ -348,7 +345,7 @@
 }
 
 .namespaces tr.processing {
-	background-color: #bb8766;	
+	background-color: #e88;	
 }
 	
 .namespaces tr.childexit {
@@ -358,10 +355,8 @@
 table.directives {
 	border-collapse: collapse;
 }
-
 .directives thead {
-    background-color:   #5e3c27;
-    color:              white;
+    background-color:   #bbf;
     font-size:          1em;
     text-decoration:    none;
 }
@@ -371,30 +366,29 @@
 }
 
 .directives tbody tr > :first-child {
-    background-color:   #bb8766;
-    color:              white;
+    background-color:   #eee;
     padding-left:       2em;
     padding-right:      2em;
     text-align:         left;
 }
 
 .directives tbody tr > :last-child {
-   text-decoration:    	none;
-   font-weight: 		normal;
-   font-size: 			small;
-   border-left:			1px solid black;
-   text-align:         	left;
+   text-decoration:    	    none;
+   font-weight: 			normal;
+	font-size: 				small;
+	border-left:			1px solid black;
+	text-align:         	left;
 }
 
 .directives tbody tr td {
    text-align:          center;
    text-decoration:     none;
    font-weight: 		normal;
-   padding-left:		1em;
-   padding-right: 		1em;
-   padding-bottom: 	    4px;
-   padding-top: 		4px;
-   border-bottom:		1px solid black;
+	padding-left:		1em;
+	padding-right: 		1em;
+	padding-bottom: 	4px;
+	padding-top: 		4px;
+	border-bottom:		1px solid black;
 }
 
 .note td {
diff --git a/doc/xml/commands.xml b/doc/xml/commands.xml
index ddbf976..0b95309 100644
--- a/doc/xml/commands.xml
+++ b/doc/xml/commands.xml
@@ -953,38 +953,6 @@
 	    </refsect1>
 	</refentry>
 
-	<refentry id="incr0">
-	    <refnamediv>
-            <refname>incr0</refname>
-            <refpurpose>increment a variable or set it to 1 if nonexistent.</refpurpose>
-	    </refnamediv>
-
-	    <refsynopsisdiv>
-            <cmdsynopsis>
-                <command>incr0</command>
-                <arg><replaceable>varname</replaceable></arg>
-                <arg><replaceable>num</replaceable></arg>
-            </cmdsynopsis>
-	    </refsynopsisdiv>
-	    <refsect1>
-            <title>Description</title>
-            <para>
-                Increment a variable
-                <option><replaceable>varname</replaceable></option> by
-                <option><replaceable>num</replaceable></option>.  If the
-                variable doesn't exist, create it instead of returning an
-                error.
-            </para>
-            <note> 
-                incr0 functionality is provided by the native <command>incr</command> in 
-                Tcl &gt;= 8.5, therefore this command is deprecated and kept as an
-                interpreter alias only for compatibility. As such <command>incr0</command> 
-                wasn't moved to the ::rivet namespace and
-                it will be removed in future versions of Rivet.
-            </note>
-	    </refsect1>
-	</refentry>
-
 	<refentry id="inspect">
 	    <refnamediv>
             <refname>inspect</refname>
diff --git a/doc/xml/formbroker.xml b/doc/xml/formbroker.xml
index 3160f2b..6ea64ab 100644
--- a/doc/xml/formbroker.xml
+++ b/doc/xml/formbroker.xml
@@ -127,7 +127,8 @@
                         Example of form data validation (assuming ::rivet::load_response is loading the array <emphasis>response</emphasis>
                         with data taken from a form non displayed here)
                      </para>
-                     <programlisting>% set fbroker [::FormBroker create {var1 integer} {var2 unsigned} {var3 string} {var4 integer bounds {-10 100}}]
+                     <programlisting>% package require formbroker
+% set fbroker [::FormBroker create {var1 integer} {var2 unsigned} {var3 string} {var4 integer bounds {-10 100}}]
 ::FormBroker::form0
 
 % ::rivet::load_response
@@ -243,10 +244,11 @@
                         the 'constrain' attribute forced the truncation of the string. 
                         In fact this applies also to the integer and unsigned values 
                      </para>
-<programlisting>% set fbroker [::FormBroker create {var1 integer bounds 10 constrain} \
-			            {var2 unsigned constrain} \
-			            {var3 string length 10 constrain} \
-			            {var4 integer bounds {-10 100} constrain}]
+<programlisting>% package require formbroker
+% set fbroker [::FormBroker create {var1 integer bounds 10 constrain} \
+                                   {var2 unsigned constrain} \
+                                   {var3 string length 10 constrain} \
+                                   {var4 integer bounds {-10 100} constrain}]
 ::FormBroker::form0
 % ::rivet::load_response
 % parray response
@@ -287,10 +289,11 @@
                         such is a way to create form default arrays to initialize forms created with
                         the <xref linkend="form_package">form</xref> package.
                      </para>
-<programlisting>set fbroker [::FormBroker create {var1 integer default 0} \
-            {var2 unsigned default 1} \
-            {var3 string} \
-            {var4 integer default 0}]
+<programlisting>
+set fbroker [::FormBroker create {var1 integer default 0} \
+                                 {var2 unsigned default 1} \
+                                 {var3 string} \
+                                 {var4 integer default 0}]
 % $fbroker response a
 % parray a
 a(var1) = 0
diff --git a/src/mod_rivet_ng/rivet_lazy_mpm.c b/src/mod_rivet_ng/rivet_lazy_mpm.c
index f5bc6df..40f68b6 100644
--- a/src/mod_rivet_ng/rivet_lazy_mpm.c
+++ b/src/mod_rivet_ng/rivet_lazy_mpm.c
@@ -58,13 +58,12 @@
     request_rec*        r;
     int                 ctype;
     int                 ap_sts;
-    rivet_server_conf*  conf;               /* rivet_server_conf* record            */
+    rivet_server_conf*  conf;               /* rivet_server_conf* record                */
 } lazy_tcl_worker;
 
 /* virtual host thread queue descriptor */
 
 typedef struct vhost_iface {
-    int                 idle_threads_cnt;   /* idle threads for the virtual hosts       */
     int                 threads_count;      /* total number of running and idle threads */
     apr_thread_mutex_t* mutex;              /* mutex protecting 'array'                 */
     apr_array_header_t* array;              /* LIFO array of lazy_tcl_worker pointers   */
@@ -192,14 +191,21 @@
 
     Lazy_RunConfScript(private,w,child_init);
 
+    idx = w->conf->idx;
+
+    /* After the thread has run the configuration script we 
+       increment the threads counter */
+
+    apr_thread_mutex_lock(module_globals->mpm->vhosts[idx].mutex);
+    (module_globals->mpm->vhosts[idx].threads_count)++;
+    apr_thread_mutex_unlock(module_globals->mpm->vhosts[idx].mutex);
+
     /* The thread is now set up to serve request within the the 
      * do...while loop controlled by private->keep_going  */
 
-    idx = w->conf->idx;
     apr_thread_mutex_lock(w->mutex);
     do 
     {
-        module_globals->mpm->vhosts[idx].idle_threads_cnt++;
         while ((w->status != init) && (w->status != thread_exit)) {
             apr_thread_cond_wait(w->condition,w->mutex);
         } 
@@ -209,7 +215,6 @@
         }
 
         w->status = processing;
-        module_globals->mpm->vhosts[idx].idle_threads_cnt--;
 
         /* Content generation */
 
@@ -236,8 +241,8 @@
     } while (private->ext->keep_going);
     apr_thread_mutex_unlock(w->mutex);
     
-    ap_log_error(APLOG_MARK,APLOG_DEBUG,APR_SUCCESS,w->server,"processor thread orderly exit");
     Lazy_RunConfScript(private,w,child_exit);
+    ap_log_error(APLOG_MARK,APLOG_DEBUG,APR_SUCCESS,w->server,"processor thread orderly exit");
 
     apr_thread_mutex_lock(module_globals->mpm->vhosts[idx].mutex);
     (module_globals->mpm->vhosts[idx].threads_count)--;
@@ -260,7 +265,7 @@
 
 static lazy_tcl_worker* create_worker (apr_pool_t* pool,server_rec* server)
 {
-    lazy_tcl_worker*    w;
+    lazy_tcl_worker* w;
 
     w = apr_pcalloc(pool,sizeof(lazy_tcl_worker));
 
@@ -319,19 +324,18 @@
      
     for (s = root_server; s != NULL; s = s->next)
     {
-        int                 vh;
+        int                 idx;
         apr_array_header_t* array;
         rivet_server_conf*  rsc = RIVET_SERVER_CONF(s->module_config);
 
-        vh = rsc->idx;
-        rv = apr_thread_mutex_create(&module_globals->mpm->vhosts[vh].mutex,
+        idx = rsc->idx;
+        rv  = apr_thread_mutex_create(&module_globals->mpm->vhosts[idx].mutex,
                                       APR_THREAD_MUTEX_UNNESTED,pool);
         ap_assert(rv == APR_SUCCESS);
         array = apr_array_make(pool,0,sizeof(void*));
         ap_assert(array != NULL);
-        module_globals->mpm->vhosts[vh].array = array;
-        module_globals->mpm->vhosts[vh].idle_threads_cnt = 0;
-        module_globals->mpm->vhosts[vh].threads_count = 0;
+        module_globals->mpm->vhosts[idx].array = array;
+        module_globals->mpm->vhosts[idx].threads_count = 0;
     }
     module_globals->mpm->server_shutdown = 0;
 }
@@ -377,7 +381,7 @@
     if (apr_is_empty_array(array))
     {
         w = create_worker(module_globals->pool,r->server);
-        (module_globals->mpm->vhosts[conf->idx].threads_count)++; 
+        //(module_globals->mpm->vhosts[conf->idx].threads_count)++; 
     }
     else
     {
@@ -385,7 +389,9 @@
     }
 
     apr_thread_mutex_unlock(mutex);
-    
+
+    /* Locking the thread descriptor structure mutex */    
+
     apr_thread_mutex_lock(w->mutex);
     w->r        = r;
     w->ctype    = ctype;
@@ -421,27 +427,42 @@
     return private->ext->interp;
 }
 
+/*
+ * -- LazyBridge_Finalize
+ *
+ * Bridge thread and resources shutdown
+ *
+ */
+
 apr_status_t LazyBridge_Finalize (void* data)
 {
-    int vh;
-    rivet_server_conf* conf = RIVET_SERVER_CONF(((server_rec*) data)->module_config);
+    int idx;
+    server_rec* server = (server_rec*) data;
+    rivet_server_conf* conf = RIVET_SERVER_CONF(server->module_config);
    
     module_globals->mpm->server_shutdown = 1;
-    for (vh = 0; vh < module_globals->vhosts_count; vh++)
+    for (idx = 0; idx < module_globals->vhosts_count; idx++)
     {
         int try;
         int count;
         apr_array_header_t* array;
         apr_thread_mutex_t* mutex;
 
-        mutex = module_globals->mpm->vhosts[vh].mutex;
-        array = module_globals->mpm->vhosts[vh].array;
-        apr_thread_mutex_lock(mutex);
-        try = 0;
-        do {
+        mutex = module_globals->mpm->vhosts[idx].mutex;
+        array = module_globals->mpm->vhosts[idx].array;
 
-            count = module_globals->mpm->vhosts[vh].threads_count;
-            if (((conf->idx == vh) && (count == 1)) || (count == 0)) { break; } 
+        /* we need to lock the vhost data mutex */
+
+        apr_thread_mutex_lock(mutex);
+
+        count = module_globals->mpm->vhosts[idx].threads_count;
+        apr_thread_mutex_unlock(mutex);
+        try = 0;
+
+        while ((try++ < 3) && (count > 0)) 
+        {
+            ap_log_error(APLOG_MARK,APLOG_DEBUG,APR_SUCCESS,server,"waiting for %d thread to exit",count);
+            if ((conf->idx == idx) && (count == 1)) { break; } 
 
             while (!apr_is_empty_array(array)) 
             {
@@ -449,20 +470,31 @@
 
                 w = *(lazy_tcl_worker**) apr_array_pop(array); 
                 apr_thread_mutex_lock(w->mutex);
-                w->r        = NULL;
-                w->status   = thread_exit;
+                w->r      = NULL;
+                w->status = thread_exit;
                 apr_thread_cond_signal(w->condition);
                 apr_thread_mutex_unlock(w->mutex);
             }
-            apr_sleep(10000);
+            
+            apr_thread_mutex_lock(mutex);
+            count = module_globals->mpm->vhosts[idx].threads_count;
+            apr_thread_mutex_unlock(mutex);
 
-        } while ((try++ < 3));
-        apr_thread_mutex_unlock(mutex);
+            apr_sleep(1000);
+        }
     }
 
     return APR_SUCCESS;
 }
 
+/*
+ * -- LazyBridge_ExitHandler
+ *
+ *
+ *
+ */
+
+
 int LazyBridge_ExitHandler(rivet_thread_private* private)
 {
 
@@ -473,29 +505,6 @@
 
     private->ext->keep_going = 0;
 
-    /*
-     * This is the only place where exit_command and 
-     * exit_command_status are set, anywere alse these
-     * fields are only read. We lock on writing to synchronize
-     * with other threads that might try to access
-     * this info. That means that in the unlikely case
-     * of several threads calling ::rivet::exit 
-     * simultaneously the first sets the exit code.
-     * This is just terrible, it highlights the bad habit
-     * of calling 'exit' when programming with mod_rivet
-     * and calls out for a version of Tcl with which
-     * we could safely call Tcl_DeleteInterp and then terminate
-     * a single thread
-    
-    apr_thread_mutex_lock(module_globals->mpm->mutex);
-    if (module_globals->mpm->exit_command == 0)
-    {
-        module_globals->mpm->exit_command = 1;
-        module_globals->mpm->exit_command_status = private->exit_status;
-    }
-    apr_thread_mutex_unlock(module_globals->mpm->mutex);
-     */
-
     if (!private->running_conf->single_thread_exit)
     {
         /* We now tell the supervisor to terminate the Tcl worker thread pool
@@ -509,51 +518,6 @@
     return TCL_OK;
 }
 
-#if 0
-int LazyBridge_ExitHandler(rivet_thread_private* private)
-{
-
-    /* This is not strictly necessary, because this command will 
-     * eventually terminate the whole processes */
-
-    /* This will force the current thread to exit */
-
-    private->ext->keep_going = 0;
-
-    /*
-     * This is the only place where exit_command and 
-     * exit_command_status are set, anywere alse these
-     * fields are only read. We lock on writing to synchronize
-     * with other threads that might try to access
-     * this info. That means that in the unlikely case
-     * of several threads calling ::rivet::exit 
-     * simultaneously the first sets the exit code.
-     * This is just terrible, it highlights the bad habit
-     * of calling 'exit' when programming with mod_rivet
-     * and calls out for a version of Tcl with which
-     * we could safely call Tcl_DeleteInterp and then terminate
-     * a single thread
-     */
-
-    apr_thread_mutex_lock(module_globals->mpm->mutex);
-    if (module_globals->mpm->exit_command == 0)
-    {
-        module_globals->mpm->exit_command = 1;
-        module_globals->mpm->exit_command_status = private->exit_status;
-    }
-    apr_thread_mutex_unlock(module_globals->mpm->mutex);
-
-    if (private->running_conf->single_thread_exit) return TCL_OK;
-
-    /* We now tell the supervisor to terminate the Tcl worker thread pool
-     * to exit and is sequence the whole process to shutdown 
-     * by calling exit() */
- 
-    LazyBridge_Finalize (private->r->server);
-    return TCL_OK;
-}
-#endif
-
 DLLEXPORT
 RIVET_MPM_BRIDGE {
     NULL,