1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer 3*4882a593Smuzhiyun# Copyright (C) 2006 - 2007 Richard Purdie 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0-only 6*4882a593Smuzhiyun# 7*4882a593Smuzhiyun 8*4882a593Smuzhiyun""" 9*4882a593SmuzhiyunUse this class to fork off a thread to recieve event callbacks from the bitbake 10*4882a593Smuzhiyunserver and queue them for the UI to process. This process must be used to avoid 11*4882a593Smuzhiyunclient/server deadlocks. 12*4882a593Smuzhiyun""" 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunimport collections, logging, pickle, socket, threading 15*4882a593Smuzhiyunfrom xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler 16*4882a593Smuzhiyun 17*4882a593Smuzhiyunimport bb 18*4882a593Smuzhiyun 19*4882a593Smuzhiyunlogger = logging.getLogger(__name__) 20*4882a593Smuzhiyun 21*4882a593Smuzhiyunclass BBUIEventQueue: 22*4882a593Smuzhiyun def __init__(self, BBServer, clientinfo=("localhost, 0")): 23*4882a593Smuzhiyun 24*4882a593Smuzhiyun self.eventQueue = [] 25*4882a593Smuzhiyun self.eventQueueLock = threading.Lock() 26*4882a593Smuzhiyun self.eventQueueNotify = threading.Event() 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun self.BBServer = BBServer 29*4882a593Smuzhiyun self.clientinfo = clientinfo 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun server = UIXMLRPCServer(self.clientinfo) 32*4882a593Smuzhiyun self.host, self.port = server.socket.getsockname() 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun server.register_function( self.system_quit, "event.quit" ) 35*4882a593Smuzhiyun server.register_function( self.send_event, "event.sendpickle" ) 36*4882a593Smuzhiyun server.socket.settimeout(1) 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun self.EventHandle = None 39*4882a593Smuzhiyun 40*4882a593Smuzhiyun # the event handler registration may fail here due to cooker being in invalid state 41*4882a593Smuzhiyun # this is a transient situation, and we should retry a couple of times before 42*4882a593Smuzhiyun # giving up 43*4882a593Smuzhiyun 44*4882a593Smuzhiyun for count_tries in range(5): 45*4882a593Smuzhiyun ret = self.BBServer.registerEventHandler(self.host, self.port) 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun if isinstance(ret, collections.abc.Iterable): 48*4882a593Smuzhiyun self.EventHandle, error = ret 49*4882a593Smuzhiyun else: 50*4882a593Smuzhiyun self.EventHandle = ret 51*4882a593Smuzhiyun error = "" 52*4882a593Smuzhiyun 53*4882a593Smuzhiyun if self.EventHandle is not None: 54*4882a593Smuzhiyun break 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun errmsg = "Could not register UI event handler. Error: %s, host %s, "\ 57*4882a593Smuzhiyun "port %d" % (error, self.host, self.port) 58*4882a593Smuzhiyun bb.warn("%s, retry" % errmsg) 59*4882a593Smuzhiyun 60*4882a593Smuzhiyun import time 61*4882a593Smuzhiyun time.sleep(1) 62*4882a593Smuzhiyun else: 63*4882a593Smuzhiyun raise Exception(errmsg) 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun self.server = server 66*4882a593Smuzhiyun 67*4882a593Smuzhiyun self.t = threading.Thread() 68*4882a593Smuzhiyun self.t.setDaemon(True) 69*4882a593Smuzhiyun self.t.run = self.startCallbackHandler 70*4882a593Smuzhiyun self.t.start() 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun def getEvent(self): 73*4882a593Smuzhiyun 74*4882a593Smuzhiyun self.eventQueueLock.acquire() 75*4882a593Smuzhiyun 76*4882a593Smuzhiyun if not self.eventQueue: 77*4882a593Smuzhiyun self.eventQueueLock.release() 78*4882a593Smuzhiyun return None 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun item = self.eventQueue.pop(0) 81*4882a593Smuzhiyun 82*4882a593Smuzhiyun if not self.eventQueue: 83*4882a593Smuzhiyun self.eventQueueNotify.clear() 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun self.eventQueueLock.release() 86*4882a593Smuzhiyun return item 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun def waitEvent(self, delay): 89*4882a593Smuzhiyun self.eventQueueNotify.wait(delay) 90*4882a593Smuzhiyun return self.getEvent() 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun def queue_event(self, event): 93*4882a593Smuzhiyun self.eventQueueLock.acquire() 94*4882a593Smuzhiyun self.eventQueue.append(event) 95*4882a593Smuzhiyun self.eventQueueNotify.set() 96*4882a593Smuzhiyun self.eventQueueLock.release() 97*4882a593Smuzhiyun 98*4882a593Smuzhiyun def send_event(self, event): 99*4882a593Smuzhiyun self.queue_event(pickle.loads(event)) 100*4882a593Smuzhiyun 101*4882a593Smuzhiyun def startCallbackHandler(self): 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun self.server.timeout = 1 104*4882a593Smuzhiyun bb.utils.set_process_name("UIEventQueue") 105*4882a593Smuzhiyun while not self.server.quit: 106*4882a593Smuzhiyun try: 107*4882a593Smuzhiyun self.server.handle_request() 108*4882a593Smuzhiyun except Exception as e: 109*4882a593Smuzhiyun import traceback 110*4882a593Smuzhiyun logger.error("BBUIEventQueue.startCallbackHandler: Exception while trying to handle request: %s\n%s" % (e, traceback.format_exc())) 111*4882a593Smuzhiyun 112*4882a593Smuzhiyun self.server.server_close() 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun def system_quit( self ): 115*4882a593Smuzhiyun """ 116*4882a593Smuzhiyun Shut down the callback thread 117*4882a593Smuzhiyun """ 118*4882a593Smuzhiyun try: 119*4882a593Smuzhiyun self.BBServer.unregisterEventHandler(self.EventHandle) 120*4882a593Smuzhiyun except: 121*4882a593Smuzhiyun pass 122*4882a593Smuzhiyun self.server.quit = True 123*4882a593Smuzhiyun 124*4882a593Smuzhiyunclass UIXMLRPCServer (SimpleXMLRPCServer): 125*4882a593Smuzhiyun 126*4882a593Smuzhiyun def __init__( self, interface ): 127*4882a593Smuzhiyun self.quit = False 128*4882a593Smuzhiyun SimpleXMLRPCServer.__init__( self, 129*4882a593Smuzhiyun interface, 130*4882a593Smuzhiyun requestHandler=SimpleXMLRPCRequestHandler, 131*4882a593Smuzhiyun logRequests=False, allow_none=True, use_builtin_types=True) 132*4882a593Smuzhiyun 133*4882a593Smuzhiyun def get_request(self): 134*4882a593Smuzhiyun while not self.quit: 135*4882a593Smuzhiyun try: 136*4882a593Smuzhiyun sock, addr = self.socket.accept() 137*4882a593Smuzhiyun sock.settimeout(1) 138*4882a593Smuzhiyun return (sock, addr) 139*4882a593Smuzhiyun except socket.timeout: 140*4882a593Smuzhiyun pass 141*4882a593Smuzhiyun return (None, None) 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun def close_request(self, request): 144*4882a593Smuzhiyun if request is None: 145*4882a593Smuzhiyun return 146*4882a593Smuzhiyun SimpleXMLRPCServer.close_request(self, request) 147*4882a593Smuzhiyun 148*4882a593Smuzhiyun def process_request(self, request, client_address): 149*4882a593Smuzhiyun if request is None: 150*4882a593Smuzhiyun return 151*4882a593Smuzhiyun SimpleXMLRPCServer.process_request(self, request, client_address) 152*4882a593Smuzhiyun 153