You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@httpd.apache.org by Greg Stein <gs...@lyra.org> on 2000/06/03 23:26:16 UTC

recap of filtered I/O

On Sat, 3 Jun 2000 rbb@covalent.net wrote:
>...
> Yep, your making perfect sense.  I hope to have a new patch next week
> sometime that addresses all of Greg's points and still uses the hook
> mechanism.  :-)

My current concerns with the hook mechanism:

*) complexity for the module programmer due to iovec[] strategem

*) how would mod_include.c::include_cgi() be written in a hook scheme?

*) how would my insert-100Mb-filter be written?

*) if the filter wants to insert a file, then how do we get the FD
   returned to Apache so that it can use sendfile/TransmitFile?

*) flow control

*) how would "SetFilter SSI PHP" be implemented in the hook scheme?
   (note this implies a table mapping names to functions; also, I believe
    that solving this directive for the hook-based scheme will essentially
    look just like the link-based scheme)

*) hook-based scheme potentially has a larger working set because the
   iovec[] return values must occur on the heap
   (in the link-based scheme, they may occur on the heap or temporarily on
    the stack)


Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/


Re: recap of filtered I/O

Posted by Greg Stein <gs...@lyra.org>.
On Sun, 4 Jun 2000, Life is hard, and then you die wrote:
> On Sun, Jun 04, 2000 at 04:16:21PM -0700, Greg Stein wrote:
>...
> > [ both schemes can handle the "wait for the rest of the input" scenario; I
> >   believe that the link-based approach is a bit easier, it that it
> >   provides an explicit context pointer for maintaining this data; the
> >   hook-based approach requires the filter to attach "user data" to the
> >   request (which then prevents "SetFilters SSI PHP SSI" because there is
> >   only one "SSI user data slot" ]
> 
> Agreed - using the stack to hold the context is definitely easier.
> However, even the layer approach needs to attach user data to the
> request to keep state accross function calls (e.g. mod_include needs to
> remember the "<!--#inc").

The layer structure looks like:

  struct ap_layer_t {
      ap_layer_func_t *callback;
      void *context;
      struct ap_layer_t *next;
      request_rec *r;
  }

Each "installed" layer thus has its own context for retaining state across
invocations. Thus, if you have a particular layer installed more than
once, then you have matching context pointers for each one.

In the "store it in the request 'user data'" case, you can only store it
once. If you will: it is a global, with the associated single-use
problems.

> > > > *) if the filter wants to insert a file, then how do we get the FD
> > > >    returned to Apache so that it can use sendfile/TransmitFile?
> > > 
> > > This is something that's not clear to me in *either* scheme. Can you
> > > elaborate how this should be done using layers?
> > 
> > Quite easily :-)
> > 
> >   mod_include::filter_callback(this_layer, buf, len):
> >      ...
> >      ap_lwrite(this_layer->next, plain_text)
> >      ... oh! found a #include ...
> >      ap_lsend_fd(this_layer->next, fd)
> >      ...
> >      ap_lwrite(this_layer->next, plain_text)
> > 
> > Under the covers, ap_lsend_fd() looks like:
> > 
> >   ap_lsend_fd(next_layer, fd):
> >      if next_layer == NULL:
> >         ap_send_fd(fd)
> >      else:
> >         mmap_thingy map = mmap_file(fd)
> >         invoke_callback(next_layer, map.buf, map.len)
> 
> Eeek! The problem with this scheme is that ap_send_fd will *never* be
> used for any HTTP/1.1 responses, assuming chunking will be a layer.
> Also, somebody just wants to add a header and trailer - bam, mmap
> instead of sendfile. No, there needs to be more intelligence here,
> along the lines of what Roy suggested: typed buffers (char*, fd, sd,
> etc), with ways to read from those if necessary.

Agreed.

Typed buffers would offer more possibilities of optimization here. This
does start to look a bit like the complex "iovec" thingy from the
hook-based scheme. However, [compared to the hook scheme] the control flow
makes it a bit simpler -- you accept an iovec and deal with it accordingly
(wrap some chunking around it, prepend/append text, etc). I don't like
this as much due to the complexity, but for serious perf issues it may be
necessary. In the hook-based scheme, these iovecs are in/out parameters,
which make them a bit harder to work with.

Why harder? Well, consider the following:

  my_layer_callback(layer, items[]):
    ap_lwrite(layer, header_text)
    ap_lwritev(layer, items)
    ap_lwrite(layer, trailer_text)

