def setUp(self): self.game = ExampleGame(Builder(10, 10, 7, 6).create()) self.game.addUnit(Unit(1), 1, (0, 0)) self.bot1 = ExampleBot(1) self.bot1.gameState = self.game.copy() self.linker1 = ExampleBotControllerWrapper(self.bot1) self.game.addUnit(Unit(2), 1, (0, 0)) self.bot2 = ExampleBot(2) self.bot2.gameState = self.game.copy() self.linker2 = ExampleBotControllerWrapper(self.bot2) self.game_info_pipe_parent1, self.game_info_pipe_child1 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.game_info_pipe_parent2, self.game_info_pipe_child2 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.move_pipe_parent1, self.move_pipe_child1 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.move_pipe_parent2, self.move_pipe_child2 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.linker1.setMainPipe(self.move_pipe_child1) self.linker1.setGameInfoPipe(self.game_info_pipe_child1) self.linker2.setMainPipe(self.move_pipe_child2) self.linker2.setGameInfoPipe(self.game_info_pipe_child2) self.collaboration_pipe_1, self.collaboration_pipe_2 = Pipe() self.linker1.addCollaborationPipe(2, self.collaboration_pipe_1) self.linker2.addCollaborationPipe(1, self.collaboration_pipe_2)
def __init__(self): self._reader, self._writer = Pipe(duplex=False) self._rlock = Lock() self._poll = self._reader.poll if sys.platform == 'win32': self._wlock = None else: self._wlock = Lock() self._make_methods()
def __init__(self, maxsize=0): if maxsize <= 0: maxsize = _multiprocessing.SemLock.SEM_VALUE_MAX self._maxsize = maxsize self._reader, self._writer = Pipe(duplex=False) self._rlock = Lock() self._opid = os.getpid() if sys.platform == 'win32': self._wlock = None else: self._wlock = Lock() self._sem = BoundedSemaphore(maxsize) # For use by concurrent.futures self._ignore_epipe = False self._after_fork() if sys.platform != 'win32': register_after_fork(self, Queue._after_fork)
def _addControllerWrapper(self, wrapper: ControllerWrapper, unit: Unit) -> None: """ Adds the linker to the loop, creating the pipe connections Args: wrapper: The linker to add unit: The unit, linked by this linker """ self.wrappers[wrapper] = unit parent_conn, child_conn = Pipe() parent_info_conn, child_info_conn = Pipe() self.wrappersConnection[wrapper] = parent_conn self.wrappersInfoConnection[wrapper] = parent_info_conn self._eventsToSend[wrapper] = [] wrapper.setMainPipe(child_conn) wrapper.setGameInfoPipe(child_info_conn) if isinstance(wrapper, BotControllerWrapper): self._addCollaborationPipes(wrapper)
def _addCollaborationPipes(self, linker: BotControllerWrapper) -> None: """ Adds the collaboration pipes between the given linker and its teammate's Args: linker: The linker to connect with its teammate """ for teammate in self.game.teams[self.game.unitsTeam[ self.wrappers[linker]]]: if teammate is not self.wrappers[linker]: teammate_linker = None # type: BotControllerWrapper for other_linker in self.wrappers: if self.wrappers[other_linker] is teammate: teammate_linker = other_linker break pipe1, pipe2 = Pipe() linker.addCollaborationPipe( teammate_linker.controller.playerNumber, pipe1) teammate_linker.addCollaborationPipe( linker.controller.playerNumber, pipe2)
def Pipe(duplex=True): ''' Returns two connection object connected by a pipe ''' from multiprocess.connection import Pipe return Pipe(duplex)
class TestBotControllerWrapper(unittest.TestCase): def setUp(self): self.game = ExampleGame(Builder(10, 10, 7, 6).create()) self.game.addUnit(Unit(1), 1, (0, 0)) self.bot1 = ExampleBot(1) self.bot1.gameState = self.game.copy() self.linker1 = ExampleBotControllerWrapper(self.bot1) self.game.addUnit(Unit(2), 1, (0, 0)) self.bot2 = ExampleBot(2) self.bot2.gameState = self.game.copy() self.linker2 = ExampleBotControllerWrapper(self.bot2) self.game_info_pipe_parent1, self.game_info_pipe_child1 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.game_info_pipe_parent2, self.game_info_pipe_child2 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.move_pipe_parent1, self.move_pipe_child1 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.move_pipe_parent2, self.move_pipe_child2 = Pipe( ) # type: Tuple[PipeConnection, PipeConnection] self.linker1.setMainPipe(self.move_pipe_child1) self.linker1.setGameInfoPipe(self.game_info_pipe_child1) self.linker2.setMainPipe(self.move_pipe_child2) self.linker2.setGameInfoPipe(self.game_info_pipe_child2) self.collaboration_pipe_1, self.collaboration_pipe_2 = Pipe() self.linker1.addCollaborationPipe(2, self.collaboration_pipe_1) self.linker2.addCollaborationPipe(1, self.collaboration_pipe_2) def test_invalid_type_sent(self): """ Tests that the linker raises an error when a message that is not a "BotEvent" is sent """ self.move_pipe_parent1.send("") self.assertRaises(TypeError, self.linker1._routine, self.game_info_pipe_child1, self.move_pipe_child1) def test_send_move(self): """ Tests that moves are sent correctly, that they affect the GameState and that the AI responds well """ move1_event = BotEvent(1, MOVE1) move2_event = BotEvent(1, MOVE2) self.move_pipe_parent1.send(move1_event) self.move_pipe_parent1.send(move1_event) self.move_pipe_parent1.send(move1_event) self.move_pipe_parent1.send(move2_event) self.linker1._routine() self.assertFalse(self.move_pipe_parent1.poll()) self.linker1._routine() self.assertTrue(self.move_pipe_parent1.poll()) self.assertEqual(self.move_pipe_parent1.recv(), "MOVE1-3/MOVE2-1") def test_send_message_to_teammate(self): """ Tests that messages are sent well between two teammates """ self.bot1.sendMessageToTeammate(2, MSG1) self.linker1._routine() # Will send the message self.linker2._routine() # Will receive the message self.assertTrue(self.move_pipe_parent2.poll()) self.assertEqual(self.move_pipe_parent2.recv(), "MOVE1") def test_send_end_event(self): """ Checks if the linker's logical loop ends correctly when it receives the end event """ self.game_info_pipe_parent1.send(SpecialEvent(SpecialEvent.END)) self.linker1.run() # Should run indefinitely if no flag was sent self.assertTrue(True) def test_unit_dead(self): """ Checks if the linker blocks the incoming message of a dead unit, and starts to send again when resurrected """ self.game_info_pipe_parent1.send(SpecialEvent( SpecialEvent.UNIT_KILLED)) self.move_pipe_parent1.send(BotEvent(1, MOVE1)) self.linker1._routine() self.assertFalse(self.move_pipe_parent1.poll()) self.linker1._routine() # Message blocked self.assertFalse(self.move_pipe_parent1.poll()) self.game_info_pipe_parent1.send( SpecialEvent(SpecialEvent.RESURRECT_UNIT)) self.move_pipe_parent1.send(BotEvent(1, MOVE2)) self.linker1._routine() # Message received self.linker1._routine() # Message sent self.assertTrue(self.move_pipe_parent1.poll()) # The message "MOVE1" was correctly received while the unit was dead => the game state is updated # while the unit is dead self.assertEqual(self.move_pipe_parent1.recv(), "MOVE1-1/MOVE2-1")
class Queue(object): def __init__(self, maxsize=0): if maxsize <= 0: maxsize = _multiprocessing.SemLock.SEM_VALUE_MAX self._maxsize = maxsize self._reader, self._writer = Pipe(duplex=False) self._rlock = Lock() self._opid = os.getpid() if sys.platform == 'win32': self._wlock = None else: self._wlock = Lock() self._sem = BoundedSemaphore(maxsize) # For use by concurrent.futures self._ignore_epipe = False self._after_fork() if sys.platform != 'win32': register_after_fork(self, Queue._after_fork) def __getstate__(self): assert_spawning(self) return (self._ignore_epipe, self._maxsize, self._reader, self._writer, self._rlock, self._wlock, self._sem, self._opid) def __setstate__(self, state): (self._ignore_epipe, self._maxsize, self._reader, self._writer, self._rlock, self._wlock, self._sem, self._opid) = state self._after_fork() def _after_fork(self): debug('Queue._after_fork()') self._notempty = threading.Condition(threading.Lock()) self._buffer = collections.deque() self._thread = None self._jointhread = None self._joincancelled = False self._closed = False self._close = None self._send = self._writer.send self._recv = self._reader.recv self._poll = self._reader.poll def put(self, obj, block=True, timeout=None): assert not self._closed if not self._sem.acquire(block, timeout): raise Full self._notempty.acquire() try: if self._thread is None: self._start_thread() self._buffer.append(obj) self._notempty.notify() finally: self._notempty.release() def get(self, block=True, timeout=None): if block and timeout is None: self._rlock.acquire() try: res = self._recv() self._sem.release() return res finally: self._rlock.release() else: if block: deadline = time.time() + timeout if not self._rlock.acquire(block, timeout): raise Empty try: if block: timeout = deadline - time.time() if timeout < 0 or not self._poll(timeout): raise Empty elif not self._poll(): raise Empty res = self._recv() self._sem.release() return res finally: self._rlock.release() def qsize(self): # Raises NotImplementedError on Mac OSX because of broken sem_getvalue() return self._maxsize - self._sem._semlock._get_value() def empty(self): return not self._poll() def full(self): return self._sem._semlock._is_zero() def get_nowait(self): return self.get(False) def put_nowait(self, obj): return self.put(obj, False) def close(self): self._closed = True self._reader.close() if self._close: self._close() def join_thread(self): debug('Queue.join_thread()') assert self._closed if self._jointhread: self._jointhread() def cancel_join_thread(self): debug('Queue.cancel_join_thread()') self._joincancelled = True try: self._jointhread.cancel() except AttributeError: pass def _start_thread(self): debug('Queue._start_thread()') # Start thread which transfers data from buffer to pipe self._buffer.clear() self._thread = threading.Thread( target=Queue._feed, args=(self._buffer, self._notempty, self._send, self._wlock, self._writer.close, self._ignore_epipe), name='QueueFeederThread' ) self._thread.daemon = True debug('doing self._thread.start()') self._thread.start() debug('... done self._thread.start()') # On process exit we will wait for data to be flushed to pipe. # # However, if this process created the queue then all # processes which use the queue will be descendants of this # process. Therefore waiting for the queue to be flushed # is pointless once all the child processes have been joined. created_by_this_process = (self._opid == os.getpid()) if not self._joincancelled and not created_by_this_process: self._jointhread = Finalize( self._thread, Queue._finalize_join, [weakref.ref(self._thread)], exitpriority=-5 ) # Send sentinel to the thread queue object when garbage collected self._close = Finalize( self, Queue._finalize_close, [self._buffer, self._notempty], exitpriority=10 ) @staticmethod def _finalize_join(twr): debug('joining queue thread') thread = twr() if thread is not None: thread.join() debug('... queue thread joined') else: debug('... queue thread already dead') @staticmethod def _finalize_close(buffer, notempty): debug('telling queue thread to quit') notempty.acquire() try: buffer.append(_sentinel) notempty.notify() finally: notempty.release() @staticmethod def _feed(buffer, notempty, send, writelock, close, ignore_epipe): debug('starting thread to feed data to pipe') from .util import is_exiting nacquire = notempty.acquire nrelease = notempty.release nwait = notempty.wait bpopleft = buffer.popleft sentinel = _sentinel if sys.platform != 'win32': wacquire = writelock.acquire wrelease = writelock.release else: wacquire = None try: while 1: nacquire() try: if not buffer: nwait() finally: nrelease() try: while 1: obj = bpopleft() if obj is sentinel: debug('feeder thread got sentinel -- exiting') close() return if wacquire is None: send(obj) else: wacquire() try: send(obj) finally: wrelease() except IndexError: pass except Exception as e: if ignore_epipe and getattr(e, 'errno', 0) == errno.EPIPE: return # Since this runs in a daemon thread the resources it uses # may be become unusable while the process is cleaning up. # We ignore errors which happen after the process has # started to cleanup. try: if is_exiting(): info('error in queue thread: %s', e) else: import traceback traceback.print_exc() except Exception: pass
class Queue(object): def __init__(self, maxsize=0): if maxsize <= 0: maxsize = _multiprocessing.SemLock.SEM_VALUE_MAX self._maxsize = maxsize self._reader, self._writer = Pipe(duplex=False) self._rlock = Lock() self._opid = os.getpid() if sys.platform == 'win32': self._wlock = None else: self._wlock = Lock() self._sem = BoundedSemaphore(maxsize) # For use by concurrent.futures self._ignore_epipe = False self._after_fork() if sys.platform != 'win32': register_after_fork(self, Queue._after_fork) def __getstate__(self): assert_spawning(self) return (self._ignore_epipe, self._maxsize, self._reader, self._writer, self._rlock, self._wlock, self._sem, self._opid) def __setstate__(self, state): (self._ignore_epipe, self._maxsize, self._reader, self._writer, self._rlock, self._wlock, self._sem, self._opid) = state self._after_fork() def _after_fork(self): debug('Queue._after_fork()') self._notempty = threading.Condition(threading.Lock()) self._buffer = collections.deque() self._thread = None self._jointhread = None self._joincancelled = False self._closed = False self._close = None self._send = self._writer.send self._recv = self._reader.recv self._poll = self._reader.poll def put(self, obj, block=True, timeout=None): assert not self._closed if not self._sem.acquire(block, timeout): raise Full self._notempty.acquire() try: if self._thread is None: self._start_thread() self._buffer.append(obj) self._notempty.notify() finally: self._notempty.release() def get(self, block=True, timeout=None): if block and timeout is None: self._rlock.acquire() try: res = self._recv() self._sem.release() return res finally: self._rlock.release() else: if block: deadline = time.time() + timeout if not self._rlock.acquire(block, timeout): raise Empty try: if block: timeout = deadline - time.time() if timeout < 0 or not self._poll(timeout): raise Empty elif not self._poll(): raise Empty res = self._recv() self._sem.release() return res finally: self._rlock.release() def qsize(self): # Raises NotImplementedError on Mac OSX because of broken sem_getvalue() return self._maxsize - self._sem._semlock._get_value() def empty(self): return not self._poll() def full(self): return self._sem._semlock._is_zero() def get_nowait(self): return self.get(False) def put_nowait(self, obj): return self.put(obj, False) def close(self): self._closed = True self._reader.close() if self._close: self._close() def join_thread(self): debug('Queue.join_thread()') assert self._closed if self._jointhread: self._jointhread() def cancel_join_thread(self): debug('Queue.cancel_join_thread()') self._joincancelled = True try: self._jointhread.cancel() except AttributeError: pass def _start_thread(self): debug('Queue._start_thread()') # Start thread which transfers data from buffer to pipe self._buffer.clear() self._thread = threading.Thread( target=Queue._feed, args=(self._buffer, self._notempty, self._send, self._wlock, self._writer.close, self._ignore_epipe), name='QueueFeederThread') self._thread.daemon = True debug('doing self._thread.start()') self._thread.start() debug('... done self._thread.start()') # On process exit we will wait for data to be flushed to pipe. # # However, if this process created the queue then all # processes which use the queue will be descendants of this # process. Therefore waiting for the queue to be flushed # is pointless once all the child processes have been joined. created_by_this_process = (self._opid == os.getpid()) if not self._joincancelled and not created_by_this_process: self._jointhread = Finalize(self._thread, Queue._finalize_join, [weakref.ref(self._thread)], exitpriority=-5) # Send sentinel to the thread queue object when garbage collected self._close = Finalize(self, Queue._finalize_close, [self._buffer, self._notempty], exitpriority=10) @staticmethod def _finalize_join(twr): debug('joining queue thread') thread = twr() if thread is not None: thread.join() debug('... queue thread joined') else: debug('... queue thread already dead') @staticmethod def _finalize_close(buffer, notempty): debug('telling queue thread to quit') notempty.acquire() try: buffer.append(_sentinel) notempty.notify() finally: notempty.release() @staticmethod def _feed(buffer, notempty, send, writelock, close, ignore_epipe): debug('starting thread to feed data to pipe') from .util import is_exiting nacquire = notempty.acquire nrelease = notempty.release nwait = notempty.wait bpopleft = buffer.popleft sentinel = _sentinel if sys.platform != 'win32': wacquire = writelock.acquire wrelease = writelock.release else: wacquire = None try: while 1: nacquire() try: if not buffer: nwait() finally: nrelease() try: while 1: obj = bpopleft() if obj is sentinel: debug('feeder thread got sentinel -- exiting') close() return if wacquire is None: send(obj) else: wacquire() try: send(obj) finally: wrelease() except IndexError: pass except Exception as e: if ignore_epipe and getattr(e, 'errno', 0) == errno.EPIPE: return # Since this runs in a daemon thread the resources it uses # may be become unusable while the process is cleaning up. # We ignore errors which happen after the process has # started to cleanup. try: if is_exiting(): info('error in queue thread: %s', e) else: import traceback traceback.print_exc() except Exception: pass