On Thu, Nov 23, 2023 at 12:11 PM Ruediger Pluem <rpl...@apache.org> wrote: > > On 9/11/23 3:50 PM, jfcl...@apache.org wrote: > > Author: jfclere > > Date: Mon Sep 11 13:50:21 2023 > > New Revision: 1912245 > > > > URL: http://svn.apache.org/viewvc?rev=1912245&view=rev > > Log: > > Arrange the bybusyness logic and prevent bad busy values > > this closes #383 [] > > --- httpd/httpd/trunk/modules/proxy/proxy_util.c (original) > > +++ httpd/httpd/trunk/modules/proxy/proxy_util.c Mon Sep 11 13:50:21 2023 > > @@ -21,6 +21,7 @@ > > #include "apr_version.h" > > #include "apr_strings.h" > > #include "apr_hash.h" > > +#include "apr_atomic.h" > > #include "http_core.h" > > #include "proxy_util.h" > > #include "ajp.h" > > @@ -4984,6 +4985,124 @@ PROXY_DECLARE(apr_status_t) ap_proxy_tun > > return APR_SUCCESS; > > } > > > > +PROXY_DECLARE(apr_status_t) decrement_busy_count(void *worker_) > > Why can't we use the respective apr_atomic_sub/add/inc/dec* functions here > instead of the logic below?
This has been discussed in https://github.com/apache/httpd/pull/383, at least for why the "if atomic_dec(&busy) < 0 then atomic_inc(&busy)" proposed originally was racy. So the below implements dec_not_zero and inc_not_at_max to keep ->busy in the bounds, like the original non-atomic functions. I think the under/overflows could still happen without this because, even if we inc per request and dec on request pool cleanup, there is a balancer->lbmethod->reset() which may clear ->busy at any time (IIUC), not to talk about a child crash (though then ->busy may not be very reliable anyway, under/overflow or not..). As for the 64bit atomics vs APR version dance, it's because the former are not available before apr-1.7 and likely not reliable before 1.7.4 (at least for some architectures where builtins are not available). In any case we need a fallback for apr < 1.7, but maybe to keep this simpler we should fall back to non-atomics (as before) in this case. It all looks over complicated for the feature, but I can't think of something simpler and still correct.. > > > +{ > > + apr_size_t val; > > + proxy_worker *worker = worker_; > > + > > +#if APR_SIZEOF_VOIDP == 4 > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint32_t)); > > + val = apr_atomic_read32(&worker->s->busy); > > + while (val > 0) { > > + apr_size_t old = val; > > + val = apr_atomic_cas32(&worker->s->busy, val - 1, old); > > + if (val == old) { > > + break; > > + } > > + } > > +#elif APR_VERSION_AT_LEAST(1,7,4) /* APR 64bit atomics not safe before > > 1.7.4 */ > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint64_t)); > > + val = apr_atomic_read64(&worker->s->busy); > > + while (val > 0) { > > + apr_size_t old = val; > > + val = apr_atomic_cas64(&worker->s->busy, val - 1, old); > > + if (val == old) { > > + break; > > + } > > + } > > +#else /* Use atomics for (64bit) pointers */ > > + void *volatile *busy_p = (void *)&worker->s->busy; > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(void*)); > > + AP_DEBUG_ASSERT((apr_uintptr_t)busy_p % sizeof(void*) == 0); > > + val = (apr_uintptr_t)apr_atomic_casptr((void *)busy_p, NULL, NULL); > > + while (val > 0) { > > + apr_size_t old = val; > > + val = (apr_uintptr_t)apr_atomic_casptr((void *)busy_p, > > + (void *)(apr_uintptr_t)(val > > - 1), > > + (void *)(apr_uintptr_t)old); > > + if (val == old) { > > + break; > > + } > > + } > > +#endif > > + return APR_SUCCESS; > > +} > > + > > +PROXY_DECLARE(void) increment_busy_count(proxy_worker *worker) > > +{ > > + apr_size_t val; > > +#if APR_SIZEOF_VOIDP == 4 > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint32_t)); > > + val = apr_atomic_read32(&worker->s->busy); > > + while (val < APR_INT32_MAX) { > > + apr_size_t old = val; > > + val = apr_atomic_cas32(&worker->s->busy, val + 1, old); > > + if (val == old) { > > + break; > > + } > > + } > > +#elif APR_VERSION_AT_LEAST(1,7,4) /* APR 64bit atomics not safe before > > 1.7.4 */ > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint64_t)); > > + val = apr_atomic_read64(&worker->s->busy); > > + while (val < APR_INT64_MAX) { > > + apr_size_t old = val; > > + val = apr_atomic_cas64(&worker->s->busy, val + 1, old); > > + if (val == old) { > > + break; > > + } > > + } > > +#else /* Use atomics for (64bit) pointers */ > > + void *volatile *busy_p = (void *)&worker->s->busy; > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(void*)); > > + AP_DEBUG_ASSERT((apr_uintptr_t)busy_p % sizeof(void*) == 0); > > + val = (apr_uintptr_t)apr_atomic_casptr((void *)busy_p, NULL, NULL); > > + while (val < APR_INT64_MAX) { > > + apr_size_t old = val; > > + val = (apr_uintptr_t)apr_atomic_casptr((void *)busy_p, > > + (void *)(apr_uintptr_t)(val > > + 1), > > + (void *)(apr_uintptr_t)old); > > + if (val == old) { > > + break; > > + } > > + } > > +#endif > > +} > > + > > +PROXY_DECLARE(apr_size_t) getbusy_count(proxy_worker *worker) > > +{ > > + apr_size_t val; > > +#if APR_SIZEOF_VOIDP == 4 > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint32_t)); > > + val = apr_atomic_read32(&worker->s->busy); > > +#elif APR_VERSION_AT_LEAST(1,7,4) /* APR 64bit atomics not safe before > > 1.7.4 */ > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint64_t)); > > + val = apr_atomic_read64(&worker->s->busy); > > +#else /* Use atomics for (64bit) pointers */ > > + void *volatile *busy_p = (void *)&worker->s->busy; > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(void*)); > > + AP_DEBUG_ASSERT((apr_uintptr_t)busy_p % sizeof(void*) == 0); > > + val = (apr_uintptr_t)apr_atomic_casptr((void *)busy_p, NULL, NULL); > > +#endif > > + > > + return val; > > +} > > + > > +PROXY_DECLARE(void) setbusy_count(proxy_worker *worker, apr_size_t to) > > +{ > > +#if APR_SIZEOF_VOIDP == 4 > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint32_t)); > > + apr_atomic_set32(&worker->s->busy, to); > > +#elif APR_VERSION_AT_LEAST(1,7,4) /* APR 64bit atomics not safe before > > 1.7.4 */ > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(apr_uint64_t)); > > + apr_atomic_set64(&worker->s->busy, to); > > +#else /* Use atomics for (64bit) pointers */ > > + void *volatile *busy_p = (void *)&worker->s->busy; > > + AP_DEBUG_ASSERT(sizeof(apr_size_t) == sizeof(void*)); > > + AP_DEBUG_ASSERT((apr_uintptr_t)busy_p % sizeof(void*) == 0); > > + apr_atomic_xchgptr((void *)busy_p, (void *)(apr_uintptr_t)to); > > +#endif > > +} Yeah it's not nice :/ Regards; Yann.