conversely:

  my_filter(items[]):
    new_items = alloc(len(items) + 2)
    new_items[0] = ap_pstrdup(header_text)
    memcpy(&new_items[1], items, some_size)
    new_items[len(items)-1] = ap_pstrdup(trailer_text)
    *items = new_items


eesh... :-)

(and note the allocs in there)

>...
> > > > *) how would "SetFilter SSI PHP" be implemented in the hook scheme?
> > > >    (note this implies a table mapping names to functions; also, I believe
> > > >     that solving this directive for the hook-based scheme will essentially
> > > >     look just like the link-based scheme)
> > > 
> > > I think this is a minor point. The hook scheme allows you to order a specific
> > > filter after another one. Yes, it essentially degrades to a linked-list.
> > 
> > Actually, I don't believe it is all that minor. If the typical behavior is
> > to order them, and this degrades to a linked-list, then why are we
> > bothering with the hook-based scheme and its iovec complexity? (among my
> > other concerns raised in the recap)
> 
> I see two issues here which are getting confused: one is how to determine and
> setup the list of filters/layers,

I think we're pretty well in agreement that both schemes can handle the
determination and setup. IMO, it is a bit simpler for the link-based
mechanism, but I do agree that it is possible to write covers (and etc)
for the hook scheme to keep things reasonably simple for the user and for
most module writers.

> the other is whether to use filters or
> layers. These are independent AKAICT. Even the hook scheme eventually produces
> an ordered list, so question is just how to produce that list. Hmm, I guess
> I've also been assuming that the hooks stuff was just to get a list of filters,
> and ap_run_hook wouldn't actually be used,

As far as I know, the design has been to use ap_run_hook. If you don't use
that, then you really *do* have something like the linked-list scheme.

>...
> Ok, I see your point. However, we need both things: "external" configuration
> by the core (the "SetFilter"), and "internal" configuration by each module
> (mod_auth_digest parses the request headers and figures out from that
> whether it needs to add a layer, similarly for content-encoders,
> transfer-encoders, content-md5, etc).

Agreed. Modules can always insert themselves manually by virtue of the
(new) "install_filters" hook. External direcives can also insert and order
filters.

> I guess the way I see it is that during the post_read_request phase the
> modules register which filters/layers to use (along with the filter

In both schemes, we plan to introduce a "install_filters" hook that runs
just before the "handler" phase. This hook allows the modules to examine

the request/response and base a filter-insertion on the values
found.

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/


Re: recap of filtered I/O

Posted by "Life is hard, and then you die" <ro...@innovation.ch>.
On Sun, Jun 04, 2000 at 04:16:21PM -0700, Greg Stein wrote:
> On Sun, 4 Jun 2000, Life is hard, and then you die wrote:
> >...
> > On Sat, Jun 03, 2000 at 02:26:16PM -0700, Greg Stein wrote:
> >...
> > > *) how would my insert-100Mb-filter be written?
> > 
> > I Ryan addressed this point with the filter being able to signal that
> > it hasn't sent all yet.
> 
> In the hook-based scheme, the hook is only called when the
> content-generator writes some output (e.g. via ap_rwrite()). If the filter
> isn't going to write all the data at that point, then when will it?
> 
> content-generator:
>     ap_rwrite("foobar<!--#giant-data -->bleek");
>     ap_rwrite("more stuff");
> 
> That 100Meg needs to go out before the "bleek" can be written. Either, it
> gets completely dumped into an iovec[], or the hook calling will look
> like:
> 
> ap_rwrite:
>     iovec[0] = (buf, len)
>     run_hook(iovec, &more_to_write)
>     ap_bwrite(iovec)
>     while more_to_write:
>         iovec = empty
>         run_hook(iovec, &more_to_write)
>         ap_bwrite(iovec)
> 
> That just seems a bit complicated :-)
> 
> I'm not sure, but you might be confusing the "I have more to write" with
> the concept of "I haven't processed all the input yet."
[snip]

Hmm, yes, I guess I was confusing the two. And yes, the above was about
what I had thought would be done. Pretty? No.

