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.py223
1 files changed, 223 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..aa072020af
--- /dev/null
+++ b/bitbake/lib/bb/server/process.py
@@ -0,0 +1,223 @@
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, args=(featurelist))
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.keep_running = Event()
91 self.keep_running.set()
92 self.event_handle = multiprocessing.Value("i")
93
94 def run(self):
95 for event in bb.event.ui_queue:
96 self.event_queue.put(event)
97 self.event_handle.value = bb.event.register_UIHhandler(self)
98
99 # process any feature changes based on what UI requested
100 original_featureset = list(self.cooker.featureset)
101 while len(self.featurelist)> 0:
102 self.cooker.featureset.setFeature(self.featurelist.pop())
103 if (original_featureset != list(self.cooker.featureset)):
104 self.cooker.reset()
105
106 bb.cooker.server_main(self.cooker, self.main)
107
108 def main(self):
109 # Ignore SIGINT within the server, as all SIGINT handling is done by
110 # the UI and communicated to us
111 signal.signal(signal.SIGINT, signal.SIG_IGN)
112 while self.keep_running.is_set():
113 try:
114 if self.command_channel.poll():
115 command = self.command_channel.recv()
116 self.runCommand(command)
117
118 self.idle_commands(.1, [self.event_queue._reader, self.command_channel])
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 self.idle_commands(.1)
127
128 def idle_commands(self, delay, fds = []):
129 nextsleep = delay
130
131 for function, data in self._idlefuns.items():
132 try:
133 retval = function(self, data, False)
134 if retval is False:
135 del self._idlefuns[function]
136 elif retval is True:
137 nextsleep = None
138 elif nextsleep is None:
139 continue
140 else:
141 fds = fds + retval
142 except SystemExit:
143 raise
144 except Exception:
145 logger.exception('Running idle function')
146
147 if nextsleep is not None:
148 select.select(fds,[],[],nextsleep)
149
150 def runCommand(self, command):
151 """
152 Run a cooker command on the server
153 """
154 self.command_channel.send(self.cooker.command.runCommand(command))
155
156 def stop(self):
157 self.keep_running.clear()
158
159class BitBakeProcessServerConnection(BitBakeBaseServerConnection):
160 def __init__(self, serverImpl, ui_channel, event_queue):
161 self.procserver = serverImpl
162 self.ui_channel = ui_channel
163 self.event_queue = event_queue
164 self.connection = ServerCommunicator(self.ui_channel, self.procserver.event_handle)
165 self.events = self.event_queue
166
167 def terminate(self):
168 def flushevents():
169 while True:
170 try:
171 event = self.event_queue.get(block=False)
172 except (Empty, IOError):
173 break
174 if isinstance(event, logging.LogRecord):
175 logger.handle(event)
176
177 signal.signal(signal.SIGINT, signal.SIG_IGN)
178 self.procserver.stop()
179
180 while self.procserver.is_alive():
181 flushevents()
182 self.procserver.join(0.1)
183
184 self.ui_channel.close()
185 self.event_queue.close()
186
187# Wrap Queue to provide API which isn't server implementation specific
188class ProcessEventQueue(multiprocessing.queues.Queue):
189 def waitEvent(self, timeout):
190 try:
191 return self.get(True, timeout)
192 except Empty:
193 return None
194
195 def getEvent(self):
196 try:
197 return self.get(False)
198 except Empty:
199 return None
200
201
202class BitBakeServer(BitBakeBaseServer):
203 def initServer(self):
204 # establish communication channels. We use bidirectional pipes for
205 # ui <--> server command/response pairs
206 # and a queue for server -> ui event notifications
207 #
208 self.ui_channel, self.server_channel = Pipe()
209 self.event_queue = ProcessEventQueue(0)
210 manager = Manager()
211 self.featurelist = manager.list()
212 self.serverImpl = ProcessServer(self.server_channel, self.event_queue, self.featurelist)
213
214 def detach(self):
215 self.serverImpl.start()
216 return
217
218 def establishConnection(self, featureset):
219 for f in featureset:
220 self.featurelist.append(f)
221 self.connection = BitBakeProcessServerConnection(self.serverImpl, self.ui_channel, self.event_queue)
222 signal.signal(signal.SIGTERM, lambda i, s: self.connection.terminate())
223 return self.connection