Exemple #1
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
Exemple #2
0
class ServerProcess(Process):
    def __init__(self):
        super().__init__()
        self.url = f"opc.tcp://127.0.0.1:{port_num}"
        self.cond = Condition()
        self.stop_ev = Event()

    async def run_server(self, url):
        srv = Server()
        srv.set_endpoint(url)
        await srv.init()
        await add_server_methods(srv)
        await add_server_custom_enum_struct(srv)
        async with srv:
            with self.cond:
                self.cond.notify_all()
            while not self.stop_ev.is_set():
                await asyncio.sleep(1)
        await srv.stop()

    def stop(self):
        self.stop_ev.set()

    async def wait_for_start(self):
        with ThreadPoolExecutor() as pool:
            result = await asyncio.get_running_loop().run_in_executor(
                pool, self.wait_for_start_sync)

    def wait_for_start_sync(self):
        with self.cond:
            self.cond.wait()

    def run(self):
        loop = asyncio.new_event_loop()
        loop.run_until_complete(self.run_server(self.url))
    def update_cells(
            self,
            cond_looking_for_positions: multiprocessing.Condition,
            event_compute_results: multiprocessing.Event,
            positions
    ):
        """
        :param cond_looking_for_positions:
        :param event_compute_results:
        :param positions:
        """

        while self.__is_alive:
            # print(f"[{os.getpid()}] update_cells")
            duplication = self.universe.copy()
            _positions = np.argwhere(self.universe == 1)
            _positions = np.array_split(_positions, multiprocessing.cpu_count() - 1)

            positions[:] = [position for position in _positions]

            with cond_looking_for_positions:
                cond_looking_for_positions.notify_all()
            event_compute_results.wait()
            event_compute_results.clear()

            _positions = positions[0]
            for position in positions[1:]:
                _positions = np.concatenate((_positions, position))
            # Remove duplicates
            _positions = np.unique(_positions, axis=0)

            for position in _positions:
                x, y = position
                min_y = max(y - 1, 0)
                max_y = min(y + 2, self.max_y)
                min_x = max(x - 1, 0)
                max_x = min(x + 2, self.max_x)
                sum_of_cells = 0
                for i in range(min_x, max_x):
                    for j in range(min_y, max_y):
                        if i == x and j == y:
                            continue
                        sum_of_cells += self.universe[i, j]

                if sum_of_cells == 3:
                    duplication[x][y] = 1
                if sum_of_cells < 2 or sum_of_cells > 3:
                    duplication[x][y] = 0

            self.universe = duplication
            self.__shared_universe[:] = self.universe.flatten()[:]
            time.sleep(self.speed)
Exemple #4
0
class SynchronizingBus(Bus):
    def __init__(self, sync_delay=1):
        Bus.__init__(self)
        self.sync_delay = sync_delay
        self.condition = Condition()

    def start(self):
        import time
        time.sleep(self.sync_delay)
        self.log("Releasing children")
        self.condition.acquire()
        self.condition.notify_all()
        self.condition.release()
        Bus.start(self)
Exemple #5
0
class SynchronizingBus(Bus):
    def __init__(self, sync_delay=1):
        Bus.__init__(self)
        self.sync_delay = sync_delay
        self.condition = Condition()

    def start(self):
        import time
        time.sleep(self.sync_delay)
        self.log("Releasing children")
        self.condition.acquire()
        self.condition.notify_all()
        self.condition.release()
        Bus.start(self)
class Barrier(object):
    def __init__(self, num_threads):
        self.num_threads = num_threads
        self.threads_left = Value('i', num_threads, lock=True)
        self.mutex = Lock()
        self.waitcond = Condition(self.mutex)

    def wait(self):
        self.mutex.acquire()
        self.threads_left.value -= 1
        if self.threads_left.value == 0:
            self.threads_left.value = self.num_threads
            self.waitcond.notify_all()
            self.mutex.release()
        else:
            self.waitcond.wait()
            self.mutex.release()
Exemple #7
0
class Barrier(object):
    def __init__(self, num_threads):
        self.num_threads = num_threads
        self.threads_left = Value('i', num_threads, lock=True)
        self.mutex = Lock()
        self.waitcond = Condition(self.mutex)

    def wait(self):
        self.mutex.acquire()
        self.threads_left.value -= 1
        if self.threads_left.value == 0:
            self.threads_left.value = self.num_threads
            self.waitcond.notify_all()
            self.mutex.release()
        else:
            self.waitcond.wait()
            self.mutex.release()
Exemple #8
0
class OrderedQueue(object):
    def __init__(self, maxsize):
        self.queue = Queue(maxsize=maxsize)
        self.lock = Lock()
        self.getlock = Lock()
        self.putcounter = Value('i', -1)
        self.getcounter = Value('i', 0)
        self.cond = Condition(self.lock)
        self.manager = Manager()
        self.getlist = self.manager.list()

    def put(self, index, elem):
        with self.lock:
            while index != self.putcounter.value + 1:
                self.cond.wait()
            self.queue.put((index, elem))
            #sys.stderr.write("right after adding data with SEED %i. Queue size is now %i\n" %(index, self.queue.qsize()))
            self.putcounter.value += 1
            self.cond.notify_all()
        
    def get(self):
        with self.getlock:
            for i, element in enumerate(self.getlist):
                index, elem = element 
                if index == self.getcounter.value:
                    self.getcounter.value += 1
                    del self.getlist[i]
                    return (index, elem)
            while True:
                index, elem = self.queue.get()
                if index == self.getcounter.value:
                    self.getcounter.value += 1
                    return (index, elem)
                else:
                    self.getlist.append((index, elem))

    def close(self):
        return self.queue.close()

    def qsize(self):
        return self.queue.qsize()
Exemple #9
0
def run(frame: Array, new_frame: Condition):
    with picamera.PiCamera(
            resolution=RESOLUTION) as camera, picamera.array.PiRGBArray(
                camera, size=RESOLUTION) as data_container:
        stream = camera.capture_continuous(data_container,
                                           format="rgb",
                                           use_video_port=True)

        f: picamera.array.PiArrayOutput
        for f in stream:
            with frame.get_lock():
                image = numpy.frombuffer(frame.get_obj(),
                                         dtype=numpy.uint8).reshape(
                                             (RESOLUTION[1], RESOLUTION[0], 3))
                numpy.copyto(image, f.array)

            with new_frame:
                new_frame.notify_all()

            data_container.seek(0)
            data_container.truncate()
Exemple #10
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)
Exemple #11
0
class RWLock:
    def __init__(self):
        self.cond = Condition()
        self.readers = 0

    def read_acquire(self):
        self.cond.acquire()
        self.readers += 1
        self.cond.release()

    def read_release(self):
        with self.cond:
            self.readers -= 1
            if (self.readers == 0):
                self.cond.notify_all()

    def write_acquire(self):
        self.cond.acquire()
        if (self.readers > 0):
            self.cond.wait()

    def write_release(self):
        self.cond.release()
Exemple #12
0
class BARRIER:
    def __init__(self, n):
        self.n = n
        self.i = Value('i', 0)
        self.lock = Lock()
        self.condition = Condition()

    def acquire(self):
        with self.lock:
            self.i.value += 1
            if self.n == self.i.value:
                self.i.value = 0
                return True

        #print self.i.value,self.n
        self.condition.acquire()
        self.condition.wait()
        self.condition.release()
        return False

    def release(self):
        self.condition.acquire()
        self.condition.notify_all()
        self.condition.release()
Exemple #13
0
class JoinableQueue(Queue):
    def __init__(self, maxsize=0):
        Queue.__init__(self, maxsize)
        self._unfinished_tasks = Semaphore(0)
        self._cond = Condition()

    def __getstate__(self):
        return Queue.__getstate__(self) + (self._cond, self._unfinished_tasks)

    def __setstate__(self, state):
        Queue.__setstate__(self, state[:-2])
        self._cond, self._unfinished_tasks = state[-2:]

    def put(self, obj, block=True, timeout=None):
        assert not self._closed
        if not self._sem.acquire(block, timeout):
            raise Full

        with self._notempty, self._cond:
            if self._thread is None:
                self._start_thread()
            self._buffer.append(obj)
            self._unfinished_tasks.release()
            self._notempty.notify()

    def task_done(self):
        with self._cond:
            if not self._unfinished_tasks.acquire(False):
                raise ValueError('task_done() called too many times')
            if self._unfinished_tasks._semlock._is_zero():
                self._cond.notify_all()

    def join(self):
        with self._cond:
            if not self._unfinished_tasks._semlock._is_zero():
                self._cond.wait()
Exemple #14
0
class RWLock():
    """A Readers-Writer lock.
    
    Allows for multiple readers or one writer. Writers will not starve.

    Attributes:
        for_reading (RWLock.ReadLock): A lock-like object with appropriate
            `acquire`, `release`, `__enter__` and `__exit__` methods pointed
            to the *read methods of the RWLock. Chiefly for use with the 
            `with` statement.
        for_writing (RWLock.WriteLock): A lock-like object with appropriate
            `acquire`, `release`, `__enter__` and `__exit__` methods pointed
            to the *write methods of the RWLock. Chiefly for use with the 
            `with` statement.

    """
    class ReadLock():
        def __init__(self, rw):
            self._rw = rw
            self.acquire = rw.acquire_read
            self.release = rw.release_read

        def __enter__(self):
            self.acquire()

        def __exit__(self, exception_type, exception_value, traceback):
            self.release()

    class WriteLock():
        def __init__(self, rw):
            self._rw = rw
            self.acquire = rw.acquire_write
            self.release = rw.release_write

        def __enter__(self):
            self.acquire()

        def __exit__(self, exception_type, exception_value, traceback):
            self.release()

    def __init__(self):
        """Initialises the RWLock."""
        self._condition = Condition()
        self._readers = Value(c_uint64, 0, lock=False)
        self._writers_waiting = Value(c_uint64, 0, lock=False)

        self.for_reading = self.ReadLock(self)
        self.for_writing = self.WriteLock(self)

    def acquire_read(self):
        """Acquire a read lock. 
        
        Blocks if a thread has acquired the write lock or is waiting to
        acquire the write lock.
        
        """
        with self._condition:
            while self._writers_waiting.value:
                self._condition.wait()
            self._readers.value += 1

    def release_read(self):
        """Release a read lock."""
        with self._condition:
            self._readers.value -= 1
            if not self._readers.value:
                self._condition.notify_all()

    def acquire_write(self):
        """Acquire a write lock.
        
        Blocks until there are no acquired read or write locks.
        
        """
        self._condition.acquire()
        self._writers_waiting.value += 1
        while self._readers.value:
            self._condition.wait()
        self._writers_waiting.value -= 1

    def release_write(self):
        """Release a write lock."""
        self._condition.release()
Exemple #15
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)
Exemple #16
0
class kernel:
    """
    Guarda la estructura con la imformacion referente al
    listener
    """
    def __init__(self,dim_i=3,dim_j=1):
        self.jugadores=[]
        self.M=Manager()
        self.listenner=Listener(address=('localhost',6000),authkey='secret password')
        self.color=0
        
        self.numCasillas=0
        self.numEspera=self.M.list([0,0,self.numCasillas])
        self.desocupado=Condition()      
        self.cont=0
        self.turno=self.M.list([0])
        self.dim_i=dim_i
        self.dim_j=dim_j
        self.len=self.dim_i*self.dim_j
        self.tablero=(self.M.list([0]*(self.dim_i*self.dim_j)))

    def trhows(self):
        '''
        lanza los programas iniciando el listenner
           accion
           carga_nuevos_jugadores
        '''
        CNP=Process(target=self.carga_nuevos_jugadores)
        
        
        CNP.start();CNP.join()
    def carga_nuevos_jugadores(self):
        print 'carga up'        
        while self.numCasillas<self.len:
            new_con=self.listenner.accept()
            self.jugadores.append(new_con)
            new_con.send((self.color,(self.dim_i,self.dim_j)))
            P=Process(target=self.check,args=(new_con,self.color))
            (self.color,self.cont)=(1-self.color,self.cont+1)
            print 'pass'
            P.start()
            print 'pass2'
    def check(self,con,color):
            '''
        Controla el I/O de un jugador
        print 'check up'
            '''
            cont=True
            while cont and self.numCasillas<self.len:
                print 'esperando para colocar'
                recibido=print_(con.recv())
                if recibido!='q':
                    cont2=True
                    while cont2:
                        if self.desocupado.acquire():
                            R=self.protocol(recibido)
                            self.desocupado.notify_all()
                            self.desocupado.release()
                            con.send(R)
                            
                            if R[0]==2:
                                sleep(1.0)
                                con.send(R)
                            cont2=False
                        else:
                            self.numEspera[color]+=1
                            print 'pacientes:', self.numEspera
                            self.desocupado.notify_all()
                            self.desocupado.release()
                            self.desocupado.wait()
                            self.numEspera[color]-=1
                else:
                    con.close()
                    cont=False
            
    def protocol(self,recibido):
        
        if (recibido[0]==self.turno[0]) or (self.numEspera[1-self.turno[0]]==0): #self.desocupado.acquire():
            if (self.tablero[(recibido[1])])==0:
                self.turno[0]=1-recibido[0]
                self.tablero[(recibido[1])]=recibido[0]+1
                self.numEspera[2]+=1
                Ans=(1,self.dim_i,self.dim_j,self.tablero[:])
                
            else:
                
                Ans=(2,self.dim_i,self.dim_j,self.tablero[:])
           
            
            
        else:
             Ans= (0,self.dim_i,self.dim_j,self.tablero[:])
        
        if self.numEspera[2]==len(self.tablero):
             Ans=(3,self.dim_i,self.dim_j,self.tablero[:])
        return Ans
