On 6/17/22 11:24 AM, ic...@apache.org wrote:
> Author: icing
> Date: Fri Jun 17 09:24:57 2022
> New Revision: 1902005
>
> URL: http://svn.apache.org/viewvc?rev=1902005&view=rev
> Log:
> *) mod_http2: new implementation of h2 worker pool.
> - O(1) cost at registration of connection processing producers
> - no limit on registered producers
> - join of ongoing work on unregister
> - callbacks to unlink dependencies into other h2 code
> - memory cleanup on workers deactivation (on idle timeouts)
> - idle_limit as apr_time_t instead of seconds
>
>
> Modified:
> httpd/httpd/trunk/modules/http2/h2_c1.c
> httpd/httpd/trunk/modules/http2/h2_config.c
> httpd/httpd/trunk/modules/http2/h2_config.h
> httpd/httpd/trunk/modules/http2/h2_mplx.c
> httpd/httpd/trunk/modules/http2/h2_mplx.h
> httpd/httpd/trunk/modules/http2/h2_workers.c
> httpd/httpd/trunk/modules/http2/h2_workers.h
> httpd/httpd/trunk/modules/http2/mod_http2.c
> Modified: httpd/httpd/trunk/modules/http2/h2_workers.c
> URL:
> http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_workers.c?rev=1902005&r1=1902004&r2=1902005&view=diff
> ==============================================================================
> --- httpd/httpd/trunk/modules/http2/h2_workers.c (original)
> +++ httpd/httpd/trunk/modules/http2/h2_workers.c Fri Jun 17 09:24:57 2022
>
> @@ -385,13 +415,13 @@ static apr_status_t workers_pool_cleanup
> }
>
> h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild,
> - int min_workers, int max_workers,
> - int idle_secs)
> + int max_slots, int min_active, apr_time_t
> idle_limit)
> {
> apr_status_t rv;
> h2_workers *workers;
> apr_pool_t *pool;
> - int i, n;
> + apr_allocator_t *allocator;
> + int i, locked = 0;
>
> ap_assert(s);
> ap_assert(pchild);
> @@ -401,7 +431,16 @@ h2_workers *h2_workers_create(server_rec
> * guarded by our lock. Without this pool, all subpool creations would
> * happen on the pool handed to us, which we do not guard.
> */
> - apr_pool_create(&pool, pchild);
> + rv = apr_allocator_create(&allocator);
> + if (rv != APR_SUCCESS) {
> + goto cleanup;
> + }
> + rv = apr_pool_create_ex(&pool, pchild, NULL, allocator);
> + if (rv != APR_SUCCESS) {
> + apr_allocator_destroy(allocator);
> + goto cleanup;
> + }
> + apr_allocator_owner_set(allocator, pool);
> apr_pool_tag(pool, "h2_workers");
> workers = apr_pcalloc(pool, sizeof(h2_workers));
> if (!workers) {
> @@ -410,19 +449,23 @@ h2_workers *h2_workers_create(server_rec
>
> workers->s = s;
> workers->pool = pool;
> - workers->min_workers = min_workers;
> - workers->max_workers = max_workers;
> - workers->max_idle_secs = (idle_secs > 0)? idle_secs : 10;
> + workers->min_active = min_active;
> + workers->max_slots = max_slots;
> + workers->idle_limit = (idle_limit > 0)? idle_limit :
> apr_time_from_sec(10);
> + workers->dynamic = (workers->min_active < workers->max_slots);
> +
> + ap_log_error(APLOG_MARK, APLOG_INFO, 0, workers->s,
> + "h2_workers: created with min=%d max=%d idle_ms=%d",
> + workers->min_active, workers->max_slots,
> + (int)apr_time_as_msec(idle_limit));
> +
> + APR_RING_INIT(&workers->idle, h2_slot, link);
> + APR_RING_INIT(&workers->busy, h2_slot, link);
> + APR_RING_INIT(&workers->free, h2_slot, link);
> + APR_RING_INIT(&workers->zombie, h2_slot, link);
>
> - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
> - "h2_workers: created with min=%d max=%d idle_timeout=%d
> sec",
> - workers->min_workers, workers->max_workers,
> - (int)workers->max_idle_secs);
> - /* FIXME: the fifo set we use here has limited capacity. Once the
> - * set is full, connections with new requests do a wait.
> - */
> - rv = h2_fifo_set_create(&workers->mplxs, pool, 16 * 1024);
> - if (rv != APR_SUCCESS) goto cleanup;
> + APR_RING_INIT(&workers->prod_active, ap_conn_producer_t, link);
> + APR_RING_INIT(&workers->prod_idle, ap_conn_producer_t, link);
>
> rv = apr_threadattr_create(&workers->thread_attr, workers->pool);
> if (rv != APR_SUCCESS) goto cleanup;
> @@ -441,32 +484,35 @@ h2_workers *h2_workers_create(server_rec
> if (rv != APR_SUCCESS) goto cleanup;
> rv = apr_thread_cond_create(&workers->all_done, workers->pool);
> if (rv != APR_SUCCESS) goto cleanup;
> + rv = apr_thread_cond_create(&workers->prod_done, workers->pool);
> + if (rv != APR_SUCCESS) goto cleanup;
>
> - n = workers->nslots = workers->max_workers;
> - workers->slots = apr_pcalloc(workers->pool, n * sizeof(h2_slot));
> - if (workers->slots == NULL) {
> - n = workers->nslots = 0;
> - rv = APR_ENOMEM;
> - goto cleanup;
> - }
> - for (i = 0; i < n; ++i) {
> + apr_thread_mutex_lock(workers->lock);
> + locked = 1;
> +
> + /* create the slots and put them on the free list */
> + workers->slots = apr_pcalloc(workers->pool, workers->max_slots *
> sizeof(h2_slot));
> +
> + for (i = 0; i < workers->max_slots; ++i) {
> workers->slots[i].id = i;
> - }
> - /* we activate all for now, TODO: support min_workers again.
> - * do this in reverse for vanity reasons so slot 0 will most
> - * likely be at head of idle queue. */
> - n = workers->min_workers;
> - for (i = n-1; i >= 0; --i) {
> - rv = activate_slot(workers, &workers->slots[i]);
> + workers->slots[i].state = H2_SLOT_FREE;
> + workers->slots[i].workers = workers;
> + APR_RING_ELEM_INIT(&workers->slots[i], link);
> + APR_RING_INSERT_TAIL(&workers->free, &workers->slots[i], h2_slot,
> link);
> + rv = apr_thread_cond_create(&workers->slots[i].more_work,
> workers->pool);
> if (rv != APR_SUCCESS) goto cleanup;
> }
> - /* the rest of the slots go on the free list */
> - for(i = n; i < workers->nslots; ++i) {
> - push_slot(&workers->free, &workers->slots[i]);
> +
> + /* activate the min amount of workers */
> + for (i = 0; i < workers->min_active; ++i) {
> + rv = activate_slot(workers);
> + if (rv != APR_SUCCESS) goto cleanup;
> }
> - workers->dynamic = (workers->worker_count < workers->max_workers);
>
> cleanup:
> + if (locked) {
> + apr_thread_mutex_unlock(workers->lock);
> + }
> if (rv == APR_SUCCESS) {
> /* Stop/join the workers threads when the MPM child exits (pchild is
> * destroyed), and as a pre_cleanup of pchild thus before the threads
> @@ -476,24 +522,84 @@ cleanup:
> apr_pool_pre_cleanup_register(pchild, workers,
> workers_pool_cleanup);
> return workers;
> }
> + ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, workers->s,
> + "h2_workers: errors initializing");
> return NULL;
> }
Open style question with an ask for opinions: Would it be better to use
pool instead of workers->pool
min_active instead of workers->min_active
max_slots instead of workers->max_slots
?
Would that make the code more
- readable
- faster
or is the current approach better?
Regards
RĂ¼diger