Hi,

Continuing my investigation of some unexplained latency issues, I am going
back through the bnull_transform logic to check our logic. With this I have
a few questions:

In bnull_transform.c the logic is that a state variable determines if we
are buffering or writing. Data is read and buffered via handle_buffering(),
once finished the buffer is released via handle_output(). This is done in
two places in handle_buffering() by setting the new state and returning
from the handler. Then the plugin will call handle_output() when it gets
the IMMEDIATE event at a later time:

  /* We also check to see if the write VIO's buffer is non-NULL. A
     NULL buffer indicates that the write operation has been
     shutdown and that the continuation does not want us to send any
     more WRITE_READY or WRITE_COMPLETE events. For this buffered
     transformation that means we're done buffering data. */

  if (!TSVIOBufferGet(write_vio)) {
    data->state = STATE_OUTPUT_DATA;
    return 0;
  }

And here:

  /* Now we check the write VIO to see if there is data left to read. */
  if (TSVIONTodoGet(write_vio) > 0) {
    if (towrite > 0) {
      /* Call back the write VIO continuation to let it know that we
         are ready for more data. */
      TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_READY,
write_vio);
    }
  } else {
    data->state = STATE_OUTPUT_DATA;

    /* Call back the write VIO continuation to let it know that we
       have completed the write operation. */
    TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_COMPLETE,
write_vio);
  }

My question is why would you set state and return to wait for IMMEDIATE vs
just call the handle_output() directly from handle_buffering()? By
returning and waiting for the IMMEDIATE it seems we have some inconsistent
latency in flushing the buffer and I am trying to determine why it is done
this way vs something like:

  /* We also check to see if the write VIO's buffer is non-NULL. A
     NULL buffer indicates that the write operation has been
     shutdown and that the continuation does not want us to send any
     more WRITE_READY or WRITE_COMPLETE events. For this buffered
     transformation that means we're done buffering data. */

  if (!TSVIOBufferGet(write_vio)) {
    handle_output(contp, data); /** <- CHANGE HERE */
    return 0;
  }

And here:

  /* Now we check the write VIO to see if there is data left to read. */
  if (TSVIONTodoGet(write_vio) > 0) {
    if (towrite > 0) {
      /* Call back the write VIO continuation to let it know that we
         are ready for more data. */
      TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_READY,
write_vio);
    }
  } else {
    handle_output(contp, data); /** <- CHANGE HERE */

    /* Call back the write VIO continuation to let it know that we
       have completed the write operation. */
    TSContCall(TSVIOContGet(write_vio), TS_EVENT_VCONN_WRITE_COMPLETE,
write_vio);
  }

Any advantage/disavantage to doing this either way? With some basic tests,
I see that I can get up to 10ms latency waiting for the IMMEDIATE to
trigger. I need to verify this with some better tests, but calling the
handle_output() directly makes this inconsistency seem to go away.

Cheers!
-B

--
Brian Rectanus

Reply via email to