Exemple #17
0
class RenderProcess:
    """
    Wraps a multiprocessing.Process for rendering. Assumes there
    is one MjSim per process.
    """
    def __init__(self, device_id, setup_sim, update_sim, output_var_shape):
        """
        Args:
        - device_id (int): GPU device to use for rendering (0-indexed)
        - setup_sim (callback): callback that is given a device_id and
            returns a MjSim. It is responsible for making MjSim render
            to given device.
        - update_sim (callback): callback given a sim and device_id, and
            should return a numpy array of shape `output_var_shape`.
        - output_var_shape (tuple): shape of the synchronized output
            array from `update_sim`.
        """
        self.device_id = device_id
        self.setup_sim = setup_sim
        self.update_sim = update_sim

        # Create a synchronized output variable (numpy array)
        self._shared_output_var = Array(ctypes.c_double,
                                        int(np.prod(output_var_shape)))
        self._output_var = np.frombuffer(self._shared_output_var.get_obj())

        # Number of variables used to communicate with process
        self._cv = Condition()
        self._ready = Value('b', 0)
        self._start = Value('b', 0)
        self._terminate = Value('b', 0)

        # Start the actual process
        self._process = Process(target=self._run)
        self._process.start()

    def wait(self):
        """ Wait for process to be ready for another update call. """
        with self._cv:
            if self._start.value:
                self._cv.wait()
            if self._ready.value:
                return
            self._cv.wait()

    def read(self, copy=False):
        """ Reads the output variable. Returns a copy if copy=True. """
        if copy:
            with self._shared_output_var.get_lock():
                return np.copy(self._output_var)
        else:
            return self._output_var

    def update(self):
        """ Calls update_sim asynchronously. """
        with self._cv:
            self._start.value = 1
            self._cv.notify()

    def stop(self):
        """ Tells process to stop and waits for it to terminate. """
        with self._cv:
            self._terminate.value = 1
            self._cv.notify()
        self._process.join()

    def _run(self):
        sim = self.setup_sim(self.device_id)

        while True:
            with self._cv:
                self._ready.value = 1
                self._cv.notify_all()

            with self._cv:
                if not self._start.value and not self._terminate.value:
                    self._cv.wait()
                if self._terminate.value:
                    break
                assert self._start.value
                self._start.value = 0

            # Run the update and assign output variable
            with self._shared_output_var.get_lock():
                self._output_var[:] = self.update_sim(sim,
                                                      self.device_id).ravel()
Exemple #18
0
class DBPipeline(object):
    """
    Like L{Exscript.workqueue.Pipeline}, but keeps all queued objects
    in a database, instead of using in-memory data structures.
    """
    def __init__(self, engine, max_working = 1):
        self.condition     = Condition(RLock())
        self.engine        = engine
        self.max_working   = max_working
        self.running       = False
        self.paused        = False
        self.metadata      = sa.MetaData(self.engine)
        self._table_prefix = 'exscript_pipeline_'
        self._table_map    = {}
        self.__update_table_names()
        self.clear()

    def __add_table(self, table):
        """
        Adds a new table to the internal table list.
        
        @type  table: Table
        @param table: An sqlalchemy table.
        """
        pfx = self._table_prefix
        self._table_map[table.name[len(pfx):]] = table

    def __update_table_names(self):
        """
        Adds all tables to the internal table list.
        """
        pfx = self._table_prefix
        self.__add_table(sa.Table(pfx + 'job', self.metadata,
            sa.Column('id',     sa.Integer, primary_key = True),
            sa.Column('name',   sa.String(150), index = True),
            sa.Column('status', sa.String(50), index = True),
            sa.Column('job',    sa.PickleType()),
            mysql_engine = 'INNODB'
        ))

    @synchronized
    def install(self):
        """
        Installs (or upgrades) database tables.
        """
        self.metadata.create_all()

    @synchronized
    def uninstall(self):
        """
        Drops all tables from the database. Use with care.
        """
        self.metadata.drop_all()

    @synchronized
    def clear_database(self):
        """
        Drops the content of any database table used by this library.
        Use with care.

        Wipes out everything, including types, actions, resources and acls.
        """
        delete = self._table_map['job'].delete()
        delete.execute()

    def debug(self, debug = True):
        """
        Enable/disable debugging.

        @type  debug: bool
        @param debug: True to enable debugging.
        """
        self.engine.echo = debug

    def set_table_prefix(self, prefix):
        """
        Define a string that is prefixed to all table names in the database.

        @type  prefix: string
        @param prefix: The new prefix.
        """
        self._table_prefix = prefix
        self.__update_table_names()

    def get_table_prefix(self):
        """
        Returns the current database table prefix.
        
        @rtype:  string
        @return: The current prefix.
        """
        return self._table_prefix

    def __len__(self):
        return self._table_map['job'].count().execute().fetchone()[0]

    def __contains__(self, item):
        return self.has_id(id(item))

    def get_from_name(self, name):
        """
        Returns the item with the given name, or None if no such item
        is known.
        """
        with self.condition:
            tbl_j = self._table_map['job']
            query = tbl_j.select(tbl_j.c.name == name)
            row   = query.execute().fetchone()
            if row is None:
                return None
            return row.job

    def has_id(self, item_id):
        """
        Returns True if the queue contains an item with the given id.
        """
        tbl_j = self._table_map['job']
        query = tbl_j.select(tbl_j.c.id == item_id).count()
        return query.execute().fetchone()[0] > 0

    def task_done(self, item):
        with self.condition:
            self.working.remove(item)
            self.all.remove(id(item))
            self.condition.notify_all()

    def append(self, item):
        with self.condition:
            self.queue.append(item)
            self.all.add(id(item))
            self.condition.notify_all()

    def appendleft(self, item, force = False):
        with self.condition:
            if force:
                self.force.append(item)
            else:
                self.queue.appendleft(item)
            self.all.add(id(item))
            self.condition.notify_all()

    def prioritize(self, item, force = False):
        """
        Moves the item to the very left of the queue.
        """
        with self.condition:
            # If the job is already running (or about to be forced),
            # there is nothing to be done.
            if item in self.working or item in self.force:
                return
            self.queue.remove(item)
            self.appendleft(item, force)
            self.condition.notify_all()

    def clear(self):
        with self.condition:
            self.queue    = deque()
            self.force    = deque()
            self.sleeping = set()
            self.working  = set()
            self.all      = set()
            self.condition.notify_all()

    def stop(self):
        """
        Force the next() method to return while in another thread.
        The return value of next() will be None.
        """
        with self.condition:
            self.running = False
            self.condition.notify_all()

    def pause(self):
        with self.condition:
            self.paused = True
            self.condition.notify_all()

    def unpause(self):
        with self.condition:
            self.paused = False
            self.condition.notify_all()

    def sleep(self, item):
        assert id(item) in self.all
        with self.condition:
            self.sleeping.add(item)
            self.condition.notify_all()

    def wake(self, item):
        assert id(item) in self.all
        assert item in self.sleeping
        with self.condition:
            self.sleeping.remove(item)
            self.condition.notify_all()

    def wait_for_id(self, item_id):
        with self.condition:
            while self.has_id(item_id):
                self.condition.wait()

    def wait(self):
        """
        Waits for all currently running tasks to complete.
        """
        with self.condition:
            while self.working:
                self.condition.wait()

    def wait_all(self):
        """
        Waits for all queued and running tasks to complete.
        """
        with self.condition:
            while len(self) > 0:
                self.condition.wait()

    def with_lock(self, function, *args, **kwargs):
        with self.condition:
            return function(self, *args, **kwargs)

    def set_max_working(self, max_working):
        with self.condition:
            self.max_working = int(max_working)
            self.condition.notify_all()

    def get_max_working(self):
        return self.max_working

    def get_working(self):
        return list(self.working)

    def _popleft_sleeping(self):
        sleeping = []
        while True:
            try:
                node = self.queue[0]
            except IndexError:
                break
            if node not in self.sleeping:
                break
            sleeping.append(node)
            self.queue.popleft()
        return sleeping

    def _get_next(self):
        # We need to leave sleeping items in the queue because else we
        # would not know their original position after they wake up.
        # So we need to temporarily remove sleeping items from the top of
        # the queue here.
        sleeping = self._popleft_sleeping()

        # Get the first non-sleeping item from the queue.
        try:
            next = self.queue.popleft()
        except IndexError:
            next = None

        # Re-insert sleeping items.
        self.queue.extendleft(sleeping)
        return next

    def next(self):
        with self.condition:
            self.running = True
            while self.running:
                if self.paused:
                    self.condition.wait()
                    continue

                # Wait until enough slots are available.
                if len(self.working) - \
                   len(self.sleeping) - \
                   len(self.force) >= self.max_working:
                    self.condition.wait()
                    continue

                # Forced items are returned regardless of how many tasks
                # are already working.
                try:
                    next = self.force.popleft()
                except IndexError:
                    pass
                else:
                    self.working.add(next)
                    return next

                # Return the first non-sleeping task.
                next = self._get_next()
                if next is None:
                    self.condition.wait()
                    continue
                self.working.add(next)
                return next
class WaitableQueue(Queue):
    """Queue that uses a semaphore to reliably count items in it"""
    class Vacuum(ThreadLoop):
        def __init__(self, q, l):
            def callback():
                q.wait_notempty(0.1)

                while True:
                    try:
                        val = q.get(False)
                        l.append(val)

                    except Empty:
                        break

            ThreadLoop.__init__(self, callback)

    def __init__(self, maxsize=0):
        self.cond_empty = Condition()
        self.cond_notempty = Condition()
        self._put_counter = Value('i', 0)

        Queue.__init__(self, maxsize)

    def put(self, obj, block=True, timeout=None):
        Queue.put(self, obj, block, timeout)
        self._put_counter.value += 1

        if self.qsize() != 0:
            self.cond_notempty.acquire()
            try:
                self.cond_notempty.notify_all()
            finally:
                self.cond_notempty.release()

    @property
    def put_counter(self):
        return self._put_counter.value

    def get(self, block=True, timeout=None):
        ret = Queue.get(self, block, timeout)
        if self.qsize() == 0:
            self.cond_empty.acquire()
            try:
                self.cond_empty.notify_all()
            finally:
                self.cond_empty.release()

        return ret

    def wait_empty(self, timeout=None):
        """Wait for all items to be got"""
        self.cond_empty.acquire()
        try:
            if self.qsize():
                self.cond_empty.wait(timeout)
        finally:
            self.cond_empty.release()

    def wait_notempty(self, timeout=None):
        """Wait for all items to be got"""
        self.cond_notempty.acquire()
        try:
            if self.qsize() == 0:
                self.cond_notempty.wait(timeout)
        finally:
            self.cond_notempty.release()
Exemple #20
0
class MultiCore(Processor):
    # this is currently not used at no advantage was found... proceed with caution as it may be buggy
    """docstring for MultiCoreDetector"""
    def __init__(self, *args, **kwargs):
        Processor.__init__(self, *args, **kwargs)
        key = kwargs.get("key", 123456)

        self.sm = SharedMemory(
            key, params["resolution"][0] * params["resolution"][0] * 3)
        self.start_cond = Condition()
        self.stop_event = multiprocessing.Event()
        self.workers = [
            MultiCore.Worker(detector=detector,
                             key=key,
                             start_cond=self.start_cond,
                             stop_event=self.stop_event).start()
            for detector in self.detectors
        ]
        print("Multicore workers:")
        for worker in self.workers:
            print("  Woker: {}, pid={}".format(worker.name, worker.pid))

    def stop(self):
        Processor.stop(self)
        self.stop_event.set()
        for worker in self.workers:
            worker.join()

    def write(self, b):
        if params["verbose"] > 0:
            e1 = cv2.getTickCount()

        centers = self.processImage(b)

        if self.callback:
            self.callback(centers)

        if params['verbose']:
            e2 = cv2.getTickCount()
            elapsed_time = (e2 - e1) / cv2.getTickFrequency()

            c = ", ".join((
                "({0:6.2f}, {1:6.2f})" if math.
                isnan(center[2]) else "({0:6.2f}, {1:6.2f}, {2:6.4f})").format(
                    *center) if center else "None" for center in centers)

            print('Frame: {:5}, center [{}], elapsed time: {:.1f}ms'.format(
                self.frame_number, c, elapsed_time * 1000))

        self.frame_number += 1

        if self.stop_event.is_set():
            raise StopIteration("Number of frames done")

        self.sm.write(image)
        with self.start_cond:
            self.start_cond.notify_all()

        detector_results = [
            worker.get_result(timeout=0.5) for worker in self.workers
        ]
        # merge results
        centers = list(itertools.chain.from_iterable(detector_results))
        return centers

    class Worker(Process):
        """docstring for Worker"""
        def __init__(self, detector, start_cond, stop_event, key):
            Process.__init__(self, name=detector.name, daemon=True)
            self.detector = detector
            self.start_cond = start_cond
            self.stop_event = stop_event
            self.result_queue = Queue(1)
            self.key = key

        def start(self):
            Process.start(self)
            return self

        def run(self):
            self.sm = SharedMemory(self.key,
                                   params["resolution"][0] *
                                   params["resolution"][0] * 3,
                                   create=False)
            try:
                while not self.stop_event.is_set():
                    with self.start_cond:
                        c = self.start_cond.wait(0.5)
                    if c:
                        if params["verbose"] > 1:
                            e1 = cv2.getTickCount()
                        data = np.fromstring(self.sm.read(lock=False),
                                             dtype=np.uint8)
                        image = np.resize(data, (params["resolution"][1],
                                                 params["resolution"][0], 3))
                        centers = self.detector.processImage(-1, image)
                        self.result_queue.put(centers, timeout=0.5)
                        if params["verbose"] > 1:
                            e2 = cv2.getTickCount()
                            elapsed_time = (e2 - e1) / cv2.getTickFrequency()
                            print("{}: {:.2f}ms".format(
                                self.name, elapsed_time * 1000))
            except KeyboardInterrupt:
                self.stop_event.set()

        def get_result(self, *args, **kwargs):
            try:
                return self.result_queue.get(*args, **kwargs)
            except Queue.Empty:
                print("Lost sample in {} process".format(self.name))
                return None
