def getter(array: Array, cv: Condition):
    """
    Awaits notification through a Condition object
    (condition variable) that a value is available in array.
    Reads the value and prints it.
    :param array: a multiprocessing Array of integers of size 1 from which to read a value
    :param cv: a Condition object (a condition variable) to allow the value to be read only when it is ready
    """
    print('In getter.')
    with cv:
        # wait_for takes a _predicate_, which is a boolean-valued function.
        # A lambda expression creates an anonymous function that returns the result of evaluating the body.
        # This invocation causes the process to block until the value in a is set.
        cv.wait_for(lambda: array[0] != 0)
        print(f'Got {array[0]}.')
Ejemplo n.º 2
0
class CountDownLatch(object):
    """A synchronization aid that allows one or more processes to wait until a set of operations
    being performed in other processes completes.

    A CountDownLatch is initialized with a given count. The wait method blocks until the current
    count reaches zero due to invocations of the count_down() method, after which all waiting processes
    are released and any subsequent invocations of wait return immediately.

    The count can be reset, but one need to be 100% sure no other processes are waiting or counting down
    during reset.
    """
    def __init__(self, count: int = 1, lock=None):
        self.__count = Value('i', count, lock=True if lock is None else lock)
        self.__lock = Condition(lock)

    def reset(self, count):
        self.__lock.acquire()
        self.__count.value = count
        self.__lock.release()

    def count_down(self):
        self.__lock.acquire()
        self.__count.value -= 1
        result = self.__count.value
        if self.__count.value <= 0:
            self.__lock.notify_all()
        self.__lock.release()
        return result

    def wait(self, timeout=None):
        self.__lock.acquire()
        result = self.__lock.wait_for(lambda: self.__count.value <= 0, timeout)
        self.__lock.release()
        return result
Ejemplo n.º 3
0
class ConditionVariableDemo:
    """
    Demonstrates the use of condition variables within a class.
    """
    def __init__(self):
        self.array = Array('i', 1)
        self.cv = Condition()

    def run(self):
        """
        Creates, runs, and joins a setter and a getter process.
        """
        p0 = Process(target=self.setter)
        p1 = Process(target=self.getter)
        p0.start()
        p1.start()
        p0.join()
        p1.join()

    def setter(self):
        """
        Sets the value of the first (and only) index
        position in a exclusively and notifies the process
        awaiting this action that a valid value is available.
        :param a: a multiprocessing Array of integers of size 1 to which to write a value
        :param cv: a Condition object (a condition variable) to allow the value to be read only when it is ready
        """
        with self.cv:
            print('In setter.')
            print('Before setting:', self.array[0])
            self.array[0] = 43
            print('After setting:', self.array[0])
            self.cv.notify()

    def getter(self):
        """
        Awaits notification through a Condition object
        (condition variable) that a value is available in a.
        Reads the value and prints it.
        :param a: a multiprocessing Array of integers of size 1 from which to read a value
        :param cv: a Condition object (a condition variable) to allow the value to be read only when it is ready
        """
        print('In getter.')
        with self.cv:
            self.cv.wait_for(lambda: self.array[0] != 0)
            print(f'Got {self.array[0]}.')
Ejemplo n.º 4
0
def fork_child(request, comms):
    val = Value('i', 0)
    lock = RLock()
    cond = Condition(lock)

    pid = os.fork()
    if pid:
        # parent
        with lock:
            val.value = 1
            cond.notify_all()
            cond.wait_for(lambda: val.value == 2)
        return pid
    else:
        # child
        # noinspection PyBroadException
        try:
            handler = CaptureHTTPHandler(request, comms)
            with lock:
                cond.wait_for(lambda: val.value == 1)
                val.value = 2
                cond.notify_all()
            handler.serve()
        except Exception:
            request.server.handle_error(request.req, request.client_address)
            with lock:
                cond.wait_for(lambda: val.value == 1)
                val.value = 2
                cond.notify_all()
        finally:
            request.server.shutdown_request(request.req)
            comms.close()
            # child does not exit normally
            import signal
            os.kill(os.getpid(), signal.SIGKILL)