> [ both schemes can handle the "wait for the rest of the input" scenario; I
>   believe that the link-based approach is a bit easier, it that it
>   provides an explicit context pointer for maintaining this data; the
>   hook-based approach requires the filter to attach "user data" to the
>   request (which then prevents "SetFilters SSI PHP SSI" because there is
>   only one "SSI user data slot" ]

Agreed - using the stack to hold the context is definitely easier.
However, even the layer approach needs to attach user data to the
request to keep state accross function calls (e.g. mod_include needs to
remember the "<!--#inc").

> > > *) if the filter wants to insert a file, then how do we get the FD
> > >    returned to Apache so that it can use sendfile/TransmitFile?
> > 
> > This is something that's not clear to me in *either* scheme. Can you
> > elaborate how this should be done using layers?
> 
> Quite easily :-)
> 
>   mod_include::filter_callback(this_layer, buf, len):
>      ...
>      ap_lwrite(this_layer->next, plain_text)
>      ... oh! found a #include ...
>      ap_lsend_fd(this_layer->next, fd)
>      ...
>      ap_lwrite(this_layer->next, plain_text)
> 
> Under the covers, ap_lsend_fd() looks like:
> 
>   ap_lsend_fd(next_layer, fd):
>      if next_layer == NULL:
>         ap_send_fd(fd)
>      else:
>         mmap_thingy map = mmap_file(fd)
>         invoke_callback(next_layer, map.buf, map.len)

Eeek! The problem with this scheme is that ap_send_fd will *never* be
used for any HTTP/1.1 responses, assuming chunking will be a layer.
Also, somebody just wants to add a header and trailer - bam, mmap
instead of sendfile. No, there needs to be more intelligence here,
along the lines of what Roy suggested: typed buffers (char*, fd, sd,
etc), with ways to read from those if necessary.

> > > *) flow control
> > 
> > I don't see this as really any different from the layer approach. But
> > I'm assuming that a filter/content-generator that creates large output
> > will do so in parts (third point above), and only smaller stuff will
> > slurp all up and send it in one write.
> 
> A hook-based filter never generates a block-on-the-network event. It
> stuffs everything into the iovec[], regardless of what is happening with
> the network.
[snip]

Like I said, I was assuming partial writes from above. If those don't
exist, then yes, I don't see the flow-control either.

> > > *) how would "SetFilter SSI PHP" be implemented in the hook scheme?
> > >    (note this implies a table mapping names to functions; also, I believe
> > >     that solving this directive for the hook-based scheme will essentially
> > >     look just like the link-based scheme)
> > 
> > I think this is a minor point. The hook scheme allows you to order a specific
> > filter after another one. Yes, it essentially degrades to a linked-list.
> 
> Actually, I don't believe it is all that minor. If the typical behavior is
> to order them, and this degrades to a linked-list, then why are we
> bothering with the hook-based scheme and its iovec complexity? (among my
> other concerns raised in the recap)

I see two issues here which are getting confused: one is how to determine and
setup the list of filters/layers, the other is whether to use filters or
layers. These are independent AKAICT. Even the hook scheme eventually produces
an ordered list, so question is just how to produce that list. Hmm, I guess
I've also been assuming that the hooks stuff was just to get a list of filters,
and ap_run_hook wouldn't actually be used,

> One other item here:
> 
> In both schemes, we call the (new) "install_filters" hook on the modules.
> In the hook-based scheme, this inserts per-request hooks. In the
> link-based scheme, this adds to a linked-list of layers.
> 
> One of the things about the hook-based scheme is the notion of "we don't
> care who registered in the per-request chain... we'll just call them; they
> might have ordered themselves." Well, this "opaqueness" of the callbacks
> totally disappears when you add support for the "SetFilter" directive.
> Why? Because modules are no longer adding private functions into the
> per-request chain. An external mechanism must find the function pointers
> and insert them appropriately.

Ok, I see your point. However, we need both things: "external" configuration
by the core (the "SetFilter"), and "internal" configuration by each module
(mod_auth_digest parses the request headers and figures out from that
whether it needs to add a layer, similarly for content-encoders,
transfer-encoders, content-md5, etc).

I guess the way I see it is that during the post_read_request phase the
modules register which filters/layers to use (along with the filter
type), and the core then orders the filters first according to type and
then according to additional directives such as "SetFilter". So in some
sense the opaqueness is still there - the filter modules don't care
about the order, as that's determined by something external to them (the
core).