class RWLock():
    """A Readers-Writer lock.
    
    Allows for multiple readers or one writer. Writers will not starve.

    Attributes:
        for_reading (RWLock.ReadLock): A lock-like object with appropriate
            `acquire`, `release`, `__enter__` and `__exit__` methods pointed
            to the *read methods of the RWLock. Chiefly for use with the 
            `with` statement.
        for_writing (RWLock.WriteLock): A lock-like object with appropriate
            `acquire`, `release`, `__enter__` and `__exit__` methods pointed
            to the *write methods of the RWLock. Chiefly for use with the 
            `with` statement.

    """
    class ReadLock():
        def __init__(self, rw):
            self._rw = rw
            self.acquire = rw.acquire_read
            self.release = rw.release_read
        def __enter__(self):
            self.acquire()
        def __exit__(self, exception_type, exception_value, traceback):
            self.release()

    class WriteLock():
        def __init__(self, rw):
            self._rw = rw
            self.acquire = rw.acquire_write
            self.release = rw.release_write
        def __enter__(self):
            self.acquire()
        def __exit__(self, exception_type, exception_value, traceback):
            self.release()

    def __init__(self):
        """Initialises the RWLock."""
        self._condition = Condition()
        self._readers = Value(c_uint64, 0, lock=False)
        self._writers_waiting = Value(c_uint64, 0, lock=False)

        self.for_reading = self.ReadLock(self)
        self.for_writing = self.WriteLock(self)

    def acquire_read(self):
        """Acquire a read lock. 
        
        Blocks if a thread has acquired the write lock or is waiting to
        acquire the write lock.
        
        """
        with self._condition:
            while self._writers_waiting.value:
                self._condition.wait()
            self._readers.value += 1

    def release_read(self):
        """Release a read lock."""
        with self._condition:
            self._readers.value -= 1
            if not self._readers.value:
                self._condition.notify_all()

    def acquire_write(self):
        """Acquire a write lock.
        
        Blocks until there are no acquired read or write locks.
        
        """
        self._condition.acquire()
        self._writers_waiting.value += 1
        while self._readers.value:
            self._condition.wait()
        self._writers_waiting.value -= 1

    def release_write(self):
        """Release a write lock."""
        self._condition.release()
import time

from multiprocessing import Process, Condition
import os

cond = Condition()

def wait_condition():
    cond.acquire()
    cond.wait()
    print '[%d] waked!' % os.getpid()
    cond.release()

for i in range(3):
    Process(target=wait_condition).start()

time.sleep(1)
print 'notify!'
cond.acquire()
cond.notify_all()
cond.release()
def solve(max_level, goal, num_workers):
    # prepare message queue shared with workers
    tasks = Queue()
    task_lock = Lock()
    task_cv = Condition(lock=task_lock)

    # create and start workers
    workers = []
    for i in range(0, num_workers):
        solutions = set()
        parent_conn, child_connn = Pipe()
        worker = Process(target=run_worker,
                         args=(child_connn, goal, max_level, tasks,
                               task_lock, task_cv))
        worker.start()
        workers.append((worker, parent_conn))

    # Find all possible sequences: [n0, n1, n2, ..., nM] (M=max_level)
    # where nX is the number of binary operators so that
    # '1 <n0 ops> 2 <n1 ops> 3 <n2 ops> ... M+1 <nM ops>' can be a valid
    # Reverse Polish Notation.  Key conditions are:
    # 1. n0 + n1 + ... + nM = M
    # 2. for any X, n0 + n1 + ... + nX <= X
    # (Note that from condition #2 n0 is always 0.)
    # We'll build the sequences in 'numops_list' below while exploring cases
    # in a BFS-like (or DP-like) manner.

    # This is a queue to maintain outstanding search results.  Its each element
    # is a tuple of 2 items: 'numops_list', 'total_ops'
    # A tuple of (N, T) means:
    # - N = [n0, n1, ..., nX]
    # - T = sum(N)
    # (Note that we don't necessarily have to keep T as it can be derived
    # from N.  But we do this for efficiency).
    # The search is completed when len(N) reaches M (i.e., X=M-1) by appending
    # the last item of nM = M - (n0 + n1 + ... + nX) = M - T (see condition #1).
    tmp = [([0], 0)]

    while tmp:
        numops_list, total_ops = tmp.pop(0)
        level = len(numops_list)
        if level < max_level:
            # Expand the sequence with all possible numbers of operators at
            # the current level so we can explore the next level for each of
            # them.
            for i in range(0, level - total_ops + 1): # see condition #2
                tmp.append((numops_list + [i], total_ops + i))
        else:
            # Found one valid RPN template.  Pass it to workers and have them
            # work on it.
            numops_list.append(max_level - total_ops)
            with task_lock:
                tasks.put(numops_list)
                task_cv.notify()

    # Tell workers all data have been passed.
    solutions = set()
    with task_lock:
        for _ in workers:
            tasks.put(None)
        task_cv.notify_all()

    # Wait until all workers complete the tasks, while receiving any
    # intermediate and last solutions.  The received solutions may not
    # necessarily be fully unique, so we have to unify them here, again.
    # Received data of 'None' means the corresponding worker has completed
    # its task.
    # Note: here we assume all workers are reasonably equally active in
    # sending data, so we simply perform blocking receive.
    conns = set([w[1] for w in workers])
    while conns:
        for c in conns.copy():
            worker_data = c.recv()
            if worker_data is None:
                conns.remove(c)
                continue
            for solution in worker_data:
                if solution not in solutions:
                    solutions.add(solution)

    # All workers have completed.  Cleanup them and print the final unified
    # results.  If we are to show all expressions (i.e. goal is None), sort
    # results by the expressions' values (listing integers followed by all
    # non-integers, followed by 'divided by 0' cases.
    for w in workers:
        w[0].join()
    if goal is None:
        l = list(solutions)
        l.sort(key=lambda x: (0, x[0]) if type(x[0]) == int else (1, str(x[0])))
        for solution in l:
            print('%s = %s' % (solution[1], str(solution[0])))
    else:
        for solution in solutions:
            print(solution)
Exemple #24
0
class Pipeline(object):
    """
    A collection that is similar to Python's Queue object, except
    it also tracks items that are currently sleeping or in progress.
    """
    def __init__(self, max_working = 1):
        self.condition   = Condition(RLock())
        self.max_working = max_working
        self.running     = True
        self.paused      = False
        self.queue       = None
        self.force       = None
        self.sleeping    = None
        self.working     = None
        self.item2id     = None
        self.id2item     = None # for performance reasons
        self.name2id     = None
        self.id2name     = None
        self.clear()

    def __len__(self):
        with self.condition:
            return len(self.id2item)

    def __contains__(self, item):
        with self.condition:
            return item in self.item2id

    def _register_item(self, name, item):
        uuid               = uuid4().hex
        self.id2item[uuid] = item
        self.item2id[item] = uuid
        if name is None:
            return uuid
        if name in self.name2id:
            msg = 'an item named %s is already queued' % repr(name)
            raise AttributeError(msg)
        self.name2id[name] = uuid
        self.id2name[uuid] = name
        return uuid

    def get_from_name(self, name):
        """
        Returns the item with the given name, or None if no such item
        is known.
        """
        with self.condition:
            try:
                item_id = self.name2id[name]
            except KeyError:
                return None
            return self.id2item[item_id]
        return None

    def has_id(self, item_id):
        """
        Returns True if the queue contains an item with the given id.
        """
        return item_id in self.id2item

    def task_done(self, item):
        with self.condition:
            try:
                self.working.remove(item)
            except KeyError:
                # This may happen if we receive a notification from a
                # thread that was previously enqueued, but then the
                # workqueue was forcefully stopped without waiting for
                # child threads to complete.
                self.condition.notify_all()
                return
            item_id = self.item2id.pop(item)
            self.id2item.pop(item_id)
            try:
                name = self.id2name.pop(item_id)
            except KeyError:
                pass
            else:
                self.name2id.pop(name)
            self.condition.notify_all()

    def append(self, item, name = None):
        """
        Adds the given item to the end of the pipeline.
        """
        with self.condition:
            self.queue.append(item)
            uuid = self._register_item(name, item)
            self.condition.notify_all()
            return uuid

    def appendleft(self, item, name = None, force = False):
        with self.condition:
            if force:
                self.force.append(item)
            else:
                self.queue.appendleft(item)
            uuid = self._register_item(name, item)
            self.condition.notify_all()
            return uuid

    def prioritize(self, item, force = False):
        """
        Moves the item to the very left of the queue.
        """
        with self.condition:
            # If the job is already running (or about to be forced),
            # there is nothing to be done.
            if item in self.working or item in self.force:
                return
            self.queue.remove(item)
            if force:
                self.force.append(item)
            else:
                self.queue.appendleft(item)
            self.condition.notify_all()

    def clear(self):
        with self.condition:
            self.queue    = deque()
            self.force    = deque()
            self.sleeping = set()
            self.working  = set()
            self.item2id  = dict()
            self.id2item  = dict()
            self.name2id  = dict()
            self.id2name  = dict()
            self.condition.notify_all()

    def stop(self):
        """
        Force the next() method to return while in another thread.
        The return value of next() will be None.
        """
        with self.condition:
            self.running = False
            self.condition.notify_all()

    def start(self):
        with self.condition:
            self.running = True
            self.condition.notify_all()

    def pause(self):
        with self.condition:
            self.paused = True
            self.condition.notify_all()

    def unpause(self):
        with self.condition:
            self.paused = False
            self.condition.notify_all()

    def sleep(self, item):
        with self.condition:
            self.sleeping.add(item)
            self.condition.notify_all()

    def wake(self, item):
        assert item in self.sleeping
        with self.condition:
            self.sleeping.remove(item)
            self.condition.notify_all()

    def wait_for_id(self, item_id):
        with self.condition:
            while self.has_id(item_id):
                self.condition.wait()

    def wait(self):
        """
        Waits for all currently running tasks to complete.
        """
        with self.condition:
            while self.working:
                self.condition.wait()

    def wait_all(self):
        """
        Waits for all queued and running tasks to complete.
        """
        with self.condition:
            while len(self) > 0:
                self.condition.wait()

    def with_lock(self, function, *args, **kwargs):
        with self.condition:
            return function(self, *args, **kwargs)

    def set_max_working(self, max_working):
        with self.condition:
            self.max_working = int(max_working)
            self.condition.notify_all()

    def get_max_working(self):
        return self.max_working

    def get_working(self):
        return list(self.working)

    def _popleft_sleeping(self):
        sleeping = []
        while True:
            try:
                node = self.queue[0]
            except IndexError:
                break
            if node not in self.sleeping:
                break
            sleeping.append(node)
            self.queue.popleft()
        return sleeping

    def _get_next(self, pop = True):
        # We need to leave sleeping items in the queue because else we
        # would not know their original position after they wake up.
        # So we need to temporarily remove sleeping items from the top of
        # the queue here.
        sleeping = self._popleft_sleeping()

        # Get the first non-sleeping item from the queue.
        if pop:
            try:
                next = self.queue.popleft()
            except IndexError:
                next = None
        else:
            try:
                next = self.queue[0]
            except IndexError:
                next = None

        # Re-insert sleeping items.
        self.queue.extendleft(sleeping)
        return next

    def try_next(self):
        """
        Like next(), but only returns the item that would be selected
        right now, without locking and without changing the queue.
        """
        with self.condition:
            try:
                return self.force[0]
            except IndexError:
                pass

            return self._get_next(False)

    def next(self):
        with self.condition:
            while self.running:
                if self.paused:
                    self.condition.wait()
                    continue

                # Wait until enough slots are available.
                if len(self.working) - \
                   len(self.sleeping) - \
                   len(self.force) >= self.max_working:
                    self.condition.wait()
                    continue

                # Forced items are returned regardless of how many tasks
                # are already working.
                try:
                    next = self.force.popleft()
                except IndexError:
                    pass
                else:
                    self.working.add(next)
                    return next

                # Return the first non-sleeping task.
                next = self._get_next()
                if next is None:
                    self.condition.wait()
                    continue
                self.working.add(next)
                return next
        return None
