async def _bind_tcp_sockets_with_consistent_port_number(self, make_socket): # Find a random port number that is free on all self.interfaces, # and get a bound TCP socket with that port number on each # interface. The argument `make_socket` is expected to be a coroutine # with the signature `make_socket(interface, port)` that does whatever # library-specific incantation is necessary to return a bound socket or # raise an IOError. tcp_sockets = {} # maps interface to bound socket stashed_ex = None for port in ca.random_ports(100, try_first=self.ca_server_port): try: for interface in self.interfaces: s = await make_socket(interface, port) tcp_sockets[interface] = s except IOError as ex: stashed_ex = ex for s in tcp_sockets.values(): s.close() tcp_sockets.clear() else: break else: raise CaprotoRuntimeError( 'No available ports and/or bind failed') from stashed_ex return port, tcp_sockets
def catvs_ioc(request): from caproto.curio.server import Context pvgroup = CatvsIOC(prefix='') # NOTE: catvs expects server tcp_port==udp_port, so make a weak attempt # here to avoid clashing between servers port = list(ca.random_ports(1))[0] try: # The environment variale only needs to e set for the initializer of # Context. os.environ['EPICS_CA_SERVER_PORT'] = str(port) context = Context(pvgroup.pvdb, ['127.0.0.1']) finally: os.environ['EPICS_CA_SERVER_PORT'] = '5064' thread = threading.Thread(target=server_thread, daemon=True, args=(context, )) thread.start() def stop_server(): context.log.setLevel('INFO') context.stop() request.addfinalizer(stop_server) while getattr(context, 'port', None) is None: logger.info('Waiting on catvs test server...') time.sleep(0.1) tcp_port = context.port udp_port = context.ca_server_port logger.info('catvs test server started up on port %d (udp port %d)', tcp_port, udp_port) time.sleep(0.5) return pvgroup, context, thread
async def run(self, *, log_pv_names=False): 'Start the server' self.log.info('Server starting up...') try: async with trio.open_nursery() as self.nursery: for address in ca.get_beacon_address_list(): sock = ca.bcast_socket(socket) await sock.connect(address) interface, _ = sock.getsockname() self.beacon_socks[address] = (interface, sock) # This reproduces the common with # self._bind_tcp_sockets_with_consistent_port_number because # trio makes socket binding async where asyncio and curio make # it synchronous. # Find a random port number that is free on all interfaces, # and get a bound TCP socket with that port number on each # interface. tcp_sockets = {} # maps interface to bound socket stashed_ex = None for port in ca.random_ports(100): try: for interface in self.interfaces: s = trio.socket.socket() await s.bind((interface, port)) tcp_sockets[interface] = s except IOError as ex: stashed_ex = ex for s in tcp_sockets.values(): s.close() else: self.port = port break else: raise RuntimeError('No available ports and/or bind failed' ) from stashed_ex # (End of reproduced code) for interface, listen_sock in tcp_sockets.items(): self.log.info("Listening on %s:%d", interface, self.port) await self.nursery.start(self.server_accept_loop, listen_sock) await self.nursery.start(self.broadcaster_udp_server_loop) await self.nursery.start(self.broadcaster_queue_loop) await self.nursery.start(self.subscription_queue_loop) await self.nursery.start(self.broadcast_beacon_loop) async_lib = TrioAsyncLayer() for name, method in self.startup_methods.items(): self.log.debug('Calling startup method %r', name) async def startup(task_status): task_status.started() await method(async_lib) await self.nursery.start(startup) self.log.info('Server startup complete.') if log_pv_names: self.log.info('PVs available:\n%s', '\n'.join(self.pvdb)) except trio.Cancelled: self.log.info('Server task cancelled. Will shut down.') finally: self.log.info('Server exiting....')