/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/** * Get the next connection to work on.
*/ static conn_rec *get_next(h2_slot *slot)
{
h2_workers *workers = slot->workers;
conn_rec *c = NULL;
ap_conn_producer_t *prod; int has_more;
apr_thread_mutex_lock(workers->lock);
slot->state = H2_SLOT_RUN;
++slot->activations;
APR_RING_ELEM_INIT(slot, link); for(;;) { if (APR_RING_NEXT(slot, link) != slot) { /* slot is part of the idle ring from the last loop */
APR_RING_REMOVE(slot, link);
--workers->idle_slots;
}
slot->is_idle = 0;
if (!workers->aborted && !slot->should_shutdown) {
APR_RING_INSERT_TAIL(&workers->busy, slot, h2_slot, link); do {
c = get_next(slot); if (!c) { break;
}
apr_thread_mutex_unlock(workers->lock); /* See the discussion at <https://github.com/icing/mod_h2/issues/195> * * Each conn_rec->id is supposed to be unique at a point in time. Since * some modules (and maybe external code) uses this id as an identifier * for the request_rec they handle, it needs to be unique for secondary * connections also. * * The MPM module assigns the connection ids and mod_unique_id is using * that one to generate identifier for requests. While the implementation * works for HTTP/1.x, the parallel execution of several requests per * connection will generate duplicate identifiers on load. * * The original implementation for secondary connection identifiers used * to shift the master connection id up and assign the stream id to the * lower bits. This was cramped on 32 bit systems, but on 64bit there was * enough space. * * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the * connection id, even on 64bit systems. Therefore collisions in request ids. * * The way master connection ids are generated, there is some space "at the * top" of the lower 32 bits on allmost all systems. If you have a setup * with 64k threads per child and 255 child processes, you live on the edge. * * The new implementation shifts 8 bits and XORs in the worker * id. This will experience collisions with > 256 h2 workers and heavy * load still. There seems to be no way to solve this in all possible * configurations by mod_h2 alone.
*/ if (c->master) {
c->id = (c->master->id << 8)^slot->id;
}
c->current_thread = thread;
AP_DEBUG_ASSERT(slot->prod);
/* wait for all the workers to become zombies and join them. * this gets called after the mpm shuts down and all connections * have either been handled (graceful) or we are forced exiting
* (ungrateful). Either way, we show limited patience. */
end = apr_time_now() + apr_time_from_sec(wait_sec); while (apr_time_now() < end) {
apr_thread_mutex_lock(workers->lock); if (!(n = workers->active_slots)) {
apr_thread_mutex_unlock(workers->lock); break;
}
wake_all_idles(workers);
rv = apr_thread_cond_timedwait(workers->all_done, workers->lock, timeout);
apr_thread_mutex_unlock(workers->lock);
if (APR_TIMEUP == rv) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s,
APLOGNO(10290) "h2_workers: waiting for workers to close, " "still seeing %d workers (%d idle) living",
workers->active_slots, workers->idle_slots);
}
} if (n) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, workers->s,
APLOGNO(10291) "h2_workers: cleanup, %d workers (%d idle) " "did not exit after %d seconds.",
n, workers->idle_slots, wait_sec);
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s, "h2_workers: cleanup all workers terminated");
apr_thread_mutex_lock(workers->lock);
join_zombies(workers);
apr_thread_mutex_unlock(workers->lock);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, workers->s, "h2_workers: cleanup zombie workers joined");
return APR_SUCCESS;
}
h2_workers *h2_workers_create(server_rec *s, apr_pool_t *pchild, int max_slots, int min_active,
apr_time_t idle_limit)
{
apr_status_t rv;
h2_workers *workers;
apr_pool_t *pool;
apr_allocator_t *allocator; int locked = 0;
apr_uint32_t i;
/* let's have our own pool that will be parent to all h2_worker * instances we create. This happens in various threads, but always * guarded by our lock. Without this pool, all subpool creations would * happen on the pool handed to us, which we do not guard.
*/
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) { return NULL;
}
/* 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;
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;
}
/* activate the min amount of workers */ for (i = 0; i < workers->min_active; ++i) {
rv = activate_slot(workers); if (rv != APR_SUCCESS) goto cleanup;
}
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 * pools (children of workers->pool) so that they are not destroyed * before/under us.
*/
apr_pool_pre_cleanup_register(pchild, workers, workers_pool_cleanup); return workers;
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, "h2_workers: errors initializing"); return NULL;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.