Exemple #25
0
class Orchestrator:
    def __init__(self,
                 submission_queue: multiprocessing.Queue,
                 status_provider: BatchStatusProvider,
                 config_file: str,
                 strict_config: bool,
                 log_folder: str,
                 cache_search_dirs: List[str],
                 log_event_que: LogEventQueue,
                 singleton_run_summary_path: Optional[str] = None):

        self._submission_que: multiprocessing.Queue = submission_queue
        self._status_provider: BatchStatusProvider = status_provider
        self._config_file: str = config_file
        self._strict_config: bool = strict_config
        self._log_folder: str = log_folder
        self._cache_search_dirs = cache_search_dirs
        self._log_event_que = log_event_que
        self._singleton_run_summary_path = singleton_run_summary_path
        self._on_batch_id = -1
        self._on_batch_type: type = type(None)

        self._master_thread = Thread(target=self._master_thread_loop,
                                     name="OrchestratorMasterThread",
                                     args=(()),
                                     daemon=True)

        self._run_summary_thread_gate = Event()
        self._run_summary_thread = Thread(target=self._run_summary_loop,
                                          name="OrchestratorRunSummaryThread",
                                          args=(()),
                                          daemon=True)

        self.__debug_loop_thread = Thread(target=self.__debug_loop,
                                          name="OrchestratorDebugLoop",
                                          args=(()),
                                          daemon=True)

        #TODO(andwald): The following hypothetical thread dynamically sets RTF and Concurrency of EndpointManagers
        #               according to its own decoupled logic. This will be nice and pluggable since EndpointManagers
        #               already adhere to whatever the dynamic settings are for the Atomic Shared Variables of
        #               RTF and Concurrency, which is what this thread will manipulate.
        # self._perf_thread = Thread(target=self.perf_thread_loop, name="OrchestratorPerfThread", args=(self,), daemon=True)

        self._file_queue = Queue()
        self._file_queue_size = 0
        self._in_progress: Dict[str, WorkItemRequest] = {
        }  # WorkItemRequest.filepath -> WorkItemRequest
        self._in_progress_owner: Dict[str, EndpointManager] = {
        }  # WorkItemRequest.filepath -> EndpointManager
        self._work_results: Dict[str, WorkItemResult] = {
        }  # WorkItemRequest.filepath -> WorkItemResult
        self._batch_completion_evt = Event()
        self._accounting_lock = RLock()
        self._file_queue_cond = Condition(self._accounting_lock)
        self._run_summary_lock = Lock()
        self._misc_lock = Lock()
        self._summarizer: BatchRunSummarizer = None
        self._stop_requested = False

        self._endpoint_managers: List[EndpointManager] = []
        self._endpoint_generation = 0
        self._old_managers = set(
        )  # Set[str], contains names of now-inactive endpoint managers
        self._config_notifier: ThreadedNotifier = \
            notify_file_modified(self._config_file, self.hotswap_endpoint_managers, self._log_event_que)

        self._start_time = time.time()
        self._creator_pid = current_process().pid
        logger.info("Orchestrator created by process: {0}".format(
            self._creator_pid))
        self.__cnt_work_success_cb = 0
        self.__cnt_work_failure_cb = 0

        self._master_thread.start()
        self._run_summary_thread.start()
        # self.__debug_loop_thread.start()  # Enable to debug concurrency changes.

    def is_alive(self):
        return self._master_thread.is_alive()

    def join(self):
        self._master_thread.join()

    def _run_summary_loop(self):
        while not self._stop_requested:
            # Prevent redundant updates when nothing can change.
            self._run_summary_thread_gate.wait()
            if self._stop_requested:
                return

            if self._on_batch_id > -1 and self._summarizer:
                try:
                    self.write_summary_information(write_run_summary=True,
                                                   log_conclusion_msg=False)

                # Don't ever let this thread die as it's too important.
                # Log and re-try. Repetitive failure loop will at least get logged.
                except Exception as e:
                    exception_details = traceback.format_exc()
                    logger.error(
                        "Orchestrator: run_summary_thread in run_summary_loop(): "
                        "Caught {0}, \nDetails: {1}".format(
                            type(e).__name__, exception_details))

            time.sleep(RUN_SUMMARY_LOOP_INTERVAL)

    def __debug_loop(self):
        """
        This is only intended to be used during development and debugging.
        """
        def _check_lock_acq(lock):
            acquired = lock.acquire(block=False)
            if acquired:
                lock.release()
                return False
            # We weren't able to acquire, so it's taken
            return True

        # Loop forever. This is a daemonic thread and it will intentionally
        # only die when the process owning Orchestrator dies.
        last_cnt_work_success = 0
        while True:
            logger.debug("Stop requested: {0}".format(self._stop_requested))
            logger.debug("Batch que size: {0}".format(
                self._submission_que.qsize()))
            logger.debug("On batch id: {0}".format(self._on_batch_id))
            logger.debug("File queue size: {0}".format(self._file_queue_size))
            logger.debug("Num in progress: {0}".format(len(self._in_progress)))
            logger.debug("Orchestrator accounting lock taken: {0}".format(
                _check_lock_acq(self._accounting_lock)))
            logger.debug("Status provider accounting lock taken: {0}".format(
                _check_lock_acq(BatchStatusProvider.lock)))
            logger.debug(
                "Notify work success callback entry count: {0}".format(
                    self.__cnt_work_success_cb))
            logger.debug(
                "Work items completed since last debug print: {0}".format(
                    self.__cnt_work_success_cb - last_cnt_work_success))
            last_cnt_work_success = self.__cnt_work_success_cb
            logger.debug(
                "Notify work failure callback entry count: {0}".format(
                    self.__cnt_work_failure_cb))
            logger.debug("Run summary thread alive: {0}".format(
                self._run_summary_thread.is_alive()))
            logger.debug("Number of old endpoint managers: {0}".format(
                len(self._old_managers)))
            for epm in self._endpoint_managers:
                logger.debug("Endpoint manager: {0}".format(epm.name))
                logger.debug("   Current requests: {0}".format(
                    epm._current_requests))
                logger.debug("   Current requests lock taken: {0}".format(
                    _check_lock_acq(epm._current_requests_lock)))
                logger.debug("   Pool apply_async count: {0}".format(
                    epm._cnt_apply_async))
                logger.debug("   Pool callback count: {0}".format(
                    epm._cnt_pool_cb))
                logger.debug("   Pool callback returns count: {0}".format(
                    epm._cnt_pool_cb_rets))
                logger.debug("   Stop requested: {0}".format(
                    epm._stop_requested))
                logger.debug("   Now trying to steal work: {0}".format(
                    epm._in_steal_work_fn))
            logger.debug("Stack frames of all threads:")
            logger.debug("\n*** STACKTRACE - START ***\n")
            current_threads_stacktrace(use_logger=True)
            logger.debug("\n*** STACKTRACE - END ***\n")
            time.sleep(DEBUG_LOOP_INTERVAL)

    def write_summary_information(self,
                                  write_run_summary: bool = True,
                                  log_conclusion_msg: bool = False,
                                  allow_fail: bool = False):
        """
        Summarize individual file results, along with overall results,
        and write them to log and/or file. Also log a conclusion message
        if requested.
        :param write_run_summary: whether run summary (individual files + overall)
                                  should be written to file.
        :param log_conclusion_msg: whether a conclusion message should be logged
                                   which includes final stats and lists failures.
        :param allow_fail: log failure to write run summary but do not raise exception.
        """
        # To ensure history serialization, we wrap this method
        # in its own lock that nobody else contends with except for
        # the threads that invoke this.
        with self._run_summary_lock:

            # Take a consistent snapshot and then report on the snapshot
            # without holding back forward progress.
            with self._accounting_lock:
                snap_work_results: Dict[str, WorkItemResult] = copy.deepcopy(
                    self._work_results)
                snap_file_queue_size: int = self._file_queue_size
                snap_num_running: int = len(self._in_progress)
                snap_run_summarizer: BatchRunSummarizer = self._summarizer
                snap_batch_id: int = self._on_batch_id

            summary_json = {}
            # It's uncommon that a run summarizer wouldn't be available yet but this could
            # happen for example by signaling early termination to the Orchestrator.
            if snap_run_summarizer:
                summary_json = snap_run_summarizer.run_summary(
                    snap_work_results, snap_file_queue_size,
                    snap_num_running, self._start_time,
                    len(self._endpoint_managers), log_conclusion_msg)

            # Write the summary json file
            if write_run_summary:
                try:
                    if self._singleton_run_summary_path:
                        logger.debug(
                            "Updating singleton run_summary: {0}".format(
                                self._singleton_run_summary_path))
                        write_json_file_atomic(
                            summary_json, self._singleton_run_summary_path)
                    else:
                        try:
                            self._status_provider.set_run_summary(
                                snap_batch_id, summary_json)
                        except BatchNotFoundException:
                            # This is benign and means we caught a rare race condition
                            # in which the batch directory is very recently deleted.
                            pass
                    # Minimal throttle on file writes. We are under _run_summary_lock.
                    time.sleep(3)
                except Exception as e:
                    logger.warning("Failed to write run_summary: {0}".format(
                        str(e)))
                    if not allow_fail:
                        raise

    def request_stop(self):
        """
        Arrange for conditions that will lead to a fast conclusion
        of any ongoing batch without finishing whatever is remaining or
        in progress in this batch if any. This will also cause
        EndpointManagers to shut down. Orchestrator's join() is
        guaranteed to eventually return.
        """
        # Assume this might be called from a signal handler.
        # Instead of preventing child proc inheritance of signals,
        # we eliminate any leaky abstractions by permitting children
        # and those who spawn them to be completely blameless
        # for creating unexpected conditions.
        if current_process().pid != self._creator_pid:
            return

        with self._misc_lock:
            try:
                if self._config_notifier:
                    self._config_notifier.stop()
                    self._config_notifier = None
            except OSError as e:
                # ThreadedNotifier.stop() is not idempotent and gives
                # errno EBADF if it is already stopped.
                if e.errno != errno.EBADF:
                    raise

        # A couple facts about Python3 in case there is any concern
        # about being invoked by a signal handler.
        # 1 - Only the main thread of a process can handle
        # signals, so now we know we are the main thread of the
        # creator process in the signal case.
        # 2 - When running a signal handler, the main thread is
        # is still subject to preemption at tick and the GIL
        # can still be released for other threads. This means
        # that picking up the lock here cannot create deadlock,
        # unless the main thread itself was holding the lock before
        # the signal. That's why we use ReentrantLock.
        with self._accounting_lock:
            self._stop_requested = True
            while self._file_queue_size > 0:
                self._file_queue.get()
                self._file_queue_size -= 1
            self._submission_que.put(None)
            self._file_queue_cond.notify_all()
            self._batch_completion_evt.set()
            for m in self._endpoint_managers:
                m.request_stop()
            self._run_summary_thread_gate.set()

    def steal_work(self, manager: EndpointManager) -> WorkItemRequest:
        """
        :param manager: the EndpointManager who is trying to steal work.
        :returns str: audio file of work to do
        """
        sentinel = SentinelWorkItemRequest()

        # Classic consumer waiter pattern using condition variable.
        self._accounting_lock.acquire()
        while True:
            if manager.name in self._old_managers or self._stop_requested:
                work = sentinel
                break
            if self._file_queue_size > 0:
                work: WorkItemRequest = self._file_queue.get()
                self._file_queue_size -= 1

                # Eliminate this manager early if we detect a language mismatch.
                # It will be recreated on a new batch.
                if work.language and manager.endpoint_config["language"].lower(
                ) != work.language.lower():
                    self._file_queue.put(
                        work)  # back on queue for someone qualified
                    self._file_queue_size += 1
                    self._file_queue_cond.notify()
                    work = sentinel
                    break

                # Got some work to do!
                self._in_progress[work.filepath] = work
                self._in_progress_owner[work.filepath] = manager
                break
            else:
                # Back to sleep because we got nothing.
                self._file_queue_cond.wait(
                )  # implicit self.accounting_lock.release()
        self._accounting_lock.release()
        return work

    def _merge_results(self, filepath: str, result: WorkItemResult):
        if filepath not in self._work_results:
            self._work_results[filepath] = result
        else:
            prev_attempts = self._work_results[filepath].attempts
            result.attempts += prev_attempts
            self._work_results[filepath] = result

    def notify_work_success(self, filepath: str, manager: EndpointManager,
                            result: WorkItemResult):
        with self._accounting_lock:
            self.__cnt_work_success_cb += 1
            if manager.name in self._old_managers:
                # The AudioFileWork item would already be back in pending
                # or running by someone else or finished. Covers an uncommon race.
                return
            if self._stop_requested:
                # It's too late for updating batch status and we're about to die.
                return
            del self._in_progress[filepath]
            del self._in_progress_owner[filepath]

            self._merge_results(filepath, result)

            # Did we just finish the batch?
            if self._file_queue_size == 0 and len(self._in_progress) == 0:
                self._batch_completion_evt.set()

    def notify_work_failure(self, filepath: str, manager: EndpointManager,
                            result: WorkItemResult):
        with self._accounting_lock:
            self.__cnt_work_failure_cb += 1
            if manager.name in self._old_managers:
                # The WorkItemResult would already be back in pending
                # or running by someone else or finished. Covers an uncommon race.
                return
            if self._stop_requested:
                # It's too late for updating batch status and we're about to die.
                return

            self._merge_results(filepath, result)

            # Do we give it another chance?
            # Check retry-ability and num retries burned already.
            if result.can_retry and \
                    self._work_results[filepath].attempts - 1 < ORCHESTRATOR_SCOPE_MAX_RETRIES:
                self._log_event_que.debug(
                    "Placed work item {0} back into queue since retriable.".
                    format(filepath))
                self._file_queue.put(self._in_progress[filepath])
                self._file_queue_size += 1
                self._file_queue_cond.notify()
            # Else no more retries.
            # Either way the item is no longer in progress.
            del self._in_progress[filepath]
            del self._in_progress_owner[filepath]

            # Did we just finish the batch? E.g. finally gave up on this work
            # item and that so happens to be the last in the batch.
            if self._file_queue_size == 0 and len(self._in_progress) == 0:
                self._batch_completion_evt.set()

    def hotswap_endpoint_managers(self):
        config_data = load_configuration(self._config_file,
                                         self._strict_config)

        with self._accounting_lock:
            if self._stop_requested:
                return

            # Get the unique generation of these endpoint managers, which
            # is useful for both debugging and logging.
            gen = self._endpoint_generation
            self._endpoint_generation += 1

            # Get an EndpointStatusChecker for the type of the
            # BatchRequest that is currently being processed.
            ep_status_checker: EndpointStatusChecker
            if isinstance(None, self._on_batch_type):
                ep_status_checker = UnknownEndpointStatusChecker(
                    self._log_event_que)
            else:
                ep_status_checker = self._on_batch_type.get_endpoint_status_checker(
                    self._log_event_que)

            try:
                # Determine EndpointManagers that need to be deleted (modified, new,
                # or no longer exist). Do not touch EndpointManagers that have not changed.
                new_em_objs: List[EndpointManager] = []
                # Start by assuming every EndpointManager needs to be deleted.
                deleted_managers: Dict[str, EndpointManager] = \
                    {em.endpoint_name: em for em in self._endpoint_managers}

                for endpoint_name, endpoint_config in config_data.items():
                    # If an existing endpoint is totally preserved in the new config, don't delete it.
                    # Also require that the endpoint's manager is not terminally stopped, otherwise we need
                    # a new instance of it anyway.
                    if endpoint_name in deleted_managers and \
                      endpoint_config == deleted_managers[endpoint_name].endpoint_config and \
                      not deleted_managers[endpoint_name]._stop_requested:  # noqa
                        # Don't delete this EndpointManager and don't make a new one.
                        del deleted_managers[endpoint_name]
                        continue

                    new_em_objs.append(
                        EndpointManager(
                            "HotswapGen{0}_{1}".format(str(gen),
                                                       endpoint_name),
                            endpoint_name,
                            endpoint_config,
                            self._log_folder,
                            self._log_event_que,
                            self._cache_search_dirs,
                            # on EndpointManager has capacity to steal work
                            self.steal_work,
                            # on EndpointManager reports success
                            self.notify_work_success,
                            # on EndpointManager reports failure
                            self.notify_work_failure,
                            ep_status_checker,
                        ))
            # Validation of the config could fail or invalid yaml may have been given, etc.
            # We catch anything so that we may permit another attempt later with a proper config file.
            # We report it in the logs and somewhere else we will die if no forward progress for too long.
            except Exception as e:
                exception_details = traceback.format_exc()
                logger.error(
                    "Caught Exception '{0}' reading config. Details: {1}\n{2}".
                    format(type(e).__name__, str(e), exception_details))
                # Don't proceed to stop the old EndpointManagers because they're all we've got to go on.
                return
            if self._stop_requested:
                return

            # Also swap the EndpointManagers under lock in case of race.
            # First stop the old EndpointManagers to be deleted.
            for m in self._endpoint_managers:
                if m.endpoint_name in deleted_managers:
                    self._old_managers.add(m.name)
                    m.request_stop()

            # Un-assign work in progress for deleted EndpointManagers.
            # Now anything the old managers might still callback
            # would be rejected so we can safely move in progress back to queue.
            work_in_progress = {k: v
                                for k, v in self._in_progress.items()
                                }  # shallow copy
            work_in_progress_owner = {
                k: v
                for k, v in self._in_progress_owner.items()
            }  # shallow copy
            for filepath, work_item in self._in_progress.items():
                owner_endpoint_name = self._in_progress_owner[
                    filepath].endpoint_name
                # If the EndpointManager that owns this work item is being deleted,
                # free up the work item.
                if owner_endpoint_name in deleted_managers:
                    del work_in_progress[filepath]
                    del work_in_progress_owner[filepath]
                    self._file_queue.put(work_item)
                    self._file_queue_size += 1
            self._in_progress = work_in_progress
            self._in_progress_owner = work_in_progress_owner

            # We've potentially repopulated the file_queue.
            self._file_queue_cond.notify_all()

            # Start the new EndpointManagers.
            for m in new_em_objs:
                m.start()

            # Record the latest set of all EndpointManagers.
            self._endpoint_managers = \
                [em for em in self._endpoint_managers
                 if em.endpoint_name not in deleted_managers] + \
                new_em_objs

            # Ensure that they are all using the correct type of EndpointStatusChecker
            # which depends on the subtype of BatchRequest we are currently processing.
            for m in self._endpoint_managers:
                m.set_endpoint_status_checker(ep_status_checker)

        logger.info(
            "Set new EndpointManagers after hot-swap: {0}".format(config_data))

    def _master_finalize(self):
        """
        Work to be done before Orchestrator's master thread exits.
        """
        # Log conclusion of run_summary information if at singleton level.
        if self._singleton_run_summary_path:
            self.write_summary_information(write_run_summary=False,
                                           log_conclusion_msg=True)

    def _master_thread_loop(self):

        # Keep doing batches until given a stop request.
        while True:
            # Starting a new batch.
            request: BatchRequest = self._submission_que.get()
            with self._accounting_lock:
                self._on_batch_type = type(request)

            # Recreate the endpoints on start of a new batch in case
            # the last batch disabled endpoints, e.g. for mismatched
            # language or other reasons.
            self.hotswap_endpoint_managers()

            with self._accounting_lock:
                if self._stop_requested:
                    self._master_finalize()
                    return

                # Starting a new batch.
                # Reset record keeping if it's not singleton run summary.
                if self._singleton_run_summary_path is None:
                    self._work_results = {}
                self._summarizer = request.get_batch_run_summarizer()

                logger.info("Orchestrator: Starting batch {0}".format(
                    request.batch_id))
                self._status_provider.change_status_enum(
                    request.batch_id, BatchStatusEnum.running)
                self._on_batch_id = request.batch_id
                self._batch_completion_evt.clear()
                self._run_summary_thread_gate.set()
                assert len(self._in_progress) == 0
                assert self._file_queue_size == 0

                for work in request.make_work_items(
                        self._status_provider.batch_base_path(
                            request.batch_id), self._cache_search_dirs,
                        self._log_folder):
                    self._file_queue.put(work)
                    self._file_queue_size += 1
                self._file_queue_cond.notify_all()

            # Wait for batch completion or early stop request. In both cases,
            # nothing is in progress and nothing is in queue when we're woken.
            self._batch_completion_evt.wait()
            logger.info("Orchestrator: Completed batch {0}".format(
                request.batch_id))

            # Report per-batch final run_summary.
            if self._singleton_run_summary_path is None:
                self.write_summary_information(write_run_summary=True,
                                               log_conclusion_msg=True,
                                               allow_fail=True)
            # Even with singleton run_summary, we should update run_summary file
            # now but not log conclusion.
            else:
                self.write_summary_information(write_run_summary=True,
                                               log_conclusion_msg=False,
                                               allow_fail=True)

            # Concatenate batch-level results to single file.
            if request.combine_results:
                write_single_output_json(
                    request.files,
                    self._status_provider.batch_base_path(request.batch_id))

            # Intentionally change status enum last so that above results committed first
            # for any event-driven observers.
            self._status_provider.change_status_enum(request.batch_id,
                                                     BatchStatusEnum.done)
            logger.info(
                "Orchestrator: Updated batch status to Done: {0}".format(
                    request.batch_id))

            # As another batch may not show up for a while (or never), stop the periodic
            # run summary thread since no new information to report.
            self._run_summary_thread_gate.clear()
