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.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.setDaemon(True) 69 self.t.run = self.startCallbackHandler 70 self.t.start() 71 72 def getEvent(self): 73 74 self.eventQueueLock.acquire() 75 76 if len(self.eventQueue) == 0: 77 self.eventQueueLock.release() 78 return None 79 80 item = self.eventQueue.pop(0) 81 82 if len(self.eventQueue) == 0: 83 self.eventQueueNotify.clear() 84 85 self.eventQueueLock.release() 86 return item 87 88 def waitEvent(self, delay): 89 self.eventQueueNotify.wait(delay) 90 return self.getEvent() 91 92 def queue_event(self, event): 93 self.eventQueueLock.acquire() 94 self.eventQueue.append(event) 95 self.eventQueueNotify.set() 96 self.eventQueueLock.release() 97 98 def send_event(self, event): 99 self.queue_event(pickle.loads(event)) 100 101 def startCallbackHandler(self): 102 103 self.server.timeout = 1 104 bb.utils.set_process_name("UIEventQueue") 105 while not self.server.quit: 106 try: 107 self.server.handle_request() 108 except Exception as e: 109 import traceback 110 logger.error("BBUIEventQueue.startCallbackHandler: Exception while trying to handle request: %s\n%s" % (e, traceback.format_exc())) 111 112 self.server.server_close() 113 114 def system_quit( self ): 115 """ 116 Shut down the callback thread 117 """ 118 try: 119 self.BBServer.unregisterEventHandler(self.EventHandle) 120 except: 121 pass 122 self.server.quit = True 123 124class UIXMLRPCServer (SimpleXMLRPCServer): 125 126 def __init__( self, interface ): 127 self.quit = False 128 SimpleXMLRPCServer.__init__( self, 129 interface, 130 requestHandler=SimpleXMLRPCRequestHandler, 131 logRequests=False, allow_none=True, use_builtin_types=True) 132 133 def get_request(self): 134 while not self.quit: 135 try: 136 sock, addr = self.socket.accept() 137 sock.settimeout(1) 138 return (sock, addr) 139 except socket.timeout: 140 pass 141 return (None, None) 142 143 def close_request(self, request): 144 if request is None: 145 return 146 SimpleXMLRPCServer.close_request(self, request) 147 148 def process_request(self, request, client_address): 149 if request is None: 150 return 151 SimpleXMLRPCServer.process_request(self, request, client_address) 152 153