USB CDC : Help with Zero Packets
Apparently, some host device drivers, like in Windows, use the indication that you have no more data immediately to pass accumulated data up to the app.  Without that, Windows apparently buffers the data and passes up to the app only when some limit is reached (seems to be 64 bytes, but not sure).

USB takes a less than full IN (received from device) packet as indication of no more immediate data.  This means if you have no more data and the previous IN packet filled the buffer, then you have to send a 0 length packet in response to the next IN token.  You would think that NACKing the next IN token clearly indicates you don't have immediate data, but apparently that's not correct for USB.

I just ran into this issue head on.  I've been using my PIC 18 USB code for many years and over a dozen projects without issues.  Up until now, I used 64 byte buffers for the app data endpoints.  I just did another USB device, but this time using a smaller PIC 18 where I couldn't afford the three 64 byte buffers for each endpoint.  I used 16 byte buffers instead.

This PIC was receiving information over a serial connection, then sending it on to the host via USB.  Communication sometimes stalled.  This was traced back to happening after a full 16 byte packet was sent, then no more data available.  Windows kept the 16 bytes without passing them up to the application.

We temporarily kludged around this by having the serial data stream contain NULL commands occasionally when nothing else was going on.  These are only 1 byte long.  If the above case happened, Windows would keep the 16 bytes.  Then a packet would come along shortly containing only the NULL command byte.  Since that is not a full packet, Windows considers that the end of immediate data and releases the accumulated 17 bytes to the applications.

When I get back next week, I'll fix my PIC 18 USB code to send a empty packet whenever the previous packet was full and there is nothing new to send.  Then it will NACK subsequent IN tokens.  Currently it NACKs IN tokens whenever it doesn't have anything immediate to send.

Since I haven't made the change yet, I can't say for sure that this was the problem and that the intended solution addresses it.  However, the occasional NULL command workaround worked, and the USB spec does seem to agree that you should send a non-full packet whenever there is no more immediate data.

I still don't understand why NACKing the next IN token doesn't convey the same information, but apparently it doesn't.

Update

I have meanwhile fixed my PIC 18 USB stack.  It now sends one zero-length packet if the previous packet was full length, and there is nothing immediately available to send.  After non-full-length packets, which includes the deliberate zero-length packets, with nothing immediate to send, IN tokens are NACKed as before.  I believe this is now following the letter of the USB spec.  A USB analyzer shows the correct sequence of packets according to this interpretation.

The NULL-sending temporary kludge mentioned above was removed, and everything seems to work correctly.