class SharedStorage(object):
    """
    Uložiště synchronizované mezi procesy.
    """
    class StorageType(Enum):
        LIST = "list"  #SharedStorageList - wrapped manager.list()
        DICT = "DICT"  #SharedStorageDict - wrapped manager.dict()
        DICT_SIMPLE = "DICT_SIMPLE"  #manager.dict() Pokud nepotřebujeme pracovat s velkým objemem dat (nad 2GB), tak je vhodnější.

    def __init__(self, storageType, manager=None):
        """
        Inicializace uložiště.
        :type storageType: StorageType
        :param storageType: Druh uložiště. Všechny podporované druhy vymezuje StorageType.
        :param manager: Volitelný parametr. Pokud chceme vnutit použití jiného multiprocessing.Manager.
        """
        if manager is None:
            manager = Manager()
        # Type checking
        if not isinstance(storageType, self.StorageType):
            raise TypeError('storageType musí být instancí StorageType')

        #Zde budou ukládány data,
        if storageType == self.StorageType.LIST:
            self._storage = SharedList(manager)
        elif storageType == self.StorageType.DICT:
            self._storage = SharedDict(manager)
        elif storageType == self.StorageType.DICT_SIMPLE:
            self._storage = manager.dict()
        else:
            raise ValueError('Neznámý druh uložiště (storageType).')

        self.__usedManager = manager
        #Sdílený zámek pro synchronizaci procesů
        self.__sharedLock = Lock()

        #počet uložených klasifikátorů
        self._numOfData = Value(c_ulong, 0)

        self.__waitForChange = Condition()

        self.acquiredStorage = False

    def __len__(self):
        """
        Zjištení počtu uložených dat.
        
        :return: Počet všech uložených dat.
        :rtype: int
        """
        return self._numOfData.value

    def _notifyChange(self):
        """
        Oznámí, že došlo ke změně připadným čekajícím.
        """
        self.__waitForChange.acquire()
        self.__waitForChange.notify_all()
        self.__waitForChange.release()

    def waitForChange(self, timeout=None):
        """
        Uspí proces dokud nenastane změna. Pokud měl proces přivlastněné uložiště, tak je uvolní
        a po probuzení zase přivlastní.
        
        :param timeout: Maximální počet sekund, které bude čekat. Může být None, pak čeká dokud nepřijde událost.
        """

        wasAcquiredBeforeSleeping = False

        if self.acquiredStorage:
            self.release()
            wasAcquiredBeforeSleeping = True

        self.__waitForChange.acquire()

        self.__waitForChange.wait(timeout)

        self.__waitForChange.release()

        if wasAcquiredBeforeSleeping:
            self.acquire()

    def acquire(self):
        """
        Přivlastní si uložiště pro sebe. Ostatní procesy musí čekat.
        """
        self.__sharedLock.acquire()
        self.acquiredStorage = True

    def release(self):
        """
        Uvolní uložiště pro ostatní procesy.
        """
        self.__sharedLock.release()
        self.acquiredStorage = False

    def _safeAcquire(self):
        """
        Přivlastnění si uložiště. V momentu, kdy chci měnit jeho stav.
        Zohledňuje případ, kdy je uložiště zamluveno pomocí acquire.
        """
        if not self.acquiredStorage:
            self.__sharedLock.acquire()

    def _safeRelease(self):
        """
        Uvolnění přístupu k uložišti.
        Zohledňuje případ, kdy je uložiště zamluveno pomocí acquire.
        """
        if not self.acquiredStorage:
            self.__sharedLock.release()
