From: Yann Ylavic Date: Fri, 9 Feb 2018 11:19:01 +0000 (+0000) Subject: Merge r1821558, r1821561, r1821595 from trunk: X-Git-Tag: 2.4.30~95 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=393c8808c119d381ef79d943abc94725265eff1d;p=apache Merge r1821558, r1821561, r1821595 from trunk: mod_event: Let the listener thread do its maintenance job on resources shortage. PR 61979. Follow up to r1821558: CHANGES typo. mpm_event: Follow up to r1821558. Don't crash (in listener) if we can't create the ptrans allocator. Submitted by: ylavic Reviewed by: ylavic, jim, icing git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1823644 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index c4f4a30af8..c7c5939049 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ -*- coding: utf-8 -*- Changes with Apache 2.4.30 + *) mpm_event: Let the listener thread do its maintenance job on resources + shortage. PR 61979. [Yann Ylavic] + *) mpm_event: Wakeup the listener to re-enable listening sockets. [Yann Ylavic] diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index 1572836266..5e3186c44c 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -177,7 +177,6 @@ static volatile int dying = 0; static volatile int workers_may_exit = 0; static volatile int start_thread_may_exit = 0; static volatile int listener_may_exit = 0; -static volatile int listeners_disabled = 0; static int listener_is_wakeable = 0; /* Pollset supports APR_POLLSET_WAKEABLE */ static int num_listensocks = 0; static apr_int32_t conns_this_child; /* MaxConnectionsPerChild, only access @@ -441,6 +440,8 @@ static pid_t ap_my_pid; /* Linux getpid() doesn't work except in main static pid_t parent_pid; static apr_os_thread_t *listener_os_thread; +static int ap_child_slot; /* Current child process slot in scoreboard */ + /* The LISTENER_SIGNAL signal will be sent from the main thread to the * listener thread to wake it up for graceful termination (what a child * process from an old generation does when the admin does "apachectl @@ -454,23 +455,29 @@ static apr_os_thread_t *listener_os_thread; */ static apr_socket_t **worker_sockets; -static void disable_listensocks(int process_slot) +static volatile apr_uint32_t listensocks_disabled; + +static void disable_listensocks(void) { int i; - listeners_disabled = 1; - for (i = 0; i < num_listensocks; i++) { - apr_pollset_remove(event_pollset, &listener_pollfd[i]); + if (apr_atomic_cas32(&listensocks_disabled, 1, 0) != 0) { + return; } - ap_scoreboard_image->parent[process_slot].not_accepting = 1; + if (event_pollset) { + for (i = 0; i < num_listensocks; i++) { + apr_pollset_remove(event_pollset, &listener_pollfd[i]); + } + } + ap_scoreboard_image->parent[ap_child_slot].not_accepting = 1; } -static void enable_listensocks(int process_slot) +static void enable_listensocks(void) { int i; - if (listener_may_exit) { + if (listener_may_exit + || apr_atomic_cas32(&listensocks_disabled, 0, 1) != 1) { return; } - listeners_disabled = 0; ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00457) "Accepting new connections again: " "%u active conns (%u lingering/%u clogged/%u suspended), " @@ -486,7 +493,12 @@ static void enable_listensocks(int process_slot) * XXX: This is not yet optimal. If many workers suddenly become available, * XXX: the parent may kill some processes off too soon. */ - ap_scoreboard_image->parent[process_slot].not_accepting = 0; + ap_scoreboard_image->parent[ap_child_slot].not_accepting = 0; +} + +static APR_INLINE apr_uint32_t listeners_disabled(void) +{ + return apr_atomic_read32(&listensocks_disabled); } static APR_INLINE int connections_above_limit(void) @@ -496,9 +508,9 @@ static APR_INLINE int connections_above_limit(void) apr_uint32_t c_count = apr_atomic_read32(&connection_count); apr_uint32_t l_count = apr_atomic_read32(&lingering_count); if (c_count <= l_count - /* Off by 'listeners_disabled' to avoid flip flop */ + /* Off by 'listeners_disabled()' to avoid flip flop */ || c_count - l_count < (apr_uint32_t)threads_per_child + - (i_count - (listeners_disabled != 0)) * + (i_count - listeners_disabled()) * (worker_factor / WORKER_FACTOR_SCALE)) { return 0; } @@ -561,6 +573,7 @@ static void close_worker_sockets(void) static void wakeup_listener(void) { listener_may_exit = 1; + disable_listensocks(); /* Unblock the listener if it's poll()ing */ if (event_pollset && listener_is_wakeable) { @@ -754,7 +767,7 @@ static apr_status_t decrement_connection_count(void *cs_) is_last_connection = !apr_atomic_dec32(&connection_count); if (listener_is_wakeable && ((is_last_connection && listener_may_exit) - || (listeners_disabled && !connections_above_limit()))) { + || (listeners_disabled() && !connections_above_limit()))) { apr_pollset_wakeup(event_pollset); } return APR_SUCCESS; @@ -1206,17 +1219,16 @@ static void check_infinite_requests(void) } } -static void close_listeners(int process_slot, int *closed) +static void close_listeners(int *closed) { if (!*closed) { int i; - disable_listensocks(process_slot); ap_close_listeners_ex(my_bucket->listeners); *closed = 1; dying = 1; - ap_scoreboard_image->parent[process_slot].quiescing = 1; + ap_scoreboard_image->parent[ap_child_slot].quiescing = 1; for (i = 0; i < threads_per_child; ++i) { - ap_update_child_status_from_indexes(process_slot, i, + ap_update_child_status_from_indexes(ap_child_slot, i, SERVER_GRACEFUL, NULL); } /* wake up the main thread */ @@ -1590,8 +1602,9 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf, "failed to initialize pollset, " - "attempting to shutdown process gracefully"); - signal_threads(ST_GRACEFUL); + "shutdown process now"); + resource_shortage = 1; + signal_threads(ST_UNGRACEFUL); return NULL; } @@ -1613,7 +1626,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) check_infinite_requests(); if (listener_may_exit) { - close_listeners(process_slot, &closed); + close_listeners(&closed); if (terminate_mode == ST_UNGRACEFUL || apr_atomic_read32(&connection_count) == 0) break; @@ -1699,7 +1712,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) * need to update timeouts (logic is above, so simply restart * the loop). */ - if (!listener_may_exit && !listeners_disabled) { + if (!listener_may_exit && !listeners_disabled()) { continue; } timeout_time = 0; @@ -1714,13 +1727,13 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) } if (listener_may_exit) { - close_listeners(process_slot, &closed); + close_listeners(&closed); if (terminate_mode == ST_UNGRACEFUL || apr_atomic_read32(&connection_count) == 0) break; } - while (num) { + for (; num; --num, ++out_pfd) { listener_poll_type *pt = (listener_poll_type *) out_pfd->client_data; if (pt->type == PT_CSD) { /* one of the sockets is readable */ @@ -1781,16 +1794,16 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) ap_assert(0); } } - else if (pt->type == PT_ACCEPT && !listeners_disabled) { + else if (pt->type == PT_ACCEPT && !listeners_disabled()) { /* A Listener Socket is ready for an accept() */ if (workers_were_busy) { - disable_listensocks(process_slot); + disable_listensocks(); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "All workers busy, not accepting new conns " "in this process"); } else if (connections_above_limit()) { - disable_listensocks(process_slot); + disable_listensocks(); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "Too many open connections (%u), " "not accepting new conns in this process", @@ -1808,22 +1821,31 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) if (ptrans == NULL) { /* create a new transaction pool for each accepted socket */ - apr_allocator_t *allocator; - - apr_allocator_create(&allocator); - apr_allocator_max_free_set(allocator, - ap_max_mem_free); - apr_pool_create_ex(&ptrans, pconf, NULL, allocator); - apr_allocator_owner_set(allocator, ptrans); - if (ptrans == NULL) { + apr_allocator_t *allocator = NULL; + + rc = apr_allocator_create(&allocator); + if (rc == APR_SUCCESS) { + apr_allocator_max_free_set(allocator, + ap_max_mem_free); + rc = apr_pool_create_ex(&ptrans, pconf, NULL, + allocator); + if (rc == APR_SUCCESS) { + apr_pool_tag(ptrans, "transaction"); + apr_allocator_owner_set(allocator, ptrans); + } + } + if (rc != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_CRIT, rc, ap_server_conf, APLOGNO(03097) "Failed to create transaction pool"); + if (allocator) { + apr_allocator_destroy(allocator); + } + resource_shortage = 1; signal_threads(ST_GRACEFUL); - return NULL; + continue; } } - apr_pool_tag(ptrans, "transaction"); get_worker(&have_idle_worker, 1, &workers_were_busy); rc = lr->accept_func(&csd, lr, ptrans); @@ -1850,9 +1872,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) } } } /* if:else on pt->type */ - out_pfd++; - num--; - } /* while for processing poll */ + } /* for processing poll */ /* XXX possible optimization: stash the current time for use as * r->request_time for new requests @@ -1924,14 +1944,14 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy) } } - if (listeners_disabled + if (listeners_disabled() && !workers_were_busy && !connections_above_limit()) { - enable_listensocks(process_slot); + enable_listensocks(); } } /* listener main loop */ - close_listeners(process_slot, &closed); + close_listeners(&closed); ap_queue_term(worker_queue); apr_thread_exit(thd, APR_SUCCESS); @@ -2379,6 +2399,7 @@ static void child_main(int child_num_arg, int child_bucket) retained->mpm->mpm_state = AP_MPMQ_STARTING; ap_my_pid = getpid(); + ap_child_slot = child_num_arg; ap_fatal_signal_child_setup(ap_server_conf); apr_pool_create(&pchild, pconf); @@ -3356,6 +3377,7 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog, event_pollset = NULL; worker_queue_info = NULL; listener_os_thread = NULL; + listensocks_disabled = 0; return OK; }