xref: /openbmc/openbmc/poky/bitbake/lib/bb/ui/uievent.py (revision 03514f19)
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