Ejemplo n.º 5
0
class HogwildWorld(World):
    """Creates a separate world for each thread (process).

    Maintains a few shared objects to keep track of state:

    - A Semaphore which represents queued examples to be processed. Every call
      of parley increments this counter; every time a Process claims an
      example, it decrements this counter.

    - A Condition variable which notifies when there are no more queued
      examples.

    - A boolean Value which represents whether the inner worlds should shutdown.

    - An integer Value which contains the number of unprocessed examples queued
      (acquiring the semaphore only claims them--this counter is decremented
      once the processing is complete).
    """
    def __init__(self, world_class, opt, agents):
        self.inner_world = world_class(opt, agents)

        self.queued_items = Semaphore(0)  # counts num exs to be processed
        self.epochDone = Condition()  # notifies when exs are finished
        self.terminate = Value('b', False)  # tells threads when to shut down
        self.cnt = Value('i', 0)  # number of exs that remain to be processed

        self.threads = []
        for i in range(opt['numthreads']):
            self.threads.append(
                HogwildProcess(i, world_class, opt, agents, self.queued_items,
                               self.epochDone, self.terminate, self.cnt))
        for t in self.threads:
            t.start()

    def __iter__(self):
        raise NotImplementedError('Iteration not available in hogwild.')

    def display(self):
        self.shutdown()
        raise NotImplementedError(
            'Hogwild does not support displaying in-run' +
            ' task data. Use `--numthreads 1`.')

    def episode_done(self):
        return False

    def parley(self):
        """Queue one item to be processed."""
        with self.cnt.get_lock():
            self.cnt.value += 1
        self.queued_items.release()

    def getID(self):
        return self.inner_world.getID()

    def report(self):
        return self.inner_world.report()

    def save(self):
        self.inner_world.save()

    def synchronize(self):
        """Sync barrier: will wait until all queued examples are processed."""
        with self.epochDone:
            self.epochDone.wait_for(lambda: self.cnt.value == 0)

    def shutdown(self):
        """Set shutdown flag and wake threads up to close themselves"""
        # set shutdown flag
        with self.terminate.get_lock():
            self.terminate.value = True
        # wake up each thread by queueing fake examples
        for _ in self.threads:
            self.queued_items.release()
        # wait for threads to close
        for t in self.threads:
            t.join()