Exemple #27
0
class DBPipeline(object):
    """
    Like L{Exscript.workqueue.Pipeline}, but keeps all queued objects
    in a database, instead of using in-memory data structures.
    """
    def __init__(self, engine, max_working=1):
        self.condition = Condition(RLock())
        self.engine = engine
        self.max_working = max_working
        self.running = False
        self.paused = False
        self.metadata = sa.MetaData(self.engine)
        self._table_prefix = 'exscript_pipeline_'
        self._table_map = {}
        self.__update_table_names()
        self.clear()

    def __add_table(self, table):
        """
        Adds a new table to the internal table list.
        
        @type  table: Table
        @param table: An sqlalchemy table.
        """
        pfx = self._table_prefix
        self._table_map[table.name[len(pfx):]] = table

    def __update_table_names(self):
        """
        Adds all tables to the internal table list.
        """
        pfx = self._table_prefix
        self.__add_table(
            sa.Table(pfx + 'job',
                     self.metadata,
                     sa.Column('id', sa.Integer, primary_key=True),
                     sa.Column('name', sa.String(150), index=True),
                     sa.Column('status', sa.String(50), index=True),
                     sa.Column('job', sa.PickleType()),
                     mysql_engine='INNODB'))

    @synchronized
    def install(self):
        """
        Installs (or upgrades) database tables.
        """
        self.metadata.create_all()

    @synchronized
    def uninstall(self):
        """
        Drops all tables from the database. Use with care.
        """
        self.metadata.drop_all()

    @synchronized
    def clear_database(self):
        """
        Drops the content of any database table used by this library.
        Use with care.

        Wipes out everything, including types, actions, resources and acls.
        """
        delete = self._table_map['job'].delete()
        delete.execute()

    def debug(self, debug=True):
        """
        Enable/disable debugging.

        @type  debug: bool
        @param debug: True to enable debugging.
        """
        self.engine.echo = debug

    def set_table_prefix(self, prefix):
        """
        Define a string that is prefixed to all table names in the database.

        @type  prefix: string
        @param prefix: The new prefix.
        """
        self._table_prefix = prefix
        self.__update_table_names()

    def get_table_prefix(self):
        """
        Returns the current database table prefix.
        
        @rtype:  string
        @return: The current prefix.
        """
        return self._table_prefix

    def __len__(self):
        return self._table_map['job'].count().execute().fetchone()[0]

    def __contains__(self, item):
        return self.has_id(id(item))

    def get_from_name(self, name):
        """
        Returns the item with the given name, or None if no such item
        is known.
        """
        with self.condition:
            tbl_j = self._table_map['job']
            query = tbl_j.select(tbl_j.c.name == name)
            row = query.execute().fetchone()
            if row is None:
                return None
            return row.job

    def has_id(self, item_id):
        """
        Returns True if the queue contains an item with the given id.
        """
        tbl_j = self._table_map['job']
        query = tbl_j.select(tbl_j.c.id == item_id).count()
        return query.execute().fetchone()[0] > 0

    def task_done(self, item):
        with self.condition:
            self.working.remove(item)
            self.all.remove(id(item))
            self.condition.notify_all()

    def append(self, item):
        with self.condition:
            self.queue.append(item)
            self.all.add(id(item))
            self.condition.notify_all()

    def appendleft(self, item, force=False):
        with self.condition:
            if force:
                self.force.append(item)
            else:
                self.queue.appendleft(item)
            self.all.add(id(item))
            self.condition.notify_all()

    def prioritize(self, item, force=False):
        """
        Moves the item to the very left of the queue.
        """
        with self.condition:
            # If the job is already running (or about to be forced),
            # there is nothing to be done.
            if item in self.working or item in self.force:
                return
            self.queue.remove(item)
            self.appendleft(item, force)
            self.condition.notify_all()

    def clear(self):
        with self.condition:
            self.queue = deque()
            self.force = deque()
            self.sleeping = set()
            self.working = set()
            self.all = set()
            self.condition.notify_all()

    def stop(self):
        """
        Force the next() method to return while in another thread.
        The return value of next() will be None.
        """
        with self.condition:
            self.running = False
            self.condition.notify_all()

    def pause(self):
        with self.condition:
            self.paused = True
            self.condition.notify_all()

    def unpause(self):
        with self.condition:
            self.paused = False
            self.condition.notify_all()

    def sleep(self, item):
        assert id(item) in self.all
        with self.condition:
            self.sleeping.add(item)
            self.condition.notify_all()

    def wake(self, item):
        assert id(item) in self.all
        assert item in self.sleeping
        with self.condition:
            self.sleeping.remove(item)
            self.condition.notify_all()

    def wait_for_id(self, item_id):
        with self.condition:
            while self.has_id(item_id):
                self.condition.wait()

    def wait(self):
        """
        Waits for all currently running tasks to complete.
        """
        with self.condition:
            while self.working:
                self.condition.wait()

    def wait_all(self):
        """
        Waits for all queued and running tasks to complete.
        """
        with self.condition:
            while len(self) > 0:
                self.condition.wait()

    def with_lock(self, function, *args, **kwargs):
        with self.condition:
            return function(self, *args, **kwargs)

    def set_max_working(self, max_working):
        with self.condition:
            self.max_working = int(max_working)
            self.condition.notify_all()

    def get_max_working(self):
        return self.max_working

    def get_working(self):
        return list(self.working)

    def _popleft_sleeping(self):
        sleeping = []
        while True:
            try:
                node = self.queue[0]
            except IndexError:
                break
            if node not in self.sleeping:
                break
            sleeping.append(node)
            self.queue.popleft()
        return sleeping

    def _get_next(self):
        # We need to leave sleeping items in the queue because else we
        # would not know their original position after they wake up.
        # So we need to temporarily remove sleeping items from the top of
        # the queue here.
        sleeping = self._popleft_sleeping()

        # Get the first non-sleeping item from the queue.
        try:
            next = self.queue.popleft()
        except IndexError:
            next = None

        # Re-insert sleeping items.
        self.queue.extendleft(sleeping)
        return next

    def next(self):
        with self.condition:
            self.running = True
            while self.running:
                if self.paused:
                    self.condition.wait()
                    continue

                # Wait until enough slots are available.
                if len(self.working) - \
                   len(self.sleeping) - \
                   len(self.force) >= self.max_working:
                    self.condition.wait()
                    continue

                # Forced items are returned regardless of how many tasks
                # are already working.
                try:
                    next = self.force.popleft()
                except IndexError:
                    pass
                else:
                    self.working.add(next)
                    return next

                # Return the first non-sleeping task.
                next = self._get_next()
                if next is None:
                    self.condition.wait()
                    continue
                self.working.add(next)
                return next
Exemple #28
0
def run(
    detect_input: Array,
    new_detect_input: Condition,
    frame: Array,
    preview: Array,
    new_preview: Condition,
    target_offset_x: Value,
    target_offset_y: Value,
    track: Sequence[str],
    target_in_sight: Condition,
):
    interp = interpreter.Interpreter(
        model_path=path.abspath(
            path.join(
                DATA_DIR,
                "model_postprocessed_quantized_128_uint8_edgetpu.tflite")),
        experimental_delegates=[
            tensorflow.lite.experimental.load_delegate("libedgetpu.so.1")
        ],
    )

    interp.allocate_tensors()
    input_details = interp.get_input_details()
    output_details = interp.get_output_details()
    label_idxs = label_to_category_index(track)

    while True:
        with new_detect_input:
            new_detect_input.wait()

        with detect_input.get_lock():
            input = numpy.asarray(detect_input.get_obj()).reshape(
                (RESOLUTION[1], RESOLUTION[0], 3))
            input_tensor = tensorflow.convert_to_tensor(input,
                                                        dtype=tensorflow.uint8)
            input_tensor = input_tensor[tensorflow.newaxis, ...]

            interp.set_tensor(input_details[0]["index"], input_tensor)

        interp.invoke()
        box_data = tensorflow.convert_to_tensor(
            interp.get_tensor(output_details[0]["index"]))
        class_data = tensorflow.convert_to_tensor(
            interp.get_tensor(output_details[1]["index"]))
        score_data = tensorflow.convert_to_tensor(
            interp.get_tensor(output_details[2]["index"]))
        # num_detections = tensorflow.convert_to_tensor(
        #    interp.get_tensor(output_details[3]["index"])
        # )

        class_data = (tensorflow.squeeze(class_data, axis=[0]).numpy().astype(
            numpy.int64) + 1)
        box_data = tensorflow.squeeze(box_data, axis=[0]).numpy()
        score_data = tensorflow.squeeze(score_data, axis=[0]).numpy()

        if any(item in label_idxs for item in class_data):
            tracked = ((i, x) for i, x in enumerate(class_data)
                       if x in label_idxs)
            tracked_idxs, tracked_classes = zip(*tracked)

            if any(score_data[i] > 0.6 for i in tracked_idxs):
                with target_in_sight:
                    target_in_sight.notify_all()

            idx = max(
                tracked_idxs,
                key=lambda i: numpy.amax(box_data[i]) - numpy.amin(box_data[i]
                                                                   ),
            )

            keypoint_x = numpy.take(box_data[idx], [1, 3]).mean()
            keypoint_y = numpy.take(box_data[idx], [0, 2]).mean()

            target_offset_x.value = (keypoint_x - 0.5) * FOV_ANGLE
            target_offset_y.value = (keypoint_y - 0.5) * FOV_ANGLE
        else:
            keypoint_x = None
            keypoint_y = None

            target_offset_x.value = float("nan")
            target_offset_y.value = float("nan")

        with preview.get_lock():
            preview_image = numpy.frombuffer(preview.get_obj(),
                                             dtype=numpy.uint8).reshape(
                                                 (camera.RESOLUTION[1],
                                                  camera.RESOLUTION[0], 3))
            with frame.get_lock():
                frame_image = numpy.frombuffer(frame.get_obj(),
                                               dtype=numpy.uint8).reshape(
                                                   (camera.RESOLUTION[1],
                                                    camera.RESOLUTION[0], 3))
                numpy.copyto(preview_image, frame_image)

            if keypoint_x is not None and keypoint_y is not None:
                draw_keypoints_on_image_array(
                    preview_image,
                    numpy.array([[keypoint_y, keypoint_x]]),
                    use_normalized_coordinates=True,
                )

            visualize_boxes_and_labels_on_image_array(
                preview_image,
                box_data,
                class_data,
                score_data,
                LABEL_MAP,
                use_normalized_coordinates=True,
                line_thickness=4,
                min_score_thresh=0.5,
                max_boxes_to_draw=3,
            )
        with new_preview:
            new_preview.notify_all()
class ExternalPlayer(GreedyPlayer):
    """
    The RL player in the gym environment wants to initiate control by
        invoking the step() function. This step function sends an action, lets the environment simulate and then
        receives an observation back from the environment.
    In this schieber environment the control is initiated by the Game and not by the player. This is why we need this
        architecture with this external player. The external player blocks when its choose_card() method is called and
        sends the current state received by the Game as an observation to the rl player from gym who connects via a
        websocket. Then the rl agent selects an action and sends it back to this external player. The external player
        submits this action as the chosen card to the Game. The Game simulates the game and this process starts over.
        With the help of this architecture we can use the benefits of the standardized gym environments with many
        rl methods which are already implemented (openai baselines: https://github.com/openai/baselines).
    """
    def __init__(self, name='unknown', seed=None, trumps='all'):
        super().__init__(name, seed, trumps)
        self.action_received = Condition()
        self.observation_received = Condition()

        self.action = {}
        self.observation = {}

    def choose_card(self, state=None):
        """
        Chooses the card and verifies if the chosen card is allowed to be played in the current game state.
        :param state:
        :return:
        """
        # if self.at_last_stich():
        #    allowed = yield self.cards[0]
        # else:
        self.observation_received.acquire()
        self.observation = self.build_observation(state, self.cards)
        logger.debug(f"choose_card received observation: {self.observation}")
        self.observation_received.notify_all()  # notify all threads to be sure
        self.observation_received.release()

        self.action_received.acquire()
        received = self.action_received.wait()
        if not received:
            logger.debug(
                "Timeout occurred. action_received condition has not been notified."
            )
        logger.debug(f"choose_card received action: {self.action}")
        allowed_cards = self.allowed_cards(state=state)
        chosen_card = allowed_cards[
            0]  # set chosen_card to the first allowed card in case anything goes south
        chosen_card = self.set_chosen_card(allowed_cards, chosen_card)
        self.action_received.release()

        allowed = yield chosen_card

        if allowed:
            yield None

    @staticmethod
    def build_observation(state, cards):
        observation = state
        observation["cards"] = cards
        return observation

    def set_chosen_card(self, allowed_cards, chosen_card):
        """
        Sets the chosen card based on the action of the RL player.
        :param allowed_cards:
        :param chosen_card:
        :return:
        """
        if self.action is not None:
            if self.action in allowed_cards:
                logger.info(f"Successfully chose the card: {self.action}")
                chosen_card = self.action
            else:
                logger.error(
                    f"{self.action} is not a valid card! Choosing the first allowed card now."
                )
        else:
            logger.debug("chosen card is None")
        return chosen_card

    def get_observation(self):
        """
        Gets the observation obtained by the game
        :return:
        """
        self.observation_received.acquire()
        received = self.observation_received.wait(0.01)
        if not received:
            print(
                "Timeout occurred. observation_received condition has not been notified."
            )
        observation = self.observation
        logger.debug(f"get_observation {observation}")
        self.observation_received.release()
        return observation

    def set_action(self, action):
        """
        Sets the action chosen by the RL player
        :param action:
        :return:
        """
        if self.hand_empty():
            logger.error(
                "set_action: There are no cards on my hand, so I cannot choose any card!"
            )
        self.action_received.acquire()
        self.action = action
        logger.debug(f"set_action: {self.action}")
        self.action_received.notify_all()  # notify all threads to be sure
        self.action_received.release()

    def before_first_stich(self):
        """
        Checks if the player has already played any cards in this game
        :return:
        """
        return len(self.cards) == 9

    def at_last_stich(self):
        """
        Checks if the player is at the last stich where there is no choice anymore
        :return:
        """
        return len(self.cards) == 1

    def hand_empty(self):
        """
        Checks if the hand is empty or if there are any cards left.
        :return:
        """
        return len(self.cards) == 0
