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.py236
1 files changed, 236 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..577c2503ac
--- /dev/null
+++ b/bitbake/lib/bb/server/process.py
@@ -0,0 +1,236 @@
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):
42 self.connection = connection
43 self.event_handle = event_handle
44
45 def runCommand(self, command):
46 # @todo try/except
47 self.connection.send(command)
48
49 while True:
50 # don't let the user ctrl-c while we're waiting for a response
51 try:
52 if self.connection.poll(20):
53 return self.connection.recv()
54 else:
55 bb.fatal("Timeout while attempting to communicate with bitbake server")
56 except KeyboardInterrupt:
57 pass
58
59 def getEventHandle(self):
60 return self.event_handle.value
61
62class EventAdapter():
63 """
64 Adapter to wrap our event queue since the caller (bb.event) expects to
65 call a send() method, but our actual queue only has put()
66 """
67 def __init__(self, queue):
68 self.queue = queue
69
70 def send(self, event):
71 try:
72 self.queue.put(event)
73 except Exception as err:
74 print("EventAdapter puked: %s" % str(err))
75
76
77class ProcessServer(Process, BaseImplServer):
78 profile_filename = "profile.log"
79 profile_processed_filename = "profile.log.processed"
80
81 def __init__(self, command_channel, event_queue, featurelist):
82 BaseImplServer.__init__(self)
83 Process.__init__(self)
84 self.command_channel = command_channel
85 self.event_queue = event_queue
86 self.event = EventAdapter(event_queue)
87 self.featurelist = featurelist
88 self.quit = False
89
90 self.quitin, self.quitout = Pipe()
91 self.event_handle = multiprocessing.Value("i")
92
93 def run(self):
94 for event in bb.event.ui_queue:
95 self.event_queue.put(event)
96 self.event_handle.value = bb.event.register_UIHhandler(self)
97
98 bb.cooker.server_main(self.cooker, self.main)
99
100 def main(self):
101 # Ignore SIGINT within the server, as all SIGINT handling is done by
102 # the UI and communicated to us
103 self.quitin.close()
104 signal.signal(signal.SIGINT, signal.SIG_IGN)
105 while not self.quit:
106 try:
107 if self.command_channel.poll():
108 command = self.command_channel.recv()
109 self.runCommand(command)
110 if self.quitout.poll():
111 self.quitout.recv()
112 self.quit = True
113
114 self.idle_commands(.1, [self.event_queue._reader, self.command_channel, self.quitout])
115 except Exception:
116 logger.exception('Running command %s', command)
117
118 self.event_queue.close()
119 bb.event.unregister_UIHhandler(self.event_handle.value)
120 self.command_channel.close()
121 self.cooker.shutdown(True)
122
123 def idle_commands(self, delay, fds = []):
124 nextsleep = delay
125
126 for function, data in self._idlefuns.items():
127 try:
128 retval = function(self, data, False)
129 if retval is False:
130 del self._idlefuns[function]
131 nextsleep = None
132 elif retval is True:
133 nextsleep = None
134 elif nextsleep is None:
135 continue
136 else:
137 fds = fds + retval
138 except SystemExit:
139 raise
140 except Exception:
141 logger.exception('Running idle function')
142
143 if nextsleep is not None:
144 select.select(fds,[],[],nextsleep)
145
146 def runCommand(self, command):
147 """
148 Run a cooker command on the server
149 """
150 self.command_channel.send(self.cooker.command.runCommand(command))
151
152 def stop(self):
153 self.quitin.send("quit")
154 self.quitin.close()
155
156class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
157 def __init__(self, serverImpl, ui_channel, event_queue):
158 self.procserver = serverImpl
159 self.ui_channel = ui_channel
160 self.event_queue = event_queue
161 self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle)
162 self.events = self.event_queue
163
164 def sigterm_terminate(self):
165 bb.error("UI received SIGTERM")
166 self.terminate()
167
168 def terminate(self):
169 def flushevents():
170 while True:
171 try:
172 event = self.event_queue.get(block=False)
173 except (Empty, IOError):
174 break
175 if isinstance(event, logging.LogRecord):
176 logger.handle(event)
177
178 signal.signal(signal.SIGINT, signal.SIG_IGN)
179 self.procserver.stop()
180
181 while self.procserver.is_alive():
182 flushevents()
183 self.procserver.join(0.1)
184
185 self.ui_channel.close()
186 self.event_queue.close()
187 self.event_queue.setexit()
188
189# Wrap Queue to provide API which isn't server implementation specific
190class ProcessEventQueue(multiprocessing.queues.Queue):
191 def __init__(self, maxsize):
192 multiprocessing.queues.Queue.__init__(self, maxsize)
193 self.exit = False
194
195 def setexit(self):
196 self.exit = True
197
198 def waitEvent(self, timeout):
199 if self.exit:
200 raise KeyboardInterrupt()
201 try:
202 return self.get(True, timeout)
203 except Empty:
204 return None
205
206 def getEvent(self):
207 try:
208 return self.get(False)
209 except Empty:
210 return None
211
212
213class BitBakeServer(BitBakeBaseServer):
214 def initServer(self):
215 # establish communication channels. We use bidirectional pipes for
216 # ui <--> server command/response pairs
217 # and a queue for server -> ui event notifications
218 #
219 self.ui_channel, self.server_channel = Pipe()
220 self.event_queue = ProcessEventQueue(0)
221 self.serverImpl = ProcessServer(self.server_channel, self.event_queue, None)
222
223 def detach(self):
224 self.serverImpl.start()
225 return
226
227 def establishConnection(self, featureset):
228
229 self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
230
231 _, error = self.connection.connection.runCommand(["setFeatures", featureset])
232 if error:
233 logger.error("Unable to set the cooker to the correct featureset: %s" % error)
234 raise BaseException(error)
235 signal.signal(signal.SIGTERM, lambda i, s: self.connection.sigterm_terminate())
236 return self.connection