On 09/22/2009 09:19 PM, Stefan Fritsch wrote: > On Sunday 13 September 2009, Stefan Fritsch wrote: >> On Sunday 13 September 2009, Ruediger Pluem wrote: >>> On 09/13/2009 01:11 PM, Stefan Fritsch wrote: >>>> http://httpd.apache.org/docs/trunk/developer/output-filters.htm >>>> l recommends to reuse bucket brigades and to not use >>>> apr_brigade_destroy. However, both in 2.2 and in trunk, the >>>> core output filter sometimes calls apr_brigade_destroy on >>>> brigades that it has received down the chain from earlier >>>> output filters. Is this not bound to cause problems since the >>>> brigade's pool cleanup is then removed but the brigade is still >>>> reused later on? >>> It could be. But the questions is if it is really reused later >>> on. >> Since this is the recommended design for output filters, I am sure >> it can happen. > > I have attached two patches: > > In the first, I change (hopefully) all places to not > apr_brigade_destroy brigades that have been passed down the filter > chain. Especially the core_output_filter change needs some review. > > In the second, I have changed all occurences of apr_brigade_split to > apr_brigade_split_ex and I have made some more changes where bucket > brigades can be reused. > > The test suite shows no regressions.
Index: server/protocol.c =================================================================== --- server/protocol.c (Revision 818232) +++ server/protocol.c (Arbeitskopie) @@ -1239,6 +1239,7 @@ * least one bucket on to the next output filter * for this request */ + apr_bucket_brigade *tmpbb; }; /* This filter computes the content length, but it also computes the number @@ -1259,6 +1260,8 @@ if (!ctx) { f->ctx = ctx = apr_palloc(r->pool, sizeof(*ctx)); ctx->data_sent = 0; + ctx->tmpbb = apr_brigade_create(r->connection->pool, This should be r->pool instead. The lifetime of ctx itself is limited to r->pool + r->connection->bucket_alloc); } @@ -1433,24 +1431,38 @@ if (f == NULL) { /* our filter hasn't been added yet */ ctx = apr_pcalloc(r->pool, sizeof(*ctx)); + ctx->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + ap_add_output_filter("OLD_WRITE", ctx, r, r->connection); f = r->output_filters; } + return f; +} + +static apr_status_t buffer_output(request_rec *r, + const char *str, apr_size_t len) +{ + conn_rec *c = r->connection; + ap_filter_t *f; + old_write_filter_ctx *ctx; + + if (len == 0) + return APR_SUCCESS; + + f = insert_old_write_filter(r); + ctx = f->ctx; + /* if the first filter is not our buffering filter, then we have to * deliver the content through the normal filter chain */ if (f != r->output_filters) { - apr_bucket_brigade *bb = apr_brigade_create(r->pool, c->bucket_alloc); apr_bucket *b = apr_bucket_transient_create(str, len, c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); + APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, b); - return ap_pass_brigade(r->output_filters, bb); + return ap_pass_brigade(r->output_filters, ctx->tmpbb); To be on the save side I think an apr_brigade_cleanup(ctx->tmpbb) is due before doing the return, just in case a filter did not consume the buckets properly. } @@ -1605,13 +1617,17 @@ AP_DECLARE(int) ap_rflush(request_rec *r) { conn_rec *c = r->connection; - apr_bucket_brigade *bb; apr_bucket *b; + ap_filter_t *f; + old_write_filter_ctx *ctx; - bb = apr_brigade_create(r->pool, c->bucket_alloc); + f = insert_old_write_filter(r); + ctx = f->ctx; + b = apr_bucket_flush_create(c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(bb, b); - if (ap_pass_brigade(r->output_filters, bb) != APR_SUCCESS) + APR_BRIGADE_INSERT_TAIL(ctx->tmpbb, b); + + if (ap_pass_brigade(r->output_filters, ctx->tmpbb) != APR_SUCCESS) Same as above: We should call apr_brigade_cleanup(ctx->tmpbb) to be on the save side. return -1; return 0; --- modules/http/chunk_filter.c (Revision 818232) +++ modules/http/chunk_filter.c (Arbeitskopie) @@ -49,11 +49,11 @@ #define ASCII_CRLF "\015\012" #define ASCII_ZERO "\060" conn_rec *c = f->r->connection; - apr_bucket_brigade *more; + apr_bucket_brigade *more, *tmp; apr_bucket *e; apr_status_t rv; - for (more = NULL; b; b = more, more = NULL) { + for (more = tmp = NULL; b; tmp = b, b = more, more = NULL) { apr_off_t bytes = 0; apr_bucket *eos = NULL; apr_bucket *flush = NULL; @@ -85,7 +85,8 @@ if (APR_BUCKET_IS_FLUSH(e)) { flush = e; if (e != APR_BRIGADE_LAST(b)) { - more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); + more = apr_brigade_split_ex(b, APR_BUCKET_NEXT(e), tmp); + tmp = NULL; } break; } @@ -105,7 +106,8 @@ * block so we pass down what we have so far. */ bytes += len; - more = apr_brigade_split(b, APR_BUCKET_NEXT(e)); + more = apr_brigade_split_ex(b, APR_BUCKET_NEXT(e), tmp); + tmp = NULL; break; } else { What is the point here? tmp is always NULL when passed to apr_brigade_split_ex so apr_brigade_split_ex == apr_brigade_split --- modules/filters/mod_sed.c (Revision 818232) +++ modules/filters/mod_sed.c (Arbeitskopie) @@ -435,11 +440,9 @@ * the question is where to add it? */ while (APR_BRIGADE_EMPTY(ctx->bb)) { - apr_bucket_brigade *bbinp; apr_bucket *b; /* read the bytes from next level filter */ - bbinp = apr_brigade_create(f->r->pool, f->c->bucket_alloc); status = ap_get_brigade(f->next, bbinp, mode, block, readbytes); if (status != APR_SUCCESS) { return status; @@ -470,19 +473,16 @@ } } apr_brigade_cleanup(bbinp); This one should be moved *before* the ap_get_brigade. It ensures that we always call ap_get_brigade with an empty brigade. - apr_brigade_destroy(bbinp); } Regards RĂ¼diger