Exemple #30
0
    cond = Condition()
    try:
        for x in range(0, threads):
            p = Process(target=worker, args=(x, v, cond))
            jobs.append(p)
            p.start()

        for i in range(0, 10000):

            while v.value < (threads):
                #print "M %d sync %d" % (v.value,threads)
                time.sleep(0.01)

            print "M %d sync done %d" % (v.value, threads)
            cond.acquire()
            cond.notify_all()
            cond.release()
            with v.get_lock():
                v.value = 0

        for j in jobs:
            j.join()

    except Exception as error:
        sys.exit(1)

        for j in jobs:
            j.join()
    except Exception as error:
        sys.exit(1)
Exemple #31
0
class DownloadManager(object):
    def __init__(self):
        self._started = False
        self.server_thread = None
        self.download_threads = {}
        self.uuid_state_dict = None
        self.uuid_map_dict = None
        self.guide_addr = None
        self.server_addr = None
        self.host = None
        self.ctx = None
        self.random_inst = None
        self.master_broadcast_blocks = {}

    def start(self):
        if self._started:
            return

        self.manager = manager = Manager()
        self.shared_uuid_fn_dict = manager.dict()
        self.shared_uuid_map_dict = manager.dict()
        self.shared_master_blocks = manager.dict()
        self.download_cond = Condition()

        self._started = True
        self.ctx = zmq.Context()
        self.host = socket.gethostname()
        if GUIDE_ADDR not in env.environ:
            start_guide_manager()

        self.guide_addr = env.get(GUIDE_ADDR)
        self.random_inst = random.SystemRandom()
        self.server_addr, self.server_thread = self.start_server()
        self.uuid_state_dict = {}
        self.uuid_map_dict = {}
        self.master_broadcast_blocks = {}
        env.register(DOWNLOAD_ADDR, self.server_addr)

    def start_server(self):
        sock = self.ctx.socket(zmq.REP)
        sock.setsockopt(zmq.LINGER, 0)
        port = sock.bind_to_random_port("tcp://0.0.0.0")
        server_addr = 'tcp://%s:%d' % (self.host, port)
        guide_sock = self.ctx.socket(zmq.REQ)
        guide_sock.setsockopt(zmq.LINGER, 0)
        guide_sock.connect(self.guide_addr)

        def run():
            logger.debug("server started at %s", server_addr)

            while self._started:
                if not sock.poll(1000, zmq.POLLIN):
                    continue
                type_, msg = sock.recv_pyobj()
                logger.debug('server recv: %s %s', type_, msg)
                if type_ == SERVER_STOP:
                    sock.send_pyobj(None)
                    break
                elif type_ == SERVER_FETCH:
                    uuid, indices, client_addr = msg
                    if uuid in self.master_broadcast_blocks:
                        block_num = len(self.master_broadcast_blocks[uuid])
                        bls = []
                        for index in indices:
                            if index >= block_num:
                                logger.warning('input index too big %s for '
                                               'len of blocks  %d from host %s',
                                               str(indices), block_num, client_addr)
                                sock.send_pyobj((SERVER_FETCH_FAIL, None))
                            else:
                                bls.append(self.master_broadcast_blocks[uuid][index])
                        sock.send_pyobj((SERVER_FETCH_OK, (indices, bls)))
                    elif uuid in self.uuid_state_dict:
                        fd = os.open(self.uuid_state_dict[uuid][0], os.O_RDONLY)
                        mmfp = mmap.mmap(fd, 0, access=ACCESS_READ)
                        os.close(fd)
                        bitmap = self.uuid_map_dict[uuid]
                        block_num = len(bitmap)
                        bls = []
                        for index in indices:
                            if index >= block_num:
                                logger.warning('input index too big %s for '
                                               'len of blocks  %d from host %s',
                                               str(indices), block_num, client_addr)
                                sock.send_pyobj((SERVER_FETCH_FAIL, None))
                            else:
                                mmfp.seek(bitmap[index][0])
                                block = mmfp.read(bitmap[index][1])
                                bls.append(block)
                        mmfp.close()
                        sock.send_pyobj((SERVER_FETCH_OK, (indices, bls)))
                    else:
                        logger.warning('server fetch failed for uuid %s '
                                       'not exists in server %s from host %s',
                                       uuid, socket.gethostname(), client_addr)
                        sock.send_pyobj((SERVER_FETCH_FAIL, None))
                elif type_ == DATA_GET:
                    uuid, compressed_size = msg
                    if uuid not in self.uuid_state_dict or not self.uuid_state_dict[uuid][1]:
                        if uuid not in self.download_threads:
                            sources = self._get_sources(uuid, guide_sock)
                            if not sources:
                                logger.warning('get sources from guide server failed in host %s',
                                               socket.gethostname())
                                sock.send_pyobj(DATA_GET_FAIL)
                                continue
                            self.download_threads[uuid] = spawn(self._download_blocks,
                                                                *[sources, uuid, compressed_size])
                            sock.send_pyobj(DATA_DOWNLOADING)
                        else:
                            sock.send_pyobj(DATA_DOWNLOADING)
                    else:
                        sock.send_pyobj(DATA_GET_OK)
                elif type_ == SERVER_CLEAR_ITEM:
                    uuid = msg
                    self.clear(uuid)
                    sock.send_pyobj(None)
                else:
                    logger.error('Unknown server message: %s %s', type_, msg)
                    sock.send_pyobj(None)

            sock.close()
            logger.debug("stop Broadcast server %s", server_addr)
            for uuid in list(self.uuid_state_dict.keys()):
                self.clear(uuid)

        return server_addr, spawn(run)

    def get_blocks(self, uuid):
        if uuid in self.master_broadcast_blocks:
            return self.master_broadcast_blocks[uuid]
        if uuid in self.shared_master_blocks:
            return self.shared_master_blocks[uuid]

    def register_blocks(self, uuid, blocks):
        if uuid in self.master_broadcast_blocks:
            logger.warning('the block uuid %s exists in dict', uuid)
            return
        self.master_broadcast_blocks[uuid] = blocks
        self.shared_master_blocks[uuid] = blocks

    def _get_sources(self, uuid, source_sock):
        try:
            source_sock.send_pyobj((GUIDE_GET_SOURCES,
                                    uuid))
            sources = source_sock.recv_pyobj()
        except:
            logger.warning('GET sources failed for addr %s with ZMQ ERR',
                           self.server_addr)
            sources = {}
        return sources

    def _update_sources(self, uuid, bitmap, source_sock):
        try:
            source_sock.send_pyobj((GUIDE_SET_SOURCES,
                                    (uuid, self.server_addr, bitmap)))
            source_sock.recv_pyobj()
        except:
            pass

    def _download_blocks(self, sources, uuid, compressed_size):
        block_num = 0
        bitmap = [0]
        write_mmap_handler = None
        download_guide_sock = self.ctx.socket(zmq.REQ)
        download_guide_sock.setsockopt(zmq.LINGER, 0)
        download_guide_sock.connect(self.guide_addr)

        def _report_bad(addr):
            logger.debug('fetch blocks failed from server %s', addr)
            download_guide_sock.send_pyobj((GUIDE_REPORT_BAD, (uuid, addr)))
            download_guide_sock.recv_pyobj()

        def _fetch(addr, indices, bit_map):
            sock = self.ctx.socket(zmq.REQ)
            try:
                sock.setsockopt(zmq.LINGER, 0)
                sock.connect(addr)
                sock.send_pyobj((SERVER_FETCH, (uuid, indices, self.server_addr)))
                avail = sock.poll(1 * 1000, zmq.POLLIN)
                check_sock = None
                if not avail:
                    try:
                        check_sock = socket.socket()
                        addr_list = addr[len('tcp://'):].split(':')
                        addr_list[1] = int(addr_list[1])
                        check_sock.connect(tuple(addr_list))
                    except Exception as e:
                        logger.warning('connect the addr %s failed with exception %s',
                                       addr, e)
                        _report_bad(addr)
                    else:
                        logger.debug("%s recv broadcast %s from %s timeout",
                                     self.server_addr, str(indices), addr)
                    finally:
                        if check_sock:
                            check_sock.close()
                    return
                result, msg = sock.recv_pyobj()
                if result == SERVER_FETCH_FAIL:
                    _report_bad(addr)
                    return
                if result == SERVER_FETCH_OK:
                    indices, blocks = msg
                    for rank, index in enumerate(indices):
                        if blocks[rank] is not None:
                            write_mmap_handler.seek(bit_map[index][0])
                            write_mmap_handler.write(blocks[rank])
                            bitmap[index] = bit_map[index]
                else:
                    raise RuntimeError('Unknown server response: %s %s' % (result, msg))
            finally:
                sock.close()

        final_path = env.workdir.alloc_tmp_file("broadcast")
        self.uuid_state_dict[uuid] = (final_path, False)
        fp = open(final_path, 'wb')
        fp.truncate(compressed_size)
        fp.close()
        fd = os.open(final_path, os.O_RDWR)
        write_mmap_handler = mmap.mmap(fd, 0,
                                       access=ACCESS_WRITE)
        os.close(fd)
        while not all(bitmap):
            remote = []
            for _addr, _bitmap in six.iteritems(sources):
                if block_num == 0:
                    block_num = len(_bitmap)
                    bitmap = [0] * block_num
                    self.uuid_map_dict[uuid] = bitmap
                if not _addr.startswith('tcp://%s:' % self.host):
                    remote.append((_addr, _bitmap))
            self.random_inst.shuffle(remote)
            for _addr, _bitmap in remote:
                _indices = [i for i in range(block_num) if not bitmap[i] and _bitmap[i]]
                if _indices:
                    self.random_inst.shuffle(_indices)
                    _fetch(_addr, _indices[:BATCHED_BLOCKS], _bitmap)
                    self._update_sources(uuid, bitmap, download_guide_sock)
            sources = self._get_sources(uuid, download_guide_sock)
        write_mmap_handler.flush()
        write_mmap_handler.close()
        self.shared_uuid_map_dict[uuid] = bitmap
        self.shared_uuid_fn_dict[uuid] = self.uuid_state_dict[uuid][0]
        self.uuid_state_dict[uuid] = self.uuid_state_dict[uuid][0], True
        download_guide_sock.close()
        with self.download_cond:
            self.download_cond.notify_all()

    def clear(self, uuid):
        if uuid in self.master_broadcast_blocks:
            del self.master_broadcast_blocks[uuid]
            del self.shared_master_blocks[uuid]
        if uuid in self.uuid_state_dict:
            del self.uuid_state_dict[uuid]
        if uuid in self.shared_uuid_fn_dict:
            del self.shared_uuid_fn_dict[uuid]
            del self.shared_uuid_map_dict[uuid]

    def shutdown(self):
        if not self._started:
            return

        self._started = False
        if self.server_thread and self.server_addr. \
                startswith('tcp://%s:' % socket.gethostname()):
            for _, th in six.iteritems(self.download_threads):
                th.join(timeout=0.1)  # only in executor, not needed
            self.server_thread.join(timeout=1)
            if self.server_thread.is_alive():
                logger.warning("Download mananger server_thread not stopped.")

        self.manager.shutdown()  # shutdown will try join and terminate server process
def solve(max_level, goal, num_workers):
    # prepare message queue shared with workers
    tasks = Queue()
    task_lock = Lock()
    task_cv = Condition(lock=task_lock)

    # create and start workers
    workers = []
    for i in range(0, num_workers):
        solutions = set()
        parent_conn, child_connn = Pipe()
        worker = Process(target=run_worker,
                         args=(child_connn, goal, max_level, tasks, task_lock,
                               task_cv))
        worker.start()
        workers.append((worker, parent_conn))

    # Find all possible sequences: [n0, n1, n2, ..., nM] (M=max_level)
    # where nX is the number of binary operators so that
    # '1 <n0 ops> 2 <n1 ops> 3 <n2 ops> ... M+1 <nM ops>' can be a valid
    # Reverse Polish Notation.  Key conditions are:
    # 1. n0 + n1 + ... + nM = M
    # 2. for any X, n0 + n1 + ... + nX <= X
    # (Note that from condition #2 n0 is always 0.)
    # We'll build the sequences in 'numops_list' below while exploring cases
    # in a BFS-like (or DP-like) manner.

    # This is a queue to maintain outstanding search results.  Its each element
    # is a tuple of 2 items: 'numops_list', 'total_ops'
    # A tuple of (N, T) means:
    # - N = [n0, n1, ..., nX]
    # - T = sum(N)
    # (Note that we don't necessarily have to keep T as it can be derived
    # from N.  But we do this for efficiency).
    # The search is completed when len(N) reaches M (i.e., X=M-1) by appending
    # the last item of nM = M - (n0 + n1 + ... + nX) = M - T (see condition #1).
    tmp = [([0], 0)]

    while tmp:
        numops_list, total_ops = tmp.pop(0)
        level = len(numops_list)
        if level < max_level:
            # Expand the sequence with all possible numbers of operators at
            # the current level so we can explore the next level for each of
            # them.
            for i in range(0, level - total_ops + 1):  # see condition #2
                tmp.append((numops_list + [i], total_ops + i))
        else:
            # Found one valid RPN template.  Pass it to workers and have them
            # work on it.
            numops_list.append(max_level - total_ops)
            with task_lock:
                tasks.put(numops_list)
                task_cv.notify()

    # Tell workers all data have been passed.
    solutions = set()
    with task_lock:
        for _ in workers:
            tasks.put(None)
        task_cv.notify_all()

    # Wait until all workers complete the tasks, while receiving any
    # intermediate and last solutions.  The received solutions may not
    # necessarily be fully unique, so we have to unify them here, again.
    # Received data of 'None' means the corresponding worker has completed
    # its task.
    # Note: here we assume all workers are reasonably equally active in
    # sending data, so we simply perform blocking receive.
    conns = set([w[1] for w in workers])
    while conns:
        for c in conns.copy():
            worker_data = c.recv()
            if worker_data is None:
                conns.remove(c)
                continue
            for solution in worker_data:
                if solution not in solutions:
                    solutions.add(solution)

    # All workers have completed.  Cleanup them and print the final unified
    # results.  If we are to show all expressions (i.e. goal is None), sort
    # results by the expressions' values (listing integers followed by all
    # non-integers, followed by 'divided by 0' cases.
    for w in workers:
        w[0].join()
    if goal is None:
        l = list(solutions)
        l.sort(
            key=lambda x: (0, x[0]) if type(x[0]) == int else (1, str(x[0])))
        for solution in l:
            print('%s = %s' % (solution[1], str(solution[0])))
    else:
        for solution in solutions:
            print(solution)
