Implement a thread-safe FIFO queue with a fixed capacity:
class BoundedBlockingQueue:
def __init__(self, capacity: int): ...
def enqueue(self, element: int) -> None: ... # blocks while the queue is full
def dequeue(self) -> int: ... # blocks while the queue is empty
def size(self) -> int: ... # current number of elements
Any number of producer threads call enqueue and any number of consumer threads call dequeue, all at once. Every element enqueued must be dequeued exactly once, in FIFO order, and the queue must never hold more than capacity elements.
Don't use queue.Queue (that's the answer already written). Store the elements in a collections.deque and coordinate the threads with threading primitives. No sleeping or busy-waiting.
Example with capacity 2: enqueue(1), enqueue(2), then a third enqueue(3) blocks until some thread calls dequeue(), which returns 1.
Show hint
There are two things to count and wait on: free slots (producers wait for one) and stored items (consumers wait for one). A counting primitive for each, plus a lock around the deque itself, is enough.