| # |
| # Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer |
| # Copyright (C) 2006 - 2007 Richard Purdie |
| # |
| # SPDX-License-Identifier: GPL-2.0-only |
| # |
| |
| """ |
| Use this class to fork off a thread to recieve event callbacks from the bitbake |
| server and queue them for the UI to process. This process must be used to avoid |
| client/server deadlocks. |
| """ |
| |
| import collections, logging, pickle, socket, threading |
| from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler |
| |
| import bb |
| |
| logger = logging.getLogger(__name__) |
| |
| class BBUIEventQueue: |
| def __init__(self, BBServer, clientinfo=("localhost, 0")): |
| |
| self.eventQueue = [] |
| self.eventQueueLock = threading.Lock() |
| self.eventQueueNotify = threading.Event() |
| |
| self.BBServer = BBServer |
| self.clientinfo = clientinfo |
| |
| server = UIXMLRPCServer(self.clientinfo) |
| self.host, self.port = server.socket.getsockname() |
| |
| server.register_function( self.system_quit, "event.quit" ) |
| server.register_function( self.send_event, "event.sendpickle" ) |
| server.socket.settimeout(1) |
| |
| self.EventHandle = None |
| |
| # the event handler registration may fail here due to cooker being in invalid state |
| # this is a transient situation, and we should retry a couple of times before |
| # giving up |
| |
| for count_tries in range(5): |
| ret = self.BBServer.registerEventHandler(self.host, self.port) |
| |
| if isinstance(ret, collections.abc.Iterable): |
| self.EventHandle, error = ret |
| else: |
| self.EventHandle = ret |
| error = "" |
| |
| if self.EventHandle is not None: |
| break |
| |
| errmsg = "Could not register UI event handler. Error: %s, host %s, "\ |
| "port %d" % (error, self.host, self.port) |
| bb.warn("%s, retry" % errmsg) |
| |
| import time |
| time.sleep(1) |
| else: |
| raise Exception(errmsg) |
| |
| self.server = server |
| |
| self.t = threading.Thread() |
| self.t.daemon = True |
| self.t.run = self.startCallbackHandler |
| self.t.start() |
| |
| def getEvent(self): |
| with bb.utils.lock_timeout(self.eventQueueLock): |
| if not self.eventQueue: |
| return None |
| item = self.eventQueue.pop(0) |
| if not self.eventQueue: |
| self.eventQueueNotify.clear() |
| return item |
| |
| def waitEvent(self, delay): |
| self.eventQueueNotify.wait(delay) |
| return self.getEvent() |
| |
| def queue_event(self, event): |
| with bb.utils.lock_timeout(self.eventQueueLock): |
| self.eventQueue.append(event) |
| self.eventQueueNotify.set() |
| |
| def send_event(self, event): |
| self.queue_event(pickle.loads(event)) |
| |
| def startCallbackHandler(self): |
| |
| self.server.timeout = 1 |
| bb.utils.set_process_name("UIEventQueue") |
| while not self.server.quit: |
| try: |
| self.server.handle_request() |
| except Exception as e: |
| import traceback |
| logger.error("BBUIEventQueue.startCallbackHandler: Exception while trying to handle request: %s\n%s" % (e, traceback.format_exc())) |
| |
| self.server.server_close() |
| |
| def system_quit( self ): |
| """ |
| Shut down the callback thread |
| """ |
| try: |
| self.BBServer.unregisterEventHandler(self.EventHandle) |
| except: |
| pass |
| self.server.quit = True |
| |
| class UIXMLRPCServer (SimpleXMLRPCServer): |
| |
| def __init__( self, interface ): |
| self.quit = False |
| SimpleXMLRPCServer.__init__( self, |
| interface, |
| requestHandler=SimpleXMLRPCRequestHandler, |
| logRequests=False, allow_none=True, use_builtin_types=True) |
| |
| def get_request(self): |
| while not self.quit: |
| try: |
| sock, addr = self.socket.accept() |
| sock.settimeout(1) |
| return (sock, addr) |
| except socket.timeout: |
| pass |
| return (None, None) |
| |
| def close_request(self, request): |
| if request is None: |
| return |
| SimpleXMLRPCServer.close_request(self, request) |
| |
| def process_request(self, request, client_address): |
| if request is None: |
| return |
| SimpleXMLRPCServer.process_request(self, request, client_address) |
| |