Exemple #33
0
class CountBucket(Query):
    """
    Class for registering callbacks on counts of packets sent to
    the controller.
    """
    def __init__(self):
        super(CountBucket, self).__init__()
        self.matches = set([])
        self.runtime_stats_query_fun = None
        self.outstanding_switches = []
        self.packet_count = 0
        self.byte_count = 0
        self.packet_count_persistent = 0
        self.byte_count_persistent = 0
        self.in_update_cv = Condition()
        self.in_update = False
        self._classifier = self.generate_classifier()
        
    def __repr__(self):
        return "CountBucket"

    def eval(self, pkt):
        """
        evaluate this policy on a single packet

        :param pkt: the packet on which to be evaluated
        :type pkt: Packet
        :rtype: set Packet
        """
        return set()

    def generate_classifier(self):
        return Classifier([Rule(identity,{self})])

    def apply(self):
        with self.bucket_lock:
            for pkt in self.bucket:
                self.packet_count_persistent += 1
                self.byte_count_persistent += pkt['header_len'] + pkt['payload_len']
            self.bucket.clear()

    def start_update(self):
        """
        Use a condition variable to mediate access to bucket state as it is
        being updated.

        Why condition variables and not locks? The main reason is that the state
        update doesn't happen in just a single function call here, since the
        runtime processes the classifier rule by rule and buckets may be touched
        in arbitrary order depending on the policy. They're not all updated in a
        single function call. In that case,

        (1) Holding locks *across* function calls seems dangerous and
        non-modular (in my opinion), since we need to be aware of this across a
        large function, and acquiring locks in different orders at different
        points in the code can result in tricky deadlocks (there is another lock
        involved in protecting bucket updates in runtime).

        (2) The "with" semantics in python is clean, and splitting that into
        lock.acquire() and lock.release() calls results in possibly replicated
        failure handling code that is boilerplate.

        """
        with self.in_update_cv:
            self.in_update = True
            self.matches = set([])
            self.runtime_stats_query_fun = None
            self.outstanding_switches = []

    def finish_update(self):
        with self.in_update_cv:
            self.in_update = False
            self.in_update_cv.notify_all()
        
    def add_match(self, m):
        """
        Add a match m to list of classifier rules to be queried for
        counts.
        """
        if not m in self.matches:
            self.matches.add(m)

    def add_pull_stats(self, fun):
        """
        Point to function that issues stats queries in the
        runtime.
        """
        if not self.runtime_stats_query_fun:
            self.runtime_stats_query_fun = fun

    def pull_stats(self):
        """Issue stats queries from the runtime"""
        queries_issued = False
        with self.in_update_cv:
            while self.in_update: # ensure buckets not updated concurrently
                self.in_update_cv.wait()
            if not self.runtime_stats_query_fun is None:
                self.outstanding_switches = []
                queries_issued = True
                self.runtime_stats_query_fun()
        # If no queries were issued, then no matches, so just call userland
        # registered callback routines
        if not queries_issued:
            self.packet_count = self.packet_count_persistent
            self.byte_count = self.byte_count_persistent
            for f in self.callbacks:
                f([self.packet_count, self.byte_count])

    def add_outstanding_switch_query(self,switch):
        self.outstanding_switches.append(switch)

    def handle_flow_stats_reply(self,switch,flow_stats):
        """
        Given a flow_stats_reply from switch s, collect only those
        counts which are relevant to this bucket.

        Very simple processing for now: just collect all packet and
        byte counts from rules that have a match that is in the set of
        matches this bucket is interested in.
        """
        def stat_in_bucket(flow_stat, s):
            table_match = match(f['match']).intersect(match(switch=s))
            network_match = match(f['match'])
            if table_match in self.matches or network_match in self.matches:
                return True
            return False

        with self.in_update_cv:
            while self.in_update:
                self.in_update_cv.wait()
            self.packet_count = self.packet_count_persistent
            self.byte_count = self.byte_count_persistent
            if switch in self.outstanding_switches:
                for f in flow_stats:
                    if 'match' in f:
                        if stat_in_bucket(f, switch):
                            self.packet_count += f['packet_count']
                            self.byte_count   += f['byte_count']
                self.outstanding_switches.remove(switch)
        # If have all necessary data, call user-land registered callbacks
        if not self.outstanding_switches:
            for f in self.callbacks:
                f([self.packet_count, self.byte_count])

    def __eq__(self, other):
        # TODO: if buckets eventually have names, equality should
        # be on names.
        return isinstance(other, CountBucket)
class SharedDocumentCache(object):
    """
    Slouží jako sdílená cache dokumentů mezi procesy.
    Zabraňuje vypisování dokumentů, které nejsou v pořadí.
    Do cache není nutné ukládat prázdné dokumenty.
    
    Pokud například řeknu pomocí metody waitFor, že čekám na dokument s číslem 20 a 40.
    Tedy:
    waitFor(20)    #Tato metoda ukládá čísla dokumentů do listu
    waitFor(40)
    
    Tak v momentě, kdy se objeví v cache dokument s číslem 20, jsou vypsány i prázdné dokumenty
    před dokumentem 20 (počáteční dokument má číslo 0). Dále pokud se objeví v cache dokument 40. Vypíší se
    i prázdné dokumenty mezi, tedy 21-39.
    """

    #CONDITION_WAIT_TIME_OUT=1.0   #maximální počet sekund, při čekání na podmínku

    def __init__(self, manager=None):
        """
        Inicializace cache.
        
        :param manager: Volitelný parametr. Pokud chceme vnutit použití jiného multiprocessing.Manager.
        """
        if manager is None:
            manager = Manager()
        self.__docCache = manager.list()  #Zde budeme ukladat dokumenty.
        self.__sharedLock = Lock()
        self.__shouldWrite = Value(ctypes.c_ulonglong, 0)
        self.__numOfWaitingForWrite = Value(
            ctypes.c_uint, 0)  #počet čekajicích procesů na výpis

        #List neprázdných dokumentů, na které čekáme.
        #Díky tomuto listu nemusíme do cache ukládat prázdné dokumenty.

        self.__waitQ = manager.list()

        self.__notifyNewActWait = Condition(self.__sharedLock)

    def gF(self):
        return self.__docCache[0][0] if len(self.__docCache) else None

    def getNumberOfDocumentsInCache(self):
        """
        Získání počtu dokumentu v cache.
        
        :return: Počet dokumentů v cache.
        """

        return len(self.__docCache)

    def getNumberOfWaitingForWrite(self):
        """
        Získání počtu procesů, které čekají na zápis.
        
        :return: Počet procesů čekajících na zápis.
        """

        return self.__numOfWaitingForWrite.value

    def getLastWritten(self):
        """
        Získání posledně vypsaného dokumentu.
        
        :return: Vrací číslo naposledy vypsaného dokumentu. -1 znamená, že doposud nebyl vypsán dokument.
        """

        return self.__shouldWrite.value - 1

    def waitFor(self, docNumber):
        """
        Uložení čísla dokumentu, který chceme vypsat. Ukládáme takto dokumenty, abychom nemuseli cachovat i prázdné řádky.
        Vkládání se provádí do fronty.
        
        :param docNumber: Číslo dokumentu, na který čekáme pro vypsání.
        """
        self.__sharedLock.acquire()

        insertAt = 0
        for x in self.__waitQ:
            if docNumber > x:
                insertAt += 1
            else:
                break

        self.__waitQ.insert(insertAt, docNumber)

        if len(self.__waitQ) == 1:
            self.__changeActWaitFor(docNumber)

        self.__sharedLock.release()

    def actWaitFor(self):
        """
        Vrátí číslo dokumentu, na který aktuálně čeká.
        
        :return: Číslo dokumentu, na který aktuálně čekáme. Pokud nečekáme na žádný dokument vrací None.
        """

        if len(self.__waitQ) != 0:
            return self.__waitQ[0]

        return None

    def __changeActWaitFor(self, docNumber):
        """
        Změna dokumentu, na který aktuálně čekáme.
        
        Dává vědět případným čekajícím dokumentům, které se mají zapsat bez ukládání do cache,
        že došlo ke změně aktuálního dokumentu, na který čekáme.
        
        :param docNumber: Číslo dokumentu.
        """
        #dame vedet pripadnym cekajicim dokumentum, ktere se maji zapsat
        #bez ukladani do cache
        self.__notifyNewActWait.notify_all()

    def __nextWaitFor(self):
        """
        Nastavíme dalšího ve frontě jako aktuálního, na který čekáme.
        Starého z fronty odstraníme.
        
        Dává vědět případným čekajícím dokumentům, které se mají zapsat bez ukládání do cache,
        Že došlo ke změně aktuálního dokumentu, na který čekáme.
        
        Používat pokud vlastním zámek.
        
        """

        if len(self.__waitQ) != 0:

            del self.__waitQ[0]
            #změníme aktuální
            if len(self.__waitQ) > 0:
                self.__changeActWaitFor(self.__waitQ[0])

    def cacheDoc(self, docNumber, docTxt, pEnd=''):
        """
        Uložení dokumentu do cache.
        
        :param docNumber: Číslo aktuálně nového dokumentu (číslo řádku).
        :param docTxt: Nový dokument pro uložení.
        :param pEnd: Jedná se o parametr end pro print. Tedy jak bude zakončen výpis řetězce.
        """

        self.__sharedLock.acquire()
        insertAt = 0
        for x in self.__docCache:
            if docNumber > x[0]:
                insertAt += 1
            else:
                break

        self.__docCache.insert(insertAt, (docNumber, docTxt, pEnd))

        self.__sharedLock.release()

    def cacheWrite(self):
        """
        Vypisuje data z cache. Na stdout.
        Vypíše jen dokumenty, které neporuší pořadí dané číslem dokumentu.
        """

        self.__sharedLock.acquire()

        try:

            actWait = self.actWaitFor()

            if actWait is not None:

                while len(
                        self.__docCache) and actWait == self.__docCache[0][0]:
                    if self.__shouldWrite.value == actWait:

                        print(self.__docCache[0][1],
                              end=self.__docCache[0][2],
                              flush=True)
                        self.__shouldWrite.value += 1

                        del self.__docCache[0]

                        self.__nextWaitFor()
                        actWait = self.actWaitFor()

                    else:
                        #doplníme bílé řádky
                        print("\n" * (actWait - self.__shouldWrite.value),
                              end="",
                              flush=True)
                        self.__shouldWrite.value = actWait

        finally:
            self.__sharedLock.release()

    def writeWithoutCaching(self, docNumber, docTxt, pEnd=''):
        """
        Vypíše dokument ve správném pořadí, ale nebude ukládán do cache.
        Místo toho bude čekat do doby než na něj přijde řada.
        
        Je nutné si uvědomit, že v tomto případě při čekání máme obsazený jeden proces/vlákno,
        nemůžeme mezitím tedy konat užitečnou práci, pouze spíme.
        
        :param docNumber: Číslo dokumentu.
        :param docTxt: Obsah dokumentu.
        :param pEnd: Jedná se o parametr end pro print. Tedy jak bude zakončen výpis řetězce.
        """

        self.__sharedLock.acquire()

        self.__numOfWaitingForWrite.value += 1

        try:
            while True:

                actWait = self.actWaitFor()

                if actWait is not None and actWait == docNumber:
                    if self.__shouldWrite.value < actWait:
                        #doplníme bílé řádky

                        print("\n" * (actWait - self.__shouldWrite.value),
                              end=pEnd,
                              flush=True)
                        self.__shouldWrite.value = actWait

                    print(docTxt, end=pEnd, flush=True)

                    self.__numOfWaitingForWrite.value -= 1
                    self.__shouldWrite.value += 1

                    self.__nextWaitFor()

                    break

                else:
                    #aktuálně nečekáme na předzpracování našeho dokumentu
                    #počkáme tedy
                    self.__notifyNewActWait.wait()

        finally:
            self.__sharedLock.release()