Add `Latch` synchronization primitive to `threading` module

Latch is a useful synchronization primitive, which is available in C++ and Java.

  1. Initialize Latch object with count and timeout.
  2. Threads A and B call wait (blocking).
  3. Threads C and D call .countDown (non-blocking).
  4. When count reaches 0, A and B are unblocked.

POC

class Latch:
    def __init__(self, count, timeout=None):
        self._timeout = timeout
        self._count = self._initial_count = count
        self._cond = Condition(Lock())

    def __repr__(self):
        cls = self.__class__
        return (f"<{cls.__module__}.{cls.__qualname__} at {id(self):#x}:"
                f" value={self._count}/{self._initial_count}>")

    def count_down(self):
        with self._cond:
            if self._count > 0:
                self._count -= 1
                if self._count == 0:
                    self._cond.notify_all()
            else:
                raise ValueError("Latch count is already at 0")

    def wait(self, timeout=None):
        if timeout is None:
            timeout = self._timeout
        with self._cond:
            if not self._cond.wait_for(lambda: self._count == 0, timeout):
                raise BrokenLatchError
            else:
                return True

    @property
    def count(self):
        return self._count

I’m not sure if this has already been considered. I was unable to find a related PEP or discussion related to this topic.

C++: https://en.cppreference.com/w/cpp/thread/latch

Java: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html