Apache HTTPD
h2_workers.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <assert.h>
18#include <apr_ring.h>
19#include <apr_thread_mutex.h>
20#include <apr_thread_cond.h>
21
22#include <mpm_common.h>
23#include <httpd.h>
24#include <http_connection.h>
25#include <http_core.h>
26#include <http_log.h>
27#include <http_protocol.h>
28
29#include "h2.h"
30#include "h2_private.h"
31#include "h2_mplx.h"
32#include "h2_c2.h"
33#include "h2_workers.h"
34#include "h2_util.h"
35
41
52
53
59
60typedef struct h2_slot h2_slot;
74
104
105
106static void* APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx);
107
109{
110 h2_slot *slot;
112 apr_status_t rv;
113
114 if (APR_RING_EMPTY(&workers->free, h2_slot, link)) {
115 return APR_EAGAIN;
116 }
118 ap_assert(slot->state == H2_SLOT_FREE);
119 APR_RING_REMOVE(slot, link);
120
122 "h2_workers: activate slot %d", slot->id);
123
124 slot->state = H2_SLOT_RUN;
125 slot->should_shutdown = 0;
126 slot->is_idle = 0;
127 slot->pool = NULL;
130 if (APR_SUCCESS != rv) goto cleanup;
131 apr_pool_tag(pool, "h2_worker_slot");
132 slot->pool = pool;
133
136
137cleanup:
138 if (rv != APR_SUCCESS) {
140 slot->state = H2_SLOT_FREE;
141 if (slot->pool) {
142 apr_pool_destroy(slot->pool);
143 slot->pool = NULL;
144 }
147 }
148 return rv;
149}
150
152{
153 h2_slot *slot;
155
156 while (!APR_RING_EMPTY(&workers->zombie, h2_slot, link)) {
158 APR_RING_REMOVE(slot, link);
159 ap_assert(slot->state == H2_SLOT_ZOMBIE);
160 ap_assert(slot->thread != NULL);
161
163 apr_thread_join(&status, slot->thread);
165
166 slot->thread = NULL;
167 slot->state = H2_SLOT_FREE;
168 if (slot->pool) {
169 apr_pool_destroy(slot->pool);
170 slot->pool = NULL;
171 }
173 }
174}
175
177{
178 if (!APR_RING_EMPTY(&workers->idle, h2_slot, link)) {
179 h2_slot *slot;
182 slot = APR_RING_NEXT(slot, link)) {
183 if (slot->is_idle && !slot->should_shutdown) {
184 apr_thread_cond_signal(slot->more_work);
185 slot->is_idle = 0;
186 return;
187 }
188 }
189 }
193 }
194}
195
200{
201 h2_workers *workers = slot->workers;
202 conn_rec *c = NULL;
203 ap_conn_producer_t *prod;
204 int has_more;
205
206 slot->prod = NULL;
208 slot->prod = prod = APR_RING_FIRST(&workers->prod_active);
209 APR_RING_REMOVE(prod, link);
211
212 c = prod->fn_next(prod->baton, &has_more);
213 if (c && has_more) {
216 }
217 else {
218 prod->state = PROD_IDLE;
220 }
221 if (c) {
222 ++prod->conns_active;
223 }
224 }
225
226 return c;
227}
228
229static void* APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx)
230{
231 h2_slot *slot = wctx;
232 h2_workers *workers = slot->workers;
233 conn_rec *c;
234 apr_status_t rv;
235
237 slot->state = H2_SLOT_RUN;
238 ++slot->activations;
240 for(;;) {
241 if (APR_RING_NEXT(slot, link) != slot) {
242 /* slot is part of the idle ring from the last loop */
243 APR_RING_REMOVE(slot, link);
245 }
246 slot->is_idle = 0;
247
248 if (!workers->aborted && !slot->should_shutdown) {
250 do {
251 c = get_next(slot);
252 if (!c) {
253 break;
254 }
256 /* See the discussion at <https://github.com/icing/mod_h2/issues/195>
257 *
258 * Each conn_rec->id is supposed to be unique at a point in time. Since
259 * some modules (and maybe external code) uses this id as an identifier
260 * for the request_rec they handle, it needs to be unique for secondary
261 * connections also.
262 *
263 * The MPM module assigns the connection ids and mod_unique_id is using
264 * that one to generate identifier for requests. While the implementation
265 * works for HTTP/1.x, the parallel execution of several requests per
266 * connection will generate duplicate identifiers on load.
267 *
268 * The original implementation for secondary connection identifiers used
269 * to shift the master connection id up and assign the stream id to the
270 * lower bits. This was cramped on 32 bit systems, but on 64bit there was
271 * enough space.
272 *
273 * As issue 195 showed, mod_unique_id only uses the lower 32 bit of the
274 * connection id, even on 64bit systems. Therefore collisions in request ids.
275 *
276 * The way master connection ids are generated, there is some space "at the
277 * top" of the lower 32 bits on allmost all systems. If you have a setup
278 * with 64k threads per child and 255 child processes, you live on the edge.
279 *
280 * The new implementation shifts 8 bits and XORs in the worker
281 * id. This will experience collisions with > 256 h2 workers and heavy
282 * load still. There seems to be no way to solve this in all possible
283 * configurations by mod_h2 alone.
284 */
285 if (c->master) {
286 c->id = (c->master->id << 8)^slot->id;
287 }
288 c->current_thread = thread;
289 AP_DEBUG_ASSERT(slot->prod);
290
291#if AP_HAS_RESPONSE_BUCKETS
293#else
294 h2_c2_process(c, thread, slot->id);
295#endif
296 slot->prod->fn_done(slot->prod->baton, c);
297
299 if (--slot->prod->conns_active <= 0) {
301 }
302 if (slot->prod->state == PROD_IDLE) {
303 APR_RING_REMOVE(slot->prod, link);
304 slot->prod->state = PROD_ACTIVE;
306 }
307
308 } while (!workers->aborted && !slot->should_shutdown);
309 APR_RING_REMOVE(slot, link); /* no longer busy */
310 }
311
312 if (workers->aborted || slot->should_shutdown) {
313 break;
314 }
315
317
318 /* we are idle */
321 slot->is_idle = 1;
322 if (slot->id >= workers->min_active && workers->idle_limit > 0) {
323 rv = apr_thread_cond_timedwait(slot->more_work, workers->lock,
325 if (APR_TIMEUP == rv) {
326 APR_RING_REMOVE(slot, link);
329 "h2_workers: idle timeout slot %d in state %d (%d activations)",
330 slot->id, slot->state, slot->activations);
331 break;
332 }
333 }
334 else {
336 }
337 }
338
340 "h2_workers: terminate slot %d in state %d (%d activations)",
341 slot->id, slot->state, slot->activations);
342 slot->is_idle = 0;
343 slot->state = H2_SLOT_ZOMBIE;
344 slot->should_shutdown = 0;
347 if (workers->active_slots <= 0) {
349 }
351
353 return NULL;
354}
355
357{
358 h2_slot *slot;
361 slot = APR_RING_NEXT(slot, link))
362 {
363 apr_thread_cond_signal(slot->more_work);
364 }
365}
366
368{
371 apr_status_t rv;
372 int n = 0, wait_sec = 5;
373
375 "h2_workers: cleanup %d workers (%d idle)",
378 workers->shutdown = 1;
379 workers->aborted = 1;
382
383 /* wait for all the workers to become zombies and join them.
384 * this gets called after the mpm shuts down and all connections
385 * have either been handled (graceful) or we are forced exiting
386 * (ungrateful). Either way, we show limited patience. */
388 while (apr_time_now() < end) {
390 if (!(n = workers->active_slots)) {
392 break;
393 }
397
398 if (APR_TIMEUP == rv) {
400 APLOGNO(10290) "h2_workers: waiting for workers to close, "
401 "still seeing %d workers (%d idle) living",
403 }
404 }
405 if (n) {
407 APLOGNO(10291) "h2_workers: cleanup, %d workers (%d idle) "
408 "did not exit after %d seconds.",
410 }
412 "h2_workers: cleanup all workers terminated");
417 "h2_workers: cleanup zombie workers joined");
418
419 return APR_SUCCESS;
420}
421
423 int max_slots, int min_active,
424 apr_time_t idle_limit)
425{
426 apr_status_t rv;
430 int locked = 0;
432
433 ap_assert(s);
435 ap_assert(idle_limit > 0);
436
437 /* let's have our own pool that will be parent to all h2_worker
438 * instances we create. This happens in various threads, but always
439 * guarded by our lock. Without this pool, all subpool creations would
440 * happen on the pool handed to us, which we do not guard.
441 */
443 if (rv != APR_SUCCESS) {
444 goto cleanup;
445 }
447 if (rv != APR_SUCCESS) {
449 goto cleanup;
450 }
452 apr_pool_tag(pool, "h2_workers");
454 if (!workers) {
455 return NULL;
456 }
457
458 workers->s = s;
459 workers->pool = pool;
460 workers->min_active = min_active;
461 workers->max_slots = max_slots;
462 workers->idle_limit = idle_limit;
464
466 "h2_workers: created with min=%d max=%d idle_ms=%d",
468 (int)apr_time_as_msec(idle_limit));
469
474
477
479 if (rv != APR_SUCCESS) goto cleanup;
480
481 if (ap_thread_stacksize != 0) {
485 "h2_workers: using stacksize=%ld",
486 (long)ap_thread_stacksize);
487 }
488
491 workers->pool);
492 if (rv != APR_SUCCESS) goto cleanup;
494 if (rv != APR_SUCCESS) goto cleanup;
496 if (rv != APR_SUCCESS) goto cleanup;
497
499 locked = 1;
500
501 /* create the slots and put them on the free list */
503
504 for (i = 0; i < workers->max_slots; ++i) {
505 workers->slots[i].id = i;
511 if (rv != APR_SUCCESS) goto cleanup;
512 }
513
514 /* activate the min amount of workers */
515 for (i = 0; i < workers->min_active; ++i) {
517 if (rv != APR_SUCCESS) goto cleanup;
518 }
519
520cleanup:
521 if (locked) {
523 }
524 if (rv == APR_SUCCESS) {
525 /* Stop/join the workers threads when the MPM child exits (pchild is
526 * destroyed), and as a pre_cleanup of pchild thus before the threads
527 * pools (children of workers->pool) so that they are not destroyed
528 * before/under us.
529 */
531 return workers;
532 }
534 "h2_workers: errors initializing");
535 return NULL;
536}
537
542
544{
545 ap_conn_producer_t *prod;
546
549 "h2_workers: shutdown graceful=%d", graceful);
550 workers->shutdown = 1;
553 for (prod = APR_RING_FIRST(&workers->prod_idle);
555 prod = APR_RING_NEXT(prod, link)) {
556 if (prod->fn_shutdown) {
557 prod->fn_shutdown(prod->baton, graceful);
558 }
559 }
561}
562
565 const char *name,
566 ap_conn_producer_next *fn_next,
567 ap_conn_producer_done *fn_done,
568 ap_conn_producer_shutdown *fn_shutdown,
569 void *baton)
570{
571 ap_conn_producer_t *prod;
572
573 prod = apr_pcalloc(producer_pool, sizeof(*prod));
574 APR_RING_ELEM_INIT(prod, link);
575 prod->name = name;
576 prod->fn_next = fn_next;
577 prod->fn_done = fn_done;
578 prod->fn_shutdown = fn_shutdown;
579 prod->baton = baton;
580
582 prod->state = PROD_IDLE;
585
586 return prod;
587}
588
590{
592
594 if (PROD_JOINED == prod->state) {
595 AP_DEBUG_ASSERT(APR_RING_NEXT(prod, link) == prod); /* should be in no ring */
596 rv = APR_EINVAL;
597 }
598 else {
599 AP_DEBUG_ASSERT(PROD_ACTIVE == prod->state || PROD_IDLE == prod->state);
600 APR_RING_REMOVE(prod, link);
601 prod->state = PROD_JOINED; /* prevent further activations */
602 while (prod->conns_active > 0) {
604 }
605 APR_RING_ELEM_INIT(prod, link); /* make it link to itself */
606 }
608 return rv;
609}
610
612{
615 if (PROD_IDLE == prod->state) {
616 APR_RING_REMOVE(prod, link);
617 prod->state = PROD_ACTIVE;
620 }
621 else if (PROD_JOINED == prod->state) {
622 rv = APR_EINVAL;
623 }
625 return rv;
626}
int n
Definition ap_regex.h:278
APR Rings.
APR Condition Variable Routines.
APR Thread Mutex Routines.
void ap_process_connection(conn_rec *c, void *csd)
Definition connection.c:210
apr_socket_t * ap_get_conn_socket(conn_rec *c)
Definition core.c:5202
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_INFO
Definition http_log.h:70
#define APLOG_TRACE3
Definition http_log.h:74
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
#define APLOG_TRACE1
Definition http_log.h:72
#define APLOG_DEBUG
Definition http_log.h:71
ap_vhost_iterate_conn_cb void * baton
Definition http_vhost.h:87
apr_size_t ap_thread_stacksize
Definition mpm_common.c:156
#define APR_EAGAIN
Definition apr_errno.h:730
#define APR_TIMEUP
Definition apr_errno.h:450
#define APR_EINVAL
Definition apr_errno.h:711
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
#define ap_assert(exp)
Definition httpd.h:2271
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
void * data
void const char apr_status_t(* cleanup)(void *))
apr_vformatter_buff_t * c
Definition apr_lib.h:175
apr_abortfunc_t apr_allocator_t * allocator
Definition apr_pools.h:208
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
#define APR_RING_ENTRY(elem)
Definition apr_ring.h:70
#define APR_RING_INSERT_TAIL(hp, nep, elem, link)
Definition apr_ring.h:328
#define APR_RING_INIT(hp, elem, link)
Definition apr_ring.h:192
#define APR_RING_HEAD(head, elem)
Definition apr_ring.h:91
#define APR_RING_SENTINEL(hp, elem, link)
Definition apr_ring.h:159
#define APR_RING_EMPTY(hp, elem, link)
Definition apr_ring.h:204
#define APR_RING_REMOVE(ep, link)
Definition apr_ring.h:381
#define APR_RING_FIRST(hp)
Definition apr_ring.h:166
#define APR_RING_NEXT(ep, link)
Definition apr_ring.h:177
#define APR_RING_ELEM_INIT(ep, link)
Definition apr_ring.h:212
const char char ** end
const char * s
Definition apr_strings.h:95
int int status
#define apr_time_as_msec(time)
Definition apr_time.h:72
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_from_sec(sec)
Definition apr_time.h:78
static struct h2_workers * workers
Definition h2_c1.c:48
apr_status_t h2_c2_process(conn_rec *c2, apr_thread_t *thread, int worker_id)
Definition h2_c2.c:660
static apr_pool_t * pchild
Definition h2_mplx.c:74
ap_conn_producer_t * h2_workers_register(h2_workers *workers, apr_pool_t *producer_pool, const char *name, ap_conn_producer_next *fn_next, ap_conn_producer_done *fn_done, ap_conn_producer_shutdown *fn_shutdown, void *baton)
Definition h2_workers.c:563
apr_uint32_t h2_workers_get_max_workers(h2_workers *workers)
Definition h2_workers.c:538
static apr_status_t activate_slot(h2_workers *workers)
Definition h2_workers.c:108
apr_status_t h2_workers_activate(h2_workers *workers, ap_conn_producer_t *prod)
Definition h2_workers.c:611
apr_status_t h2_workers_join(h2_workers *workers, ap_conn_producer_t *prod)
Definition h2_workers.c:589
static apr_status_t workers_pool_cleanup(void *data)
Definition h2_workers.c:367
void h2_workers_shutdown(h2_workers *workers, int graceful)
Definition h2_workers.c:543
static conn_rec * get_next(h2_slot *slot)
Definition h2_workers.c:199
static void join_zombies(h2_workers *workers)
Definition h2_workers.c:151
h2_workers * h2_workers_create(server_rec *s, apr_pool_t *pchild, int max_slots, int min_active, apr_time_t idle_limit)
Definition h2_workers.c:422
static void wake_idle_worker(h2_workers *workers, ap_conn_producer_t *prod)
Definition h2_workers.c:176
h2_slot_state_t
Definition h2_workers.c:54
@ H2_SLOT_FREE
Definition h2_workers.c:55
@ H2_SLOT_RUN
Definition h2_workers.c:56
@ H2_SLOT_ZOMBIE
Definition h2_workers.c:57
static void *APR_THREAD_FUNC slot_run(apr_thread_t *thread, void *wctx)
Definition h2_workers.c:229
static void wake_all_idles(h2_workers *workers)
Definition h2_workers.c:356
prod_state_t
Definition h2_workers.c:36
@ PROD_ACTIVE
Definition h2_workers.c:38
@ PROD_JOINED
Definition h2_workers.c:39
@ PROD_IDLE
Definition h2_workers.c:37
conn_rec * ap_conn_producer_next(void *baton, int *pmore)
Definition h2_workers.h:80
void ap_conn_producer_done(void *baton, conn_rec *conn)
Definition h2_workers.h:87
void ap_conn_producer_shutdown(void *baton, int graceful)
Definition h2_workers.h:94
Apache connection library.
CORE HTTP Daemon.
Apache Logging library.
HTTP protocol handling.
HTTP Daemon routines.
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
Multi-Processing Modules functions.
apr_status_t apr_thread_exit(apr_thread_t *thd, apr_status_t retval)
Definition thread.c:157
apr_status_t apr_thread_join(apr_status_t *retval, apr_thread_t *thd)
Definition thread.c:166
apr_status_t apr_threadattr_create(apr_threadattr_t **new, apr_pool_t *pool)
Definition thread.c:24
char * name
const char * name
Definition h2_workers.c:44
ap_conn_producer_shutdown * fn_shutdown
Definition h2_workers.c:48
struct ap_conn_producer_t::@19 link
ap_conn_producer_done * fn_done
Definition h2_workers.c:47
volatile prod_state_t state
Definition h2_workers.c:49
volatile int conns_active
Definition h2_workers.c:50
ap_conn_producer_next * fn_next
Definition h2_workers.c:46
Structure to store things which are per connection.
Definition httpd.h:1152
struct h2_slot::@20 link
volatile int should_shutdown
Definition h2_workers.c:66
h2_slot_state_t state
Definition h2_workers.c:65
volatile int is_idle
Definition h2_workers.c:67
h2_workers * workers
Definition h2_workers.c:68
int activations
Definition h2_workers.c:72
apr_pool_t * pool
Definition h2_workers.c:64
ap_conn_producer_t * prod
Definition h2_workers.c:69
apr_uint32_t id
Definition h2_workers.c:63
struct apr_thread_cond_t * more_work
Definition h2_workers.c:71
apr_thread_t * thread
Definition h2_workers.c:70
h2_slot * slots
Definition h2_workers.c:90
struct h2_workers::ap_conn_producer_idle prod_idle
struct h2_workers::h2_slots_idle idle
struct apr_thread_cond_t * all_done
Definition h2_workers.c:102
apr_pool_t * pool
Definition h2_workers.c:77
volatile apr_uint32_t active_slots
Definition h2_workers.c:86
server_rec * s
Definition h2_workers.c:76
struct h2_workers::ap_conn_producer_active prod_active
struct apr_thread_mutex_t * lock
Definition h2_workers.c:100
volatile int aborted
Definition h2_workers.c:82
volatile apr_uint32_t idle_slots
Definition h2_workers.c:87
apr_threadattr_t * thread_attr
Definition h2_workers.c:89
volatile int shutdown
Definition h2_workers.c:83
apr_uint32_t max_slots
Definition h2_workers.c:79
struct apr_thread_cond_t * prod_done
Definition h2_workers.c:101
volatile apr_time_t idle_limit
Definition h2_workers.c:81
apr_uint32_t min_active
Definition h2_workers.c:80
struct h2_workers::h2_slots_zombie zombie
struct h2_workers::h2_slots_busy busy
struct h2_workers::h2_slots_free free
A structure to store information for each virtual server.
Definition httpd.h:1322
IN ULONG IN INT timeout