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