Ejemplo n.º 6
0
class StateLatch(object):
    """A synchronization aid that allows one or more processes to wait for state change until a
    set of operations being performed in other processes completes.

    A StateLatch is initialized with a given state. The wait and wait_for methods block until the current
    state changes to the desired state due to invocations of the next() method, after which all waiting
    processes are released and any subsequent invocations of wait or wait_for return immediately.

    While changing state one can set the counter of the next state. The next state won't take effect
    due to invocations of the next() method until the counter reaches zero. This ensures the requested
    number of processes completed their work when state actually changes.

    The counter of next state can be amended without state change using set_next method, but one need to
    be 100% sure no other processes are waiting or changing state at the moment.
    """
    def __init__(self, state=State.READY, lock: RLock = None):
        self.__state = Value('i', state, lock=True if lock is None else lock)
        self.__lock = Condition(lock)
        self.__next_state_count_down = CountDownLatch(0, lock)
        self.__next_state_count_down_max = Value(
            'i', 0, lock=True if lock is None else lock)

    def set_next(self, next_state_count_down):
        self.__lock.acquire()
        self.__next_state_count_down.reset(next_state_count_down)
        self.__next_state_count_down_max.value = 0
        self.__lock.release()

    def next(self, next_state_count_down: int = 0):
        self.__lock.acquire()
        old = State(self.__state.value)
        self.__next_state_count_down_max.value = max(
            self.__next_state_count_down_max.value, next_state_count_down)
        if self.__next_state_count_down.wait(0) or \
                self.__next_state_count_down.count_down() == 0:
            self.__state.value = State.next(self.__state.value)
            self.__next_state_count_down.reset(
                self.__next_state_count_down_max.value)
            self.__next_state_count_down_max.value = 0
        new = State(self.__state.value)
        self.__lock.notify_all()
        self.__lock.release()
        return old, new

    def wait(self, state, timeout=None):
        self.__lock.acquire()
        result = self.__lock.wait_for(lambda: self.__state.value == state,
                                      timeout)
        self.__lock.release()
        return result

    def wait_for(self, state, predicate, timeout=None):
        """Wait for the desired state or until a condition evaluates to true. predicate must be
        a callable with the result interpreted as a boolean value. A timeout may be provided giving
        the maximum time to wait. While waiting the predicate is being checked every second.

        :param state: state to wait for
        :param predicate: callable function with the result interpreted as a boolean value
        :param timeout: the maximum time to wait
        :return: the last return value of the predicate or False if the method timed out
        """

        self.__lock.acquire()
        try:
            result = self.__state.value == state or predicate()
            if result:
                return result
            end_time = None if timeout is None else monotonic() + timeout
            wait_time = 1
            while not result:
                if end_time is not None:
                    wait_time = min(end_time - monotonic(), 1)
                    if wait_time <= 0:
                        break
                result = self.__lock.wait_for(
                    lambda: self.__state.value == state,
                    wait_time) or predicate()
            return result
        finally:
            self.__lock.release()

    @property
    def state(self):
        return State(self.__state.value)
Ejemplo n.º 7
0
class HogwildWorld(World):
    """Creates a separate world for each thread (process).

    Maintains a few shared objects to keep track of state:

    - A Semaphore which represents queued examples to be processed. Every call
      of parley increments this counter; every time a Process claims an
      example, it decrements this counter.

    - A Condition variable which notifies when there are no more queued
      examples.

    - A boolean Value which represents whether the inner worlds should shutdown.

    - An integer Value which contains the number of unprocessed examples queued
      (acquiring the semaphore only claims them--this counter is decremented
      once the processing is complete).
    """

    def __init__(self, world_class, opt, agents):
        self.inner_world = world_class(opt, agents)

        self.queued_items = Semaphore(0)  # counts num exs to be processed
        self.epochDone = Condition()  # notifies when exs are finished
        self.terminate = Value('b', False)  # tells threads when to shut down
        self.cnt = Value('i', 0)  # number of exs that remain to be processed

        self.threads = []
        for i in range(opt['numthreads']):
            self.threads.append(HogwildProcess(i, world_class, opt,
                                               agents, self.queued_items,
                                               self.epochDone, self.terminate,
                                               self.cnt))
        for t in self.threads:
            t.start()

    def __iter__(self):
        raise NotImplementedError('Iteration not available in hogwild.')

    def display(self):
        self.shutdown()
        raise NotImplementedError('Hogwild does not support displaying in-run' +
                                  ' task data. Use `--numthreads 1`.')

    def episode_done(self):
        return False

    def parley(self):
        """Queue one item to be processed."""
        with self.cnt.get_lock():
            self.cnt.value += 1
        self.queued_items.release()

    def getID(self):
        return self.inner_world.getID()

    def report(self):
        return self.inner_world.report()

    def save_agents(self):
        self.inner_world.save_agents()

    def synchronize(self):
        """Sync barrier: will wait until all queued examples are processed."""
        with self.epochDone:
            self.epochDone.wait_for(lambda: self.cnt.value == 0)

    def shutdown(self):
        """Set shutdown flag and wake threads up to close themselves"""
        # set shutdown flag
        with self.terminate.get_lock():
            self.terminate.value = True
        # wake up each thread by queueing fake examples
        for _ in self.threads:
            self.queued_items.release()
        # wait for threads to close
        for t in self.threads:
            t.join()