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