> > OTOH, I've been thinking how this would integrate with Dean's proposal to
> > have a dedicated process/thread which serves slow clients (via some
> > select/poll based pseudo-asynch-io) so as to free up the processes/threads
> > for doing the content-generation/filtering work as quickly as possible,
> > which I think is a really cool idea.  With the iovec's it seems more
> > straightforward at first glance, because it would get all the data it
> > needs in one go. However, as soon as the filters can produce partial
> > output this reverts to what I'm guessing will be needed for the layer
> > scheme: a largish buffer into which the stuff is assembled (i.e copied),
> > which the slow-client-process can then dribble to the client at will. So
> > I'm not sure I see any advantage here using iovec's either. However, I
> > suspect I may be missing something, because I also don't see how sendfile
> > integrates into these schemes - I'm starting to think Roy's
> > bucket-brigades may be the way to go after all.
> 
> I do not believe you're missing anything either. In both schemes, they can
> take the (filtered) network output and pass that off to the delivery
> thread. If the response is fully generated and passed off, then the worker
> thread can start on another request/response. Both schemes could do this
> invisibly w.r.t. the filters.
> 
> There is no advantage to either scheme in terms of the delivery thread,
> presuming the "iovec" in the hook-based scheme is made a bit more complex.
> 
> I do believe the link-based approach has some simplicity advantages. The
> ability to pass a file descriptor to the network layer, for example. The
> network layer can pass that fd off to the delivery thread, which can then
> use sendfile() on it. In the hook-based system, when we say "iovec", we
> really mean something like Roy's bucket brigades -- each element would be
> typed as a buffer, as a file descriptor, or whatever. IMO, introducing the
> buckets into the system is more complex than different types of writes
> (e.g. ap_lwrite, ap_lsend_fd, ap_lwritev)

IMHO, the different types of write isn't sufficient - see above. Btw.,
viz assembling the data into a buffer for transmission, I suppose for
efficiency that could be assembled into an iovec instead, so the layer
approach need not be worse off in this respect (this implies that
modules aren't allowed to reuse their send buffers until after the
request has been sent, though).


  Cheers,

  Ronald


Re: recap of filtered I/O

Posted by Greg Stein <gs...@lyra.org>.
On Sun, 4 Jun 2000, Life is hard, and then you die wrote:
>...
> On Sat, Jun 03, 2000 at 02:26:16PM -0700, Greg Stein wrote:
>...
> > *) how would my insert-100Mb-filter be written?
> 
> I Ryan addressed this point with the filter being able to signal that
> it hasn't sent all yet.

In the hook-based scheme, the hook is only called when the
content-generator writes some output (e.g. via ap_rwrite()). If the filter
isn't going to write all the data at that point, then when will it?

content-generator:
    ap_rwrite("foobar<!--#giant-data -->bleek");
    ap_rwrite("more stuff");

That 100Meg needs to go out before the "bleek" can be written. Either, it
gets completely dumped into an iovec[], or the hook calling will look
like:

ap_rwrite:
    iovec[0] = (buf, len)
    run_hook(iovec, &more_to_write)
    ap_bwrite(iovec)
    while more_to_write:
        iovec = empty
        run_hook(iovec, &more_to_write)
        ap_bwrite(iovec)

That just seems a bit complicated :-)

I'm not sure, but you might be confusing the "I have more to write" with
the concept of "I haven't processed all the input yet."

The latter situation is for something like:

content-generator:
    ap_rwrite("<!--#inc");
    ap_rwrite("lude foo.html -->");

In the above scenario, mod_include is going to stash away the "<!--#inc"
part and wait for the rest of the command. At the end of the response, a
"flush" is performed. If mod_include has a partial command, then it raises
a syntax error (due to EOF) at that point.

[ both schemes can handle the "wait for the rest of the input" scenario; I
  believe that the link-based approach is a bit easier, it that it
  provides an explicit context pointer for maintaining this data; the
  hook-based approach requires the filter to attach "user data" to the
  request (which then prevents "SetFilters SSI PHP SSI" because there is
  only one "SSI user data slot" ]

> > *) if the filter wants to insert a file, then how do we get the FD
> >    returned to Apache so that it can use sendfile/TransmitFile?
> 
> This is something that's not clear to me in *either* scheme. Can you
> elaborate how this should be done using layers?

Quite easily :-)

  mod_include::filter_callback(this_layer, buf, len):
     ...
     ap_lwrite(this_layer->next, plain_text)
     ... oh! found a #include ...
     ap_lsend_fd(this_layer->next, fd)
     ...
     ap_lwrite(this_layer->next, plain_text)

