On Thu, Jan 20, 2022 at 2:41 PM Ruediger Pluem <rpl...@apache.org> wrote: > > On 1/20/22 2:24 PM, Yann Ylavic wrote: > > > > > All good points, thanks RĂ¼diger, should be fixed in r1897250. > > Great. I guess next we need to think what we do for 2.4.x. > Even when 1.8.x is released, we cannot demand it for 2.4.x (for trunk we > could). > I guess we have two general choices: > > 1. We implement it differently on 2.4.x also using TLS when available, but > not requiring APR 1.8.x. > 2. We recommend that people who switch over to PCRE2 to use APR 1.8.x for > performance reasons. > > As using PCRE2 make some sense and should be encouraged 2. would make it > difficult for people tied to older APR versions like some > distributions. > > For 1. my rough idea would be that in case of a threaded MPM we could store > the apr_thread_t pointer of a worker_thread in a TLS. > That would solve the performance issue in most cases.
How about the attached patch? For APR < 1.8 it defines ap_thread_create()/ap_thread_current() as wrappers around the APR ones plus storing/loading the current apr_thread_t* from the TLS as APR 1.8 does (if implemented by the compiler still). Then it applies a simple s/apr_thread_create/ap_thread_create/g in the code base.. Finally ap_regex is adapted to use that if available at compile time and runtime, otherwise it falls back to allocation/free. > > BTW: I think the current code does not work well in the case of > !APR_HAS_THREADS, but in this case we could just a static variable > to store the pointer, correct? AP[R]_HAS_THREAD_LOCAL are defined on if APR_HAS_THREADS, so it should be fine for the compilation. I'm not sure we should optimize for the !APR_HAS_THREADS case with a static though, but I could be convinced.. Regards; Yann.
Index: include/httpd.h =================================================================== --- include/httpd.h (revision 1897156) +++ include/httpd.h (working copy) @@ -47,6 +47,7 @@ #include "ap_release.h" #include "apr.h" +#include "apr_version.h" #include "apr_general.h" #include "apr_tables.h" #include "apr_pools.h" @@ -2560,7 +2561,49 @@ AP_DECLARE(void *) ap_realloc(void *ptr, size_t si AP_FN_ATTR_WARN_UNUSED_RESULT AP_FN_ATTR_ALLOC_SIZE(2); +#if APR_VERSION_AT_LEAST(1,8,0) + +#define ap_thread_create apr_thread_create +#define ap_thread_current apr_thread_current + +#ifdef APR_HAS_THREAD_LOCAL +#define AP_HAS_THREAD_LOCAL APR_HAS_THREAD_LOCAL +#define AP_THREAD_LOCAL APR_THREAD_LOCAL +#endif + +#else /* !APR_VERSION_AT_LEAST(1,8,0) */ + +#if APR_HAS_THREADS /** + * AP_THREAD_LOCAL keyword mapping the compiler's. + */ + +#if defined(__cplusplus) && __cplusplus >= 201103L +#define AP_THREAD_LOCAL thread_local +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 +#define AP_THREAD_LOCAL _Thread_local +#elif defined(__GNUC__) /* works for clang too */ +#define AP_THREAD_LOCAL __thread +#elif defined(WIN32) && defined(_MSC_VER) +#define AP_THREAD_LOCAL __declspec(thread) +#endif +#endif /* APR_HAS_THREADS */ + +#ifdef AP_THREAD_LOCAL +#define AP_HAS_THREAD_LOCAL 1 +AP_DECLARE(apr_status_t) ap_thread_create(apr_thread_t **thread, + apr_threadattr_t *attr, + apr_thread_start_t func, + void *data, apr_pool_t *pool); +AP_DECLARE(apr_thread_t *) ap_thread_current(void); +#else +#define ap_thread_create apr_thread_create +#define ap_thread_current() NULL +#endif + +#endif /* !APR_VERSION_AT_LEAST(1,8,0) */ + +/** * Get server load params * @param ld struct to populate: -1 in fields means error */ Index: modules/core/mod_watchdog.c =================================================================== --- modules/core/mod_watchdog.c (revision 1897156) +++ modules/core/mod_watchdog.c (working copy) @@ -280,7 +280,7 @@ static apr_status_t wd_startup(ap_watchdog_t *w, a } /* Start the newly created watchdog */ - rc = apr_thread_create(&w->thread, NULL, wd_worker, w, p); + rc = ap_thread_create(&w->thread, NULL, wd_worker, w, p); if (rc == APR_SUCCESS) { apr_pool_pre_cleanup_register(p, w, wd_worker_cleanup); } Index: modules/http2/h2_workers.c =================================================================== --- modules/http2/h2_workers.c (revision 1897156) +++ modules/http2/h2_workers.c (working copy) @@ -100,8 +100,8 @@ static apr_status_t activate_slot(h2_workers *work * to the idle queue */ apr_atomic_inc32(&workers->worker_count); slot->timed_out = 0; - rv = apr_thread_create(&slot->thread, workers->thread_attr, - slot_run, slot, workers->pool); + rv = ap_thread_create(&slot->thread, workers->thread_attr, + slot_run, slot, workers->pool); if (rv != APR_SUCCESS) { apr_atomic_dec32(&workers->worker_count); } Index: modules/ssl/mod_ssl_ct.c =================================================================== --- modules/ssl/mod_ssl_ct.c (revision 1897156) +++ modules/ssl/mod_ssl_ct.c (working copy) @@ -1123,8 +1123,8 @@ static int daemon_thread_start(apr_pool_t *pconf, apr_pool_create(&pdaemon, pconf); apr_pool_tag(pdaemon, "sct_daemon"); - rv = apr_thread_create(&daemon_thread, NULL, sct_daemon_thread, s_main, - pconf); + rv = ap_thread_create(&daemon_thread, NULL, sct_daemon_thread, s_main, + pconf); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s_main, APLOGNO(02709) "could not create " DAEMON_THREAD_NAME @@ -2522,7 +2522,7 @@ static void ssl_ct_child_init(apr_pool_t *p, serve exit(APEXIT_CHILDSICK); } - rv = apr_thread_create(&service_thread, NULL, run_service_thread, s, p); + rv = ap_thread_create(&service_thread, NULL, run_service_thread, s, p); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(02745) "could not create " SERVICE_THREAD_NAME Index: server/mpm/event/event.c =================================================================== --- server/mpm/event/event.c (revision 1897156) +++ server/mpm/event/event.c (working copy) @@ -2507,11 +2507,11 @@ static void create_listener_thread(thread_starter my_info = (proc_info *) ap_malloc(sizeof(proc_info)); my_info->pslot = my_child_num; my_info->tslot = -1; /* listener thread doesn't have a thread slot */ - rv = apr_thread_create(&ts->listener, thread_attr, listener_thread, - my_info, pruntime); + rv = ap_thread_create(&ts->listener, thread_attr, listener_thread, + my_info, pruntime); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00474) - "apr_thread_create: unable to create listener thread"); + "ap_thread_create: unable to create listener thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } @@ -2702,12 +2702,12 @@ static void *APR_THREAD_FUNC start_threads(apr_thr /* We let each thread update its own scoreboard entry. This is * done because it lets us deal with tid better. */ - rv = apr_thread_create(&threads[i], thread_attr, - worker_thread, my_info, pruntime); + rv = ap_thread_create(&threads[i], thread_attr, + worker_thread, my_info, pruntime); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03104) - "apr_thread_create: unable to create worker thread"); + "ap_thread_create: unable to create worker thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } @@ -2915,11 +2915,11 @@ static void child_main(int child_num_arg, int chil ts->child_num_arg = child_num_arg; ts->threadattr = thread_attr; - rv = apr_thread_create(&start_thread_id, thread_attr, start_threads, - ts, pchild); + rv = ap_thread_create(&start_thread_id, thread_attr, start_threads, + ts, pchild); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00480) - "apr_thread_create: unable to create worker thread"); + "ap_thread_create: unable to create worker thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } Index: server/mpm/worker/worker.c =================================================================== --- server/mpm/worker/worker.c (revision 1897156) +++ server/mpm/worker/worker.c (working copy) @@ -855,11 +855,11 @@ static void create_listener_thread(thread_starter my_info->pid = my_child_num; my_info->tid = -1; /* listener thread doesn't have a thread slot */ my_info->sd = 0; - rv = apr_thread_create(&ts->listener, thread_attr, listener_thread, - my_info, pruntime); + rv = ap_thread_create(&ts->listener, thread_attr, listener_thread, + my_info, pruntime); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00275) - "apr_thread_create: unable to create listener thread"); + "ap_thread_create: unable to create listener thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } @@ -975,11 +975,11 @@ static void * APR_THREAD_FUNC start_threads(apr_th /* We let each thread update its own scoreboard entry. This is * done because it lets us deal with tid better. */ - rv = apr_thread_create(&threads[i], thread_attr, - worker_thread, my_info, pruntime); + rv = ap_thread_create(&threads[i], thread_attr, + worker_thread, my_info, pruntime); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03142) - "apr_thread_create: unable to create worker thread"); + "ap_thread_create: unable to create worker thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } @@ -1202,11 +1202,11 @@ static void child_main(int child_num_arg, int chil ts->child_num_arg = child_num_arg; ts->threadattr = thread_attr; - rv = apr_thread_create(&start_thread_id, thread_attr, start_threads, - ts, pchild); + rv = ap_thread_create(&start_thread_id, thread_attr, start_threads, + ts, pchild); if (rv != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00282) - "apr_thread_create: unable to create worker thread"); + "ap_thread_create: unable to create worker thread"); /* let the parent decide how bad this really is */ clean_child_exit(APEXIT_CHILDSICK); } Index: server/util.c =================================================================== --- server/util.c (revision 1897156) +++ server/util.c (working copy) @@ -3261,6 +3261,56 @@ AP_DECLARE(void *) ap_realloc(void *ptr, size_t si return p; } +#if defined(AP_THREAD_LOCAL) && !APR_VERSION_AT_LEAST(1,8,0) +struct thread_ctx { + apr_thread_start_t func; + void *data; +}; + +static AP_THREAD_LOCAL apr_thread_t *current_thread; + +static apr_status_t current_thread_cleanup(void *arg) +{ + current_thread = NULL; + return APR_SUCCESS; +} + +static void *(APR_THREAD_FUNC *thread_start)(apr_thread_t *thread, void *data) +{ + apr_pool_t *tp = apr_thread_pool_get(thread); + struct thread_ctx *ctx = data; + void *ret; + + current_thread = thread; + apr_pool_cleanup_register(tp, NULL, current_thread_cleanup, + apr_pool_cleanup_null); + + ret = ctx->func(thread, ctx->data); + + apr_pool_cleanup_kill(tp, NULL, current_thread_cleanup, + apr_pool_cleanup_null); + current_thread = NULL; + + return ret; +} + +AP_DECLARE(apr_status_t) ap_thread_create(apr_thread_t **thread, + apr_threadattr_t *attr, + apr_thread_start_t func, + void *data, apr_pool_t *pool) +{ + struct thread_ctx *ctx = apr_palloc(pool, sizeof(*ctx)); + ctx->func = func; + ctx->data = data; + return apr_thread_create(thread, attr, thread_start, ctx, pool); +} + +AP_DECLARE(apr_thread_t *) ap_thread_current(void) +{ + return current_thread; +} +#endif + AP_DECLARE(void) ap_get_sload(ap_sload_t *ld) { int i, j, server_limit, thread_limit; Index: server/util_pcre.c =================================================================== --- server/util_pcre.c (revision 1897250) +++ server/util_pcre.c (working copy) @@ -281,11 +281,57 @@ typedef int* match_data_pt; typedef int* match_vector_pt; #endif -#ifdef APR_HAS_THREAD_LOCAL +static match_data_pt alloc_match_data(apr_size_t size, + match_vector_pt *ovector, + match_vector_pt small_vector) +{ + match_data_pt data; +#ifdef HAVE_PCRE2 + data = pcre2_match_data_create(size, NULL); + if (data) { + *ovector = pcre2_get_ovector_pointer(data); + } +#else + if (size > POSIX_MALLOC_THRESHOLD) { + data = malloc(size * sizeof(int) * 3); + } + else { + data = small_vector; + } + *ovector = data; +#endif + + return data; +} + +static APR_INLINE void free_match_data(match_data_pt data, + apr_size_t size) +{ +#ifdef HAVE_PCRE2 + pcre2_match_data_free(data); +#else + if (size > POSIX_MALLOC_THRESHOLD) { + free(data); + } +#endif +} + +static APR_INLINE void put_match_data(match_data_pt data, + apr_size_t size, + int to_free) +{ + if (to_free) { + free_match_data(data, size); + } +} + +#ifdef AP_HAS_THREAD_LOCAL + static match_data_pt get_match_data(apr_size_t size, match_vector_pt *ovector, - match_vector_pt small_vector) + match_vector_pt small_vector, + int *to_free) { apr_thread_t *current; struct { @@ -293,9 +339,11 @@ static match_data_pt get_match_data(apr_size_t siz apr_size_t size; } *tls = NULL; - /* APR_HAS_THREAD_LOCAL garantees this works */ - current = apr_thread_current(); - ap_assert(current != NULL); + current = ap_thread_current(); + if (!current) { + *to_free = 1; + return alloc_match_data(size, ovector, small_vector); + } apr_thread_data_get((void **)&tls, "apreg", current); if (!tls || tls->size < size) { @@ -335,53 +383,19 @@ static match_data_pt get_match_data(apr_size_t siz return tls->data; } -/* Nothing to put back with thread local */ -static APR_INLINE void put_match_data(match_data_pt data, - apr_size_t size) -{ } +#else /* !AP_HAS_THREAD_LOCAL */ -#else /* !APR_HAS_THREAD_LOCAL */ - -/* Always allocate/free without thread local */ - -static match_data_pt get_match_data(apr_size_t size, - match_vector_pt *ovector, - match_vector_pt small_vector) +static APR_INLINE match_data_pt get_match_data(apr_size_t size, + match_vector_pt *ovector, + match_vector_pt small_vector, + int *to_free) { - match_data_pt data; - -#ifdef HAVE_PCRE2 - data = pcre2_match_data_create(size, NULL); - if (data) { - *ovector = pcre2_get_ovector_pointer(data); - } -#else - if (size > POSIX_MALLOC_THRESHOLD) { - data = malloc(size * sizeof(int) * 3); - } - else { - data = small_vector; - } - *ovector = data; -#endif - - return data; + *to_free = 1; + return alloc_match_data(size, ovector, small_vector); } -static APR_INLINE void put_match_data(match_data_pt data, - apr_size_t size) -{ -#ifdef HAVE_PCRE2 - pcre2_match_data_free(data); -#else - if (size > POSIX_MALLOC_THRESHOLD) { - free(data); - } -#endif -} +#endif /* !AP_HAS_THREAD_LOCAL */ -#endif /* !APR_HAS_THREAD_LOCAL */ - AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string, apr_size_t nmatch, ap_regmatch_t *pmatch, int eflags) @@ -395,14 +409,16 @@ AP_DECLARE(int) ap_regexec_len(const ap_regex_t *p ap_regmatch_t *pmatch, int eflags) { int rc; - int options = 0; + int options = 0, to_free = 0; match_vector_pt ovector = NULL; apr_size_t ncaps = (apr_size_t)preg->re_nsub + 1; -#if defined(HAVE_PCRE2) || defined(APR_HAS_THREAD_LOCAL) - match_data_pt data = get_match_data(ncaps, &ovector, NULL); +#if defined(HAVE_PCRE2) || defined(AP_HAS_THREAD_LOCAL) + match_data_pt data = get_match_data(ncaps, &ovector, NULL, + &to_free); #else int small_vector[POSIX_MALLOC_THRESHOLD * 3]; - match_data_pt data = get_match_data(ncaps, &ovector, small_vector); + match_data_pt data = get_match_data(ncaps, &ovector, small_vector, + &to_free); #endif if (!data) { @@ -437,11 +453,11 @@ AP_DECLARE(int) ap_regexec_len(const ap_regex_t *p } for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; - put_match_data(data, ncaps); + put_match_data(data, ncaps, to_free); return 0; } else { - put_match_data(data, ncaps); + put_match_data(data, ncaps, to_free); #ifdef HAVE_PCRE2 if (rc <= PCRE2_ERROR_UTF8_ERR1 && rc >= PCRE2_ERROR_UTF8_ERR21) return AP_REG_INVARG;