:: 

    import asyncio, logging
    import traceback
    
    from task_thread import TaskThread, reCreate, reSchedule,\
        delete, MessageSignal, verbose, signals
    
    
    class TCPConnectionThread(TaskThread):
    
        def __init__(self, parent = None, reader = None, writer = None):
            super().__init__(parent = parent)
            self.reader = reader
            self.writer = writer
            self.peername, self.peerport = self.writer.get_extra_info("peername")
    
        def getId(self):
            return str(id(self))
    
        def getInfo(self):
            return "<TCPConnectionThread %s connected to %s>" % (self.getId(), self.peername)
    
    
        def initVars__(self):
            self.tasks.read_socket = None
            self.tasks.write_socket = None
    
    
        @verbose
        async def enter__(self):
            self.logger.info("enter__ : %s", self.getInfo())
            self.tasks.read_socket = await reCreate(self.tasks.read_socket, self.readSocket__)
    
    
        @verbose
        async def exit__(self):
            self.tasks.read_socket = await delete(self.tasks.read_socket)
            self.tasks.write_socket = await delete(self.tasks.write_socket)
            self.logger.debug("exit__: bye!")
    
    
        async def readSocket__(self):
            try:
                try:
                    packet = await self.reader.read()
                except Exception as e:
                    self.logger.warning("readSocket__ : reading failed : '%s'", e)
                    self.logger.info("readSocket__: tcp connection %s terminated", self.getInfo())
                    await self.stop()
                else:
                    if len(packet) > 0:
                        # all good!  keep on reading = reschedule this
                        self.logger.info("readSocket__: got packet %s", packet)
                        self.tasks.read_socket = await reSchedule(self.readSocket__); return
                    else:
                        self.logger.info("readSocket__: client at %s closed connection", self.peername)
                        await self.stop()
    
            except asyncio.CancelledError:
                self.logger.info("readSocket__ : cancelling %s", self.getInfo())
                
            except Exception as e:
                self.logger.warning("readSocket__: failed with '%s'", e)
                await self.stop()
            
            
        async def writeSocket__(self, packet):        
            try:
                self.writer.write(packet)
                # await self.writer.drain() # this would flush
            except Exception as e:
                self.logger.warning("writeSocket__ : writing failed : '%s'", e)
                await self.stop()
        
    
    class MasterTCPServerThread(TaskThread):
    
    
        def __init__(self, parent = None, name = "thread", pause = 10, port = 5002, max_clients = 10):
            super().__init__(parent = parent)
            self.pause = pause
            self.port = port
            self.max_clients = max_clients
            self.name = name
    
    
        def initVars__(self):
            self.server = None
            self.tasks.tcp_server = None
    
    
        def getId(self):
            return str(id(self))
    
    
        def getInfo(self):
            return "<MasterTCPServerThread %s>" % (self.name)
    
    
        @verbose
        async def enter__(self):
            """Everything starts from here.  This cofunction is awaited (i.e. not scheduled as a task)
            
            - Await for something critical
            - Shedule the re-scheduling tasks
            """
            self.logger.info("entry point")
            self.tasks.tcp_server = await reCreate(self.tasks.tcp_server, self.tcpServer__)
            # now the tasks runs independently
    
    
        @verbose
        async def exit__(self):
            self.tasks.tcp_server = await delete(self.tasks.tcp_server)
            self.logger.debug("exit__: bye!")
    
    
        @verbose
        async def childsignalHandler__(self, signal, child):
            """How to handle a certain signal from children?
            """
            self.logger.debug("childsignalHandler__ : got signal %s from child %s", signal, child)
            if isinstance(signal, MessageSignal):
                self.logger.info("Got message %s from child with id %s", signal.getMessage(), child.getId())
            else:
                pass
        
        # *** custom recurrent tasks ***
    
        async def tcpServer__(self):
            try:
                self.server = await asyncio.start_server(self.handler__, "", self.port)
            
            except asyncio.CancelledError:
                self.logger.info("tcpServer__ %i: cancel")
                self.server = None
            
            except Exception as e:
                self.logger.warning("tcpServer__: failed with '%s'", str(e))
                self.logger.warning("tcpServer__: will try again in %s secs", self.pause)
                await asyncio.sleep(self.pause)
                self.tasks.tcp_server = await reSchedule(self.tcpServer__)
                
            else:
                self.logger.debug("tcpServer__ : new server waiting")
    
    
        # *** internal ***
    
        @verbose
        async def handler__(self, reader, writer):
            self.logger.debug("handler__ : new connection for %s", self.getInfo())
            
            if len(self.children)>self.max_clients:
                self.logger.warning("handler__ : max number of connections is %s", self.max_clients)
                return
            
            child_connection = TCPConnectionThread(reader = reader, writer = writer, parent = self)
            await child_connection.run()
            await self.addChild(child_connection)
    
    
    if __name__ == "__main__":
        loglev = logging.DEBUG
    
        logger = logging.getLogger("MasterTCPServerThread")
        logger.setLevel(loglev)
        logger = logging.getLogger("TCPConnectionThread")
        logger.setLevel(loglev)
    
        thread = MasterTCPServerThread(
            parent = None,
            name = "TCPServer"
            )
        loop = asyncio.get_event_loop()
        loop.run_until_complete(thread.run())
