libusb backend: attempt to fix DRAIN_OUTPUT race condition. Fixes #1461#1500
libusb backend: attempt to fix DRAIN_OUTPUT race condition. Fixes #1461#1500ValdikSS wants to merge 3 commits intoOpenPrinting:2.4.xfrom
Conversation
…nPrinting#1461 CUPS_SC_CMD_DRAIN_OUTPUT sidechannel command is supposed to ensure that all the data sent by the filter would be consumed by the backend and sent to the printer via USB before it returns. However, if backend's main thread read and processed filter's data before filter managed to send CUPS_SC_CMD_DRAIN_OUTPUT, the filter will be blocked for 1 second, as backend will be blocked in select() waiting for new data (while filter is waiting for DRAIN_OUTPUT response and does not send anything). This is a hacky attempt to fix this issue. However, it's not without flaw: * If backend's main thread is slow and sidechannel thread is fast, the response to CUPS_SC_CMD_DRAIN_OUTPUT may be sent without actually draining (and even reading) the data. * This means that's a tradeoff towards potentially breaking framing of some USB protocols to prevent 1-second locking (and breaking printing for timing-sensitive protocols).
Instead of a hacky way to detect which thread needs to respond to CUPS_SC_CMD_DRAIN_OUTPUT command, use pipe to kick select() in the main thread, and process everything there.
|
I made a test filter that spams with the no-response command Lines 425 to 430 in 07c17a0 It means that the backend sends a response to drain, the filter receives it, sends new data and another drain, which is processed by I would use some synchronization primitives, but this also works: if (g.drain_output && !nfds && !g.print_bytes)
{
+ g.drain_output = 0;
/* Send a response... */
cupsSideChannelWrite(CUPS_SC_CMD_DRAIN_OUTPUT, CUPS_SC_STATUS_OK, NULL, 0, 1.0);
- g.drain_output = 0;
}Also, currently, the backend first responds to drain cmd and only then reads Except for the order of setting |
I'm not exactly sure, because after my changes the check check for nfds Lines 422 to 427 in c5ce534 will not work the way it used to work: |
|
Does it work flawlessly for you with |
|
Just moved the response after reading the print_fd, please test the current MR draft. |
|
Maybe move it even later, after the data has been sent to the printer and not just read from the fd? |
Well... Forget everything I said about the filter having time to send before
Yep. |
9df1ca5 to
510ebb3
Compare
CUPS_SC_CMD_DRAIN_OUTPUT sidechannel command is supposed to ensure that all the data sent by the filter would be consumed by the backend and sent to the printer via USB before it returns.
However, if backend's main thread read and processed filter's data before filter managed to send CUPS_SC_CMD_DRAIN_OUTPUT, the filter will be blocked for 1 second, as backend will be blocked in select() waiting for new data (while filter is waiting for DRAIN_OUTPUT response and does not send anything).
This is a hacky attempt to fix this issue. However, it's not without flaw:* If backend's main thread is slow and sidechannel thread is fast, the response to CUPS_SC_CMD_DRAIN_OUTPUT may be sent without actually draining (and even reading) the data.* This means that's a tradeoff towards potentially breaking framing of some USB protocols to prevent 1-second locking (and breaking printing for timing-sensitive protocols).A pipe is used to wake up main thread from select().