summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/server/process.py
diff options
context:
space:
mode:
Diffstat (limited to 'bitbake/lib/bb/server/process.py')
-rw-r--r--bitbake/lib/bb/server/process.py252
1 files changed, 252 insertions, 0 deletions
diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py
new file mode 100644
index 0000000000..302ee5fc8a
--- /dev/null
+++ b/bitbake/lib/bb/server/process.py
@@ -0,0 +1,252 @@
1#
2# BitBake Process based server.
3#
4# Copyright (C) 2010 Bob Foerster <robert@erafx.com>
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License version 2 as
8# published by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program; if not, write to the Free Software Foundation, Inc.,
17# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19"""
20 This module implements a multiprocessing.Process based server for bitbake.
21"""
22
23import bb
24import bb.event
25import itertools
26import logging
27import multiprocessing
28import os
29import signal
30import sys
31import time
32import select
33from Queue import Empty
34from multiprocessing import Event, Process, util, Queue, Pipe, queues, Manager
35
36from . import BitBakeBaseServer, BitBakeBaseServerConnection, BaseImplServer
37
38logger = logging.getLogger('BitBake')
39
40class ServerCommunicator():
41 def __init__(self, connection, event_handle, server):
42 self.connection = connection
43 self.event_handle = event_handle
44 self.server = server
45
46 def runCommand(self, command):
47 # @todo try/except
48 self.connection.send(command)
49
50 if not self.server.is_alive():
51 raise SystemExit
52
53 while True:
54 # don't let the user ctrl-c while we're waiting for a response
55 try:
56 if self.connection.poll(20):
57 return self.connection.recv()
58 else:
59 bb.fatal("Timeout while attempting to communicate with bitbake server")
60 except KeyboardInterrupt:
61 pass
62
63 def getEventHandle(self):
64 return self.event_handle.value
65
66class EventAdapter():
67 """
68 Adapter to wrap our event queue since the caller (bb.event) expects to
69 call a send() method, but our actual queue only has put()
70 """
71 def __init__(self, queue):
72 self.queue = queue
73
74 def send(self, event):
75 try:
76 self.queue.put(event)
77 except Exception as err:
78 print("EventAdapter puked: %s" % str(err))
79
80
81class ProcessServer(Process, BaseImplServer):
82 profile_filename = "profile.log"
83 profile_processed_filename = "profile.log.processed"
84
85 def __init__(self, command_channel, event_queue, featurelist):
86 BaseImplServer.__init__(self)
87 Process.__init__(self)
88 self.command_channel = command_channel
89 self.event_queue = event_queue
90 self.event = EventAdapter(event_queue)
91 self.featurelist = featurelist
92 self.quit = False
93
94 self.quitin, self.quitout = Pipe()
95 self.event_handle = multiprocessing.Value("i")
96
97 def run(self):
98 for event in bb.event.ui_queue:
99 self.event_queue.put(event)
100 self.event_handle.value = bb.event.register_UIHhandler(self)
101
102 bb.cooker.server_main(self.cooker, self.main)
103
104 def main(self):
105 # Ignore SIGINT within the server, as all SIGINT handling is done by
106 # the UI and communicated to us
107 self.quitin.close()
108 signal.signal(signal.SIGINT, signal.SIG_IGN)
109 while not self.quit:
110 try:
111 if self.command_channel.poll():
112 command = self.command_channel.recv()
113 self.runCommand(command)
114 if self.quitout.poll():
115 self.quitout.recv()
116 self.quit = True
117
118 self.idle_commands(.1, [self.event_queue._reader, self.command_channel, self.quitout])
119 except Exception:
120 logger.exception('Running command %s', command)
121
122 self.event_queue.close()
123 bb.event.unregister_UIHhandler(self.event_handle.value)
124 self.command_channel.close()
125 self.cooker.shutdown(True)
126
127 def idle_commands(self, delay, fds = []):
128 nextsleep = delay
129
130 for function, data in self._idlefuns.items():
131 try:
132 retval = function(self, data, False)
133 if retval is False:
134 del self._idlefuns[function]
135 nextsleep = None
136 elif retval is True:
137 nextsleep = None
138 elif isinstance(retval, float):
139 if (retval < nextsleep):
140 nextsleep = retval
141 elif nextsleep is None:
142 continue
143 else:
144 fds = fds + retval
145 except SystemExit:
146 raise
147 except Exception:
148 logger.exception('Running idle function')
149 del self._idlefuns[function]
150 self.quit = True
151
152 if nextsleep is not None:
153 select.select(fds,[],[],nextsleep)
154
155 def runCommand(self, command):
156 """
157 Run a cooker command on the server
158 """
159 self.command_channel.send(self.cooker.command.runCommand(command))
160
161 def stop(self):
162 self.quitin.send("quit")
163 self.quitin.close()
164
165class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
166 def __init__(self, serverImpl, ui_channel, event_queue):
167 self.procserver = serverImpl
168 self.ui_channel = ui_channel
169 self.event_queue = event_queue
170 self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle, self.procserver)
171 self.events = self.event_queue
172
173 def sigterm_terminate(self):
174 bb.error("UI received SIGTERM")
175 self.terminate()
176
177 def terminate(self):
178 def flushevents():
179 while True:
180 try:
181 event = self.event_queue.get(block=False)
182 except (Empty, IOError):
183 break
184 if isinstance(event, logging.LogRecord):
185 logger.handle(event)
186
187 signal.signal(signal.SIGINT, signal.SIG_IGN)
188 self.procserver.stop()
189
190 while self.procserver.is_alive():
191 flushevents()
192 self.procserver.join(0.1)
193
194 self.ui_channel.close()
195 self.event_queue.close()
196 self.event_queue.setexit()
197
198# Wrap Queue to provide API which isn't server implementation specific
199class ProcessEventQueue(multiprocessing.queues.Queue):
200 def __init__(self, maxsize):
201 multiprocessing.queues.Queue.__init__(self, maxsize)
202 self.exit = False
203
204 def setexit(self):
205 self.exit = True
206
207 def waitEvent(self, timeout):
208 if self.exit:
209 sys.exit(1)
210 try:
211 if not self.server.is_alive():
212 self.setexit()
213 return None
214 return self.get(True, timeout)
215 except Empty:
216 return None
217
218 def getEvent(self):
219 try:
220 if not self.server.is_alive():
221 self.setexit()
222 return None
223 return self.get(False)
224 except Empty:
225 return None
226
227
228class BitBakeServer(BitBakeBaseServer):
229 def initServer(self):
230 # establish communication channels. We use bidirectional pipes for
231 # ui <--> server command/response pairs
232 # and a queue for server -> ui event notifications
233 #
234 self.ui_channel, self.server_channel = Pipe()
235 self.event_queue = ProcessEventQueue(0)
236 self.serverImpl = ProcessServer(self.server_channel, self.event_queue, None)
237 self.event_queue.server = self.serverImpl
238
239 def detach(self):
240 self.serverImpl.start()
241 return
242
243 def establishConnection(self, featureset):
244
245 self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
246
247 _, error = self.connection.connection.runCommand(["setFeatures", featureset])
248 if error:
249 logger.error("Unable to set the cooker to the correct featureset: %s" % error)
250 raise BaseException(error)
251 signal.signal(signal.SIGTERM, lambda i, s: self.connection.sigterm_terminate())
252 return self.connection