Implements a Barrier. Useful for synchronizing a fixed number of threads at known synchronization points. Threads block on 'wait()' and are simultaneously awoken once they have all made that call.
| 676 | # similar to 'draining' except that threads leave with a BrokenBarrierError, |
| 677 | # and a 'broken' state in which all threads get the exception. |
| 678 | class Barrier: |
| 679 | """Implements a Barrier. |
| 680 | |
| 681 | Useful for synchronizing a fixed number of threads at known synchronization |
| 682 | points. Threads block on 'wait()' and are simultaneously awoken once they |
| 683 | have all made that call. |
| 684 | |
| 685 | """ |
| 686 | |
| 687 | def __init__(self, parties, action=None, timeout=None): |
| 688 | """Create a barrier, initialised to 'parties' threads. |
| 689 | |
| 690 | 'action' is a callable which, when supplied, will be called by one of |
| 691 | the threads after they have all entered the barrier and just prior to |
| 692 | releasing them all. If a 'timeout' is provided, it is used as the |
| 693 | default for all subsequent 'wait()' calls. |
| 694 | |
| 695 | """ |
| 696 | if parties < 1: |
| 697 | raise ValueError("parties must be >= 1") |
| 698 | self._cond = Condition(Lock()) |
| 699 | self._action = action |
| 700 | self._timeout = timeout |
| 701 | self._parties = parties |
| 702 | self._state = 0 # 0 filling, 1 draining, -1 resetting, -2 broken |
| 703 | self._count = 0 |
| 704 | |
| 705 | def __repr__(self): |
| 706 | cls = self.__class__ |
| 707 | if self.broken: |
| 708 | return f"<{cls.__module__}.{cls.__qualname__} at {id(self):#x}: broken>" |
| 709 | return (f"<{cls.__module__}.{cls.__qualname__} at {id(self):#x}:" |
| 710 | f" waiters={self.n_waiting}/{self.parties}>") |
| 711 | |
| 712 | def wait(self, timeout=None): |
| 713 | """Wait for the barrier. |
| 714 | |
| 715 | When the specified number of threads have started waiting, they are all |
| 716 | simultaneously awoken. If an 'action' was provided for the barrier, one |
| 717 | of the threads will have executed that callback prior to returning. |
| 718 | Returns an individual index number from 0 to 'parties-1'. |
| 719 | |
| 720 | """ |
| 721 | if timeout is None: |
| 722 | timeout = self._timeout |
| 723 | with self._cond: |
| 724 | self._enter() # Block while the barrier drains. |
| 725 | index = self._count |
| 726 | self._count += 1 |
| 727 | try: |
| 728 | if index + 1 == self._parties: |
| 729 | # We release the barrier |
| 730 | self._release() |
| 731 | else: |
| 732 | # We wait until someone releases us |
| 733 | self._wait(timeout) |
| 734 | return index |
| 735 | finally: |
no outgoing calls
searching dependent graphs…