1a) Sleep polling on the producer side is not a good idea. Use event polling if anything - but even that won't guarantee that the thread will make progress if there are multiple producers.
1b) The event doesn't have to be exposed to the user. PRODUCER_FN could deal with it internally. The easy solution is to event-wait within PRODUCER_FN. The API is already a blocking API (due to "EnterCS"), so you're not changing much.
Choosing between the two is a design decision only you can make. You could even combine the two by having both a blocking "produce()" and non-blocking "try_produce(timeout)" (if true non-blocking is required).
2) The only thing you should care about is defining the requirements and semantics of your library interface - and meeting those requirements of course. Then the users of your library can make proper design decisions based on a well-defined API.
For producer/consumer queue's in general:
- Never sleep() poll
- Never sleep() (never necessary in a properly designed multi-threaded app)
- Only event-poll if you absolutely have to
- Always prefer event-waiting over event-polling
- The API must deal with overflow, because it can and will happen.