A system consists of N producer threads M consumer threads and a
manager thread. The threads interact with the following data
structure:

class CircularArray {
  ...
public:
  CircularArray(int size);

  void insert(int elem);
  void extract(int &elem);

  void start();
  void stop();
};

The size parameter denotes the number of elements inside the queue.

Producers call the insert() to insert new elements. If the internal
queue is full, the producers should block. Consumers call extract() to
extract elements from the queue. If the queue is empty, consumers must
block.

The manager thread can call stop() and start(). When it calls stop(),
the producers will block during their next call to insert, regardless
of the fact that the queue is full. Consumers, instead, can continue
to extract without any problem until the queue is empty.

When the manager calls start(), if the queue is empty, the producers
are unblocked and can continue to produce without any problem. If the
queue is not empty, the manager first blocks waiting for the consumers
to empty the queue, and then unblocks all the producers.

Write the data structure and a test program. The manager thread could
take input from the keyboard before invoking start() or stop().

