On Wed, Apr 15, 2020 at 09:50:01AM -0400, Jim Jagielski wrote:
> very, very elegant.
Thank you Jim!
I wonder if the new state variable is redundant with the other state
variables in the watchdog structure? I don't understand the watchdog
model well enough to be confident here.
struct ap_watchdog_t
{
apr_uint32_t thread_started; /* set to 1 once thread running */
...
int singleton;
int active;
Also reviewing this module more, in the post_config hook:
http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/core/mod_watchdog.c?annotate=1876619#l434
it looks to me like the ap_state_query() call correctly guards against
multiple post_config runs during start, so there will never be a point
where the wd_server_conf attached to pconf is reused or reusable.
- in initial startup you get only one post_config invocation if the
_PRE_CONFIG phase is skipped
- during subsequent server reloads pconf is cleared each time
I may be missing something.
Regards, Joe
>
> > On Apr 14, 2020, at 8:37 AM, [email protected] wrote:
> >
> > Author: jorton
> > Date: Tue Apr 14 12:37:17 2020
> > New Revision: 1876511
> >
> > URL: http://svn.apache.org/viewvc?rev=1876511&view=rev
> > Log:
> > * modules/core/mod_watchdog.c: Switch to simpler logic to avoid the
> > thread cleanup running before the thread has started, avoiding
> > mutex operations which both have undefined behaviour:
> >
> > a) double-locking an UNNESTED (non-recursive) mutex twice in the parent
> > b) unlocking a mutex in the spawned thread which was locked by the parent
> >
> > (wd_startup, wd_worker_cleanup, wd_worker): Use a boolean to ensure
> > the cleanup does nothing if the thread wasn't started, drop the mutex.
> >
> > Modified:
> > httpd/httpd/trunk/modules/core/mod_watchdog.c
> >
> > Modified: httpd/httpd/trunk/modules/core/mod_watchdog.c
> > URL:
> > http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/core/mod_watchdog.c?rev=1876511&r1=1876510&r2=1876511&view=diff
> > ==============================================================================
> > --- httpd/httpd/trunk/modules/core/mod_watchdog.c (original)
> > +++ httpd/httpd/trunk/modules/core/mod_watchdog.c Tue Apr 14 12:37:17 2020
> > @@ -24,6 +24,8 @@
> > #include "http_core.h"
> > #include "util_mutex.h"
> >
> > +#include "apr_atomic.h"
> > +
> > #define AP_WATCHDOG_PGROUP "watchdog"
> > #define AP_WATCHDOG_PVERSION "parent"
> > #define AP_WATCHDOG_CVERSION "child"
> > @@ -43,7 +45,7 @@ struct watchdog_list_t
> >
> > struct ap_watchdog_t
> > {
> > - apr_thread_mutex_t *startup;
> > + apr_uint32_t thread_started; /* set to 1 once thread running
> > */
> > apr_proc_mutex_t *mutex;
> > const char *name;
> > watchdog_list_t *callbacks;
> > @@ -74,6 +76,10 @@ static apr_status_t wd_worker_cleanup(vo
> > apr_status_t rv;
> > ap_watchdog_t *w = (ap_watchdog_t *)data;
> >
> > + /* Do nothing if the thread wasn't started. */
> > + if (apr_atomic_read32(&w->thread_started) != 1)
> > + return APR_SUCCESS;
> > +
> > if (w->is_running) {
> > watchdog_list_t *wl = w->callbacks;
> > while (wl) {
> > @@ -110,7 +116,8 @@ static void* APR_THREAD_FUNC wd_worker(a
> > w->pool = apr_thread_pool_get(thread);
> > w->is_running = 1;
> >
> > - apr_thread_mutex_unlock(w->startup);
> > + apr_atomic_set32(&w->thread_started, 1); /* thread started */
> > +
> > if (w->mutex) {
> > while (w->is_running) {
> > if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpmq_s) != APR_SUCCESS) {
> > @@ -264,10 +271,7 @@ static apr_status_t wd_startup(ap_watchd
> > {
> > apr_status_t rc;
> >
> > - /* Create thread startup mutex */
> > - rc = apr_thread_mutex_create(&w->startup, APR_THREAD_MUTEX_UNNESTED,
> > p);
> > - if (rc != APR_SUCCESS)
> > - return rc;
> > + apr_atomic_set32(&w->thread_started, 0);
> >
> > if (w->singleton) {
> > /* Initialize singleton mutex in child */
> > @@ -277,22 +281,12 @@ static apr_status_t wd_startup(ap_watchd
> > return rc;
> > }
> >
> > - /* This mutex fixes problems with a fast start/fast end, where the pool
> > - * cleanup was being invoked before the thread completely spawned.
> > - */
> > - apr_thread_mutex_lock(w->startup);
> > - apr_pool_pre_cleanup_register(p, w, wd_worker_cleanup);
> > -
> > /* Start the newly created watchdog */
> > rc = apr_thread_create(&w->thread, NULL, wd_worker, w, p);
> > - if (rc) {
> > - apr_pool_cleanup_kill(p, w, wd_worker_cleanup);
> > + if (rc == APR_SUCCESS) {
> > + apr_pool_pre_cleanup_register(p, w, wd_worker_cleanup);
> > }
> >
> > - apr_thread_mutex_lock(w->startup);
> > - apr_thread_mutex_unlock(w->startup);
> > - apr_thread_mutex_destroy(w->startup);
> > -
> > return rc;
> > }
> >
> >
> >
>