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