Under the covers, ap_lsend_fd() looks like:

  ap_lsend_fd(next_layer, fd):
     if next_layer == NULL:
        ap_send_fd(fd)
     else:
        mmap_thingy map = mmap_file(fd)
        invoke_callback(next_layer, map.buf, map.len)

Essentially, if you are the last layer, then you can sendfile/TransmitFile
directly to the network. Otherwise, the file gets mmap'd in and passesd to
the next layer.

> > *) flow control
> 
> I don't see this as really any different from the layer approach. But
> I'm assuming that a filter/content-generator that creates large output
> will do so in parts (third point above), and only smaller stuff will
> slurp all up and send it in one write.

A hook-based filter never generates a block-on-the-network event. It
stuffs everything into the iovec[], regardless of what is happening with
the network.

Consider the following:

  my_layer_callback(layer, buf, len):
     while 1:
        buf = read_next_file_block()
        buf2 = do_some_processing(buf)
        ap_lwrite(layer, buf2)

In the above code, the ap_lwrite() can block on the network, so we pause
in reading the file.

In the hook-based scheme, the filter must read in the whole file, process
it, and return the processed data to the caller. Congestion on the network
will not slow this processing down.

> > *) how would "SetFilter SSI PHP" be implemented in the hook scheme?
> >    (note this implies a table mapping names to functions; also, I believe
> >     that solving this directive for the hook-based scheme will essentially
> >     look just like the link-based scheme)
> 
> I think this is a minor point. The hook scheme allows you to order a specific
> filter after another one. Yes, it essentially degrades to a linked-list.

Actually, I don't believe it is all that minor. If the typical behavior is
to order them, and this degrades to a linked-list, then why are we
bothering with the hook-based scheme and its iovec complexity? (among my
other concerns raised in the recap)

One other item here:

In both schemes, we call the (new) "install_filters" hook on the modules.
In the hook-based scheme, this inserts per-request hooks. In the
link-based scheme, this adds to a linked-list of layers.

One of the things about the hook-based scheme is the notion of "we don't
care who registered in the per-request chain... we'll just call them; they
might have ordered themselves." Well, this "opaqueness" of the callbacks
totally disappears when you add support for the "SetFilter" directive.
Why? Because modules are no longer adding private functions into the
per-request chain. An external mechanism must find the function pointers
and insert them appropriately.

>...
> OTOH, I've been thinking how this would integrate with Dean's proposal to
> have a dedicated process/thread which serves slow clients (via some
> select/poll based pseudo-asynch-io) so as to free up the processes/threads
> for doing the content-generation/filtering work as quickly as possible,
> which I think is a really cool idea.  With the iovec's it seems more
> straightforward at first glance, because it would get all the data it
> needs in one go. However, as soon as the filters can produce partial
> output this reverts to what I'm guessing will be needed for the layer
> scheme: a largish buffer into which the stuff is assembled (i.e copied),
> which the slow-client-process can then dribble to the client at will. So
> I'm not sure I see any advantage here using iovec's either. However, I
> suspect I may be missing something, because I also don't see how sendfile
> integrates into these schemes - I'm starting to think Roy's
> bucket-brigades may be the way to go after all.

I do not believe you're missing anything either. In both schemes, they can
take the (filtered) network output and pass that off to the delivery
thread. If the response is fully generated and passed off, then the worker
thread can start on another request/response. Both schemes could do this
invisibly w.r.t. the filters.

There is no advantage to either scheme in terms of the delivery thread,
presuming the "iovec" in the hook-based scheme is made a bit more complex.

I do believe the link-based approach has some simplicity advantages. The
ability to pass a file descriptor to the network layer, for example. The
network layer can pass that fd off to the delivery thread, which can then
use sendfile() on it. In the hook-based system, when we say "iovec", we
really mean something like Roy's bucket brigades -- each element would be
typed as a buffer, as a file descriptor, or whatever. IMO, introducing the
buckets into the system is more complex than different types of writes
(e.g. ap_lwrite, ap_lsend_fd, ap_lwritev)

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/


Re: recap of filtered I/O

Posted by "Life is hard, and then you die" <ro...@innovation.ch>.
Note: the linking of filter/layers via hook's or linked lists is really
independent of the underlying scheme, hence I will use the term filters
for Ryans filtering proposal, and layers for Greg's proposal.

