[dev] buffer chain and NGX_AGAIN

Manlio Perillo manlio_perillo at libero.it
Fri Oct 5 01:28:50 MSD 2007


Manlio Perillo ha scritto:
> Hi.
> 
> I have another question about buffer chains and NGX_AGAIN.
> 
> Suppose that ngx_http_output_filter returns NGX_AGAIN and I set the 
> events to be notified when I can send further data.
> 
> When my handler is called again, has the previous buffer been sent to 
> the client or copied in one of the output filter chains?
> 
> This is important because in mod_wsgi the buffer points to a Python 
> object and I must know when I can safely deallocate it.
> 

I have done some tests, and, unfortunately, when the write handler is 
called (after a NGX_AGAIN), the previous buffer is still in use, so I 
can't free it.


Here is the code I use:

static void
ngx_http_wsgi_iterator_handler(ngx_http_request_t *r) {
   ngx_http_core_loc_conf_t  *clcf;

   ngx_event_t          *wev;
   ngx_int_t             rc;
   ngx_chain_t           out;
   ngx_buf_t            *b;
   u_char               *result;
   ngx_uint_t            len;
   ngx_http_wsgi_ctx_t  *ctx;

   PyObject             *item = NULL;


   clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

   wev = r->connection->write;
   ctx = ngx_http_get_module_ctx(r, ngx_http_wsgi_module);

   // ...

   /* Free the previous item, if any */
   /* XXX check me */
   //Py_XDECREF(ctx->last_item); <=== the memory is still in use

   if (ctx->waiting) {
     ctx->waiting = 0;
     if (wev->timedout) {
       r->connection->timedout = 1;
       ngx_http_wsgi_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
       return;
     }
   }

   // ... obtain the next item from the Python iterator ...

   len = PyString_GET_SIZE(item);
   result = (u_char *) PyString_AsString(item);

   out.buf = b;
   out.next = NULL;

   // ...

   b->pos = result;
   b->last = result + len;

   b->memory = 1;
   b->flush = 1;

   rc = ngx_http_output_filter(r, &out);
   switch (rc) {
   case NGX_OK:
     /*
      * Ok, the entire buffer has been sent to the client or copied in
      * one of the output filter buffers.
      *
      * We can free the item and continue the iteration.
      *
      */
     Py_DECREF(item);
     ctx->last_item = NULL;
     return ngx_http_wsgi_iterator_handler(r);
   case NGX_AGAIN:
     /*
      * The buffer can't be send to the client right now.
      *
      * Save the item on the context, so that we can free it on the next
      * iteration and setup the events so that we can continue the
      * iteration when we can send the buffer.
      *
      */
     ctx->last_item = item;
     ctx->waiting = 1;

     ngx_add_timer(wev, clcf->send_timeout);
     r->write_event_handler = ngx_http_wsgi_iterator_handler;
     if (ngx_handle_write_event(wev, 0) == NGX_ERROR) {
       ngx_http_wsgi_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
       return;
     }

     return;
   default:
     ngx_http_wsgi_finalize_request(r, rc);
     return;
   }



If I do not free the "last" used Python object, then all seems to work well.


P.S.
A performance note.
Serving an mp3, of size 3831150 bytes,
with worker_process = 2 (on a dual core)

  * nginx: 200 requests/seconds
  * mod_wsgi with a bufsize of 4096 bytes:  35 requests/seconds
  * mod_wsgi with a bufsize of 8192 bytes:  53 requests/seconds
  * mod_wsgi with a bufsize of 16384 bytes: 81 requests/seconds
  * mod_wsgi with a bufsize of 40960 bytes: 100 requests/seconds



Regards  Manlio Perillo





More information about the nginx mailing list