The task function is called once, and if it returns, the task is deleted, so the task function IS the event loop, and generally should have the for-ever loop.
Unless you are having an initialization order issue, I don’t use the singleton pattern, but just make the object as a global. I wouldn’t make a USB stack a singleton, because that says that if you ever go to a part with two USB interfaces, your whole design is broken, as it can only have one interface due to the singleton pattern. Instead I make the class and pass it a pointer/reference to the USB interface it is to use.
Since the USB driver is generally doing most of the I/O work in an ISR, a read request giving a buffer will tend to wait on a semaphore, which could have a timeout assigned. That is still called “Blocking”, as the task does still block for a while. A non-blocking read would be.a call to start the read, and the task periodically checks to see if it is done, and means the task needs to have something else to be doing in the mean time, which might indicate that it really should be two different tasks.