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