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
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)
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()
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()
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()
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)
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()
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()
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()
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()
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)
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
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()
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()
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
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)
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
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()
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
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
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)
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)
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()