On Sat, Jun 03, 2000 at 02:26:16PM -0700, Greg Stein wrote:
> On Sat, 3 Jun 2000 rbb@covalent.net wrote:
> >...
> > Yep, your making perfect sense.  I hope to have a new patch next week
> > sometime that addresses all of Greg's points and still uses the hook
> > mechanism.  :-)
> 
> My current concerns with the hook mechanism:
> 
> *) complexity for the module programmer due to iovec[] strategem
> 
> *) how would mod_include.c::include_cgi() be written in a hook scheme?
> 
> *) how would my insert-100Mb-filter be written?

I Ryan addressed this point with the filter being able to signal that
it hasn't sent all yet.

> *) if the filter wants to insert a file, then how do we get the FD
>    returned to Apache so that it can use sendfile/TransmitFile?

This is something that's not clear to me in *either* scheme. Can you
elaborate how this should be done using layers?

> *) flow control

I don't see this as really any different from the layer approach. But
I'm assuming that a filter/content-generator that creates large output
will do so in parts (third point above), and only smaller stuff will
slurp all up and send it in one write.

> *) how would "SetFilter SSI PHP" be implemented in the hook scheme?
>    (note this implies a table mapping names to functions; also, I believe
>     that solving this directive for the hook-based scheme will essentially
>     look just like the link-based scheme)

I think this is a minor point. The hook scheme allows you to order a specific
filter after another one. Yes, it essentially degrades to a linked-list.

> *) hook-based scheme potentially has a larger working set because the
>    iovec[] return values must occur on the heap
>    (in the link-based scheme, they may occur on the heap or temporarily on
>     the stack)

Let me say that on the whole I prefer the layering scheme Greg is pushing,
because that's what I'm used to ;-)  I find it very convenient to write
layers that way. Trying to assemble iovec's seems somewhat cumbersome, and
I haven't seen any advantage of iovec's yet which would make it worth it,
unless it's that it may be more efficient in the end on systems that
support iovec's on sockets.

OTOH, I've been thinking how this would integrate with Dean's proposal to
have a dedicated process/thread which serves slow clients (via some
select/poll based pseudo-asynch-io) so as to free up the processes/threads
for doing the content-generation/filtering work as quickly as possible,
which I think is a really cool idea.  With the iovec's it seems more
straightforward at first glance, because it would get all the data it
needs in one go. However, as soon as the filters can produce partial
output this reverts to what I'm guessing will be needed for the layer
scheme: a largish buffer into which the stuff is assembled (i.e copied),
which the slow-client-process can then dribble to the client at will. So
I'm not sure I see any advantage here using iovec's either. However, I
suspect I may be missing something, because I also don't see how sendfile
integrates into these schemes - I'm starting to think Roy's
bucket-brigades may be the way to go after all.


  Cheers,

  Ronald


working set issue (was: Re: recap of filtered I/O)

Posted by Greg Stein <gs...@lyra.org>.
On Sat, 3 Jun 2000, Greg Stein wrote:
>...
> *) hook-based scheme potentially has a larger working set because the
>    iovec[] return values must occur on the heap
>    (in the link-based scheme, they may occur on the heap or temporarily on
>     the stack)

I wanted to lay out an argument here... proactively :-)

Consider the following link-based filter:

    ap_status_t filter_callback(layer *this_layer, void *buf, size_t len)
    {
        my_conf *conf = ...

        send_huge_thing(this_layer->next, conf->item1);
        ap_lwrite(this_layer->next, buf, len);
        send_huge_thing(this_layer->next, conf->item2);
    }

    void send_huge_thing(layer *next, item)
    {
        char buf[HUGE_BUFFER];

        len = format_item(buf, item)
        ap_lwrite(next, buf, len);
    }


In the above example, the working set is temporarily increased by
HUGE_BUFFER (on the stack). But this is unwound when send_huge_thing()
returns. It *does* remain on the stack during that ap_lwrite() inside of
send_huge_thing.


In the link-based approach, the stack cannot be used, so filter_callback()
needs to return two HUGE_BUFFER items on the heap, pointed to by the
iovec.

Moreover, if filter_callback is called MULTIPLE times, then these will
continue to be allocated on the heap -- it is all going into the request
pool(!), which isn't cleared until the end of the request.


The link-based scheme at least allows the working set to pop back down in
between the multiple calls to filter_callback.


Finally: note that when I say HUGE_BUFFER, I'm only talking about
something on the order of, say, 20k or something. That can easily fit on a
stack, but if filter_callback is entered 100 times, then we'd be
generating a 4M working set for the request pool in the hook scheme.

Cheers,
-g

-- 
Greg Stein